diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index 4b43ed71b..c5b0e929c 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -14,7 +14,6 @@ use Icinga\Exception\AuthenticationException; use Icinga\Exception\NotReadableError; use Icinga\Exception\ConfigurationError; use Icinga\User; -use Icinga\Web\Session; use Icinga\Web\Url; /** @@ -36,7 +35,6 @@ class AuthenticationController extends ActionController { $auth = $this->Auth(); $this->view->form = $form = new LoginForm(); - $form->setRequest($this->_request); $this->view->title = $this->translate('Icingaweb Login'); try { @@ -55,27 +53,17 @@ class AuthenticationController extends ActionController $config = Config::app('authentication'); } catch (NotReadableError $e) { throw new ConfigurationError( - $this->translate( - 'Could not read your authentiction.ini, no authentication methods are available.' - ) + $this->translate('Could not read your authentiction.ini, no authentication methods are available.'), + 0, + $e ); } $chain = new AuthChain($config); - if ($this->getRequest()->isGet()) { - $user = new User(''); - foreach ($chain as $backend) { - if ($backend instanceof AutoLoginBackend) { - $authenticated = $backend->authenticate($user); - if ($authenticated === true) { - $auth->setAuthenticated($user); - $this->rerenderLayout()->redirectNow($redirectUrl); - } - } - } - } elseif ($form->isSubmittedAndValid()) { - $user = new User($form->getValue('username')); - $password = $form->getValue('password'); + $request = $this->getRequest(); + if ($request->isPost() && $this->view->form->isValid($request->getPost())) { + $user = new User($this->view->form->getValue('username')); + $password = $this->view->form->getValue('password'); $backendsTried = 0; $backendsWithError = 0; @@ -121,14 +109,27 @@ class AuthenticationController extends ActionController ); } if ($backendsWithError) { - $form->addNote( + $this->view->form->getElement('username')->addError( $this->translate( - 'Please note that not all authentication methods where available.' + 'Please note that not all authentication methods were available.' . ' Check the system log or Icinga Web 2 log for more information.' ) ); } - $form->getElement('password')->addError($this->translate('Incorrect username or password')); + $this->view->form->getElement('password')->addError($this->translate('Incorrect username or password')); + } elseif ($request->isGet()) { + $user = new User(''); + foreach ($chain as $backend) { + if ($backend instanceof AutoLoginBackend) { + $authenticated = $backend->authenticate($user); + if ($authenticated === true) { + $auth->setAuthenticated($user); + $this->rerenderLayout()->redirectNow( + Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard')) + ); + } + } + } } } catch (Exception $e) { $this->view->errorInfo = $e->getMessage(); @@ -141,14 +142,16 @@ class AuthenticationController extends ActionController public function logoutAction() { $auth = $this->Auth(); + if (! $auth->isAuthenticated()) { + $this->redirectToLogin(); + } $isRemoteUser = $auth->getUser()->isRemoteUser(); $auth->removeAuthorization(); - if ($isRemoteUser === true) { $this->_helper->layout->setLayout('login'); $this->_response->setHttpResponseCode(401); } else { - $this->rerenderLayout()->redirectToLogin(); + $this->redirectToLogin(); } } } diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 45be1d3fd..c12860e72 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -2,38 +2,25 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Web\Controller\BaseConfigController; -use Icinga\Web\Widget\Tab; -use Icinga\Web\Widget\AlertMessageBox; +use Icinga\Web\Controller\ActionController; 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\Form\Config\GeneralConfigForm; +use Icinga\Form\Config\AuthenticationBackendReorderForm; +use Icinga\Form\Config\AuthenticationBackendConfigForm; +use Icinga\Form\Config\ResourceConfigForm; +use Icinga\Form\ConfirmRemovalForm; 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 */ -class ConfigController extends BaseConfigController +class ConfigController extends ActionController { - /** - * The resource types that are available. - * - * @var array - */ - private $resourceTypes = array('livestatus', 'ido', 'statusdat', 'ldap'); - public function init() { $this->view->tabs = Widget::create('tabs')->add('index', array( @@ -45,9 +32,6 @@ class ConfigController extends BaseConfigController ))->add('resources', array( 'title' => 'Resources', 'url' => 'config/resource' - ))->add('logging', array( - 'title' => 'Logging', - 'url' => 'config/logging' )); } @@ -61,42 +45,12 @@ class ConfigController extends BaseConfigController */ public function indexAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new GeneralConfigForm(); + $form->setIniConfig(IcingaConfig::app()); + $form->handleRequest(); + + $this->view->form = $form; $this->view->tabs->activate('index'); - $form = new GeneralForm(); - $form->setConfiguration(IcingaConfig::app()); - $form->setRequest($this->_request); - if ($form->isSubmittedAndValid()) { - if (!$this->writeConfigFile($form->getConfig(), 'config')) { - return; - } - Notification::success('New configuration has successfully been stored'); - $form->setConfiguration(IcingaConfig::app(), true); - $this->redirectNow('config/index'); - } - $this->view->form = $form; - } - - /** - * Form for modifying the logging configuration - */ - public function loggingAction() - { - $this->view->messageBox = new AlertMessageBox(true); - $this->view->tabs->activate('logging'); - - $form = new LoggingForm(); - $form->setConfiguration(IcingaConfig::app()); - $form->setRequest($this->_request); - if ($form->isSubmittedAndValid()) { - if (!$this->writeConfigFile($form->getConfig(), 'config')) { - return; - } - Notification::success('New configuration has sucessfully been stored'); - $form->setConfiguration(IcingaConfig::app(), true); - $this->redirectNow('config/logging'); - } - $this->view->form = $form; } /** @@ -142,7 +96,7 @@ class ConfigController extends BaseConfigController try { $manager->enableModule($module); $manager->loadModule($module); - Notification::success('Module "' . $module . '" enabled'); + Notification::success(sprintf($this->translate('Module "%s" enabled'), $module)); $this->rerenderLayout()->reloadCss()->redirectNow('config/modules'); } catch (Exception $e) { $this->view->exceptionMesssage = $e->getMessage(); @@ -161,7 +115,7 @@ class ConfigController extends BaseConfigController $manager = Icinga::app()->getModuleManager(); try { $manager->disableModule($module); - Notification::success('Module "' . $module . '" disabled'); + Notification::success(sprintf($this->translate('Module "%s" disabled'), $module)); $this->rerenderLayout()->reloadCss()->redirectNow('config/modules'); } catch (Exception $e) { $this->view->exceptionMessage = $e->getMessage(); @@ -172,40 +126,17 @@ class ConfigController extends BaseConfigController } /** - * Action for creating a new authentication backend + * Action for listing and reordering authentication backends */ public function authenticationAction() { - $config = IcingaConfig::app('authentication', true); + $form = new AuthenticationBackendReorderForm(); + $form->setIniConfig(IcingaConfig::app('authentication')); + $form->handleRequest(); + + $this->view->form = $form; $this->view->tabs->activate('authentication'); - - $order = array_keys($config->toArray()); - $this->view->messageBox = new AlertMessageBox(true); - - $backends = array(); - foreach ($order as $backend) { - $form = new ReorderForm(); - $form->setName('form_reorder_backend_' . $backend); - $form->setBackendName($backend); - $form->setCurrentOrder($order); - $form->setRequest($this->_request); - - if ($form->isSubmittedAndValid()) { - if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) { - Notification::success('Authentication Order Updated'); - $this->redirectNow('config/authentication'); - } - - return; - } - - $backends[] = (object) array( - 'name' => $backend, - 'reorderForm' => $form - ); - } - - $this->view->backends = $backends; + $this->render('authentication/reorder'); } /** @@ -213,316 +144,151 @@ class ConfigController extends BaseConfigController */ public function createauthenticationbackendAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new AuthenticationBackendConfigForm(); + $form->setIniConfig(IcingaConfig::app('authentication')); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->setRedirectUrl('config/authentication'); + $form->handleRequest(); - if ($this->getRequest()->getParam('type') === 'ldap') { - $form = new LdapBackendForm(); - } else { - $form = new DbBackendForm(); - } - if ($this->getParam('auth_backend')) { - $form->setBackendName($this->getParam('auth_backend')); - } - $form->setRequest($this->getRequest()); - - if ($form->isSubmittedAndValid()) { - $backendCfg = IcingaConfig::app('authentication')->toArray(); - foreach ($backendCfg as $backendName => $settings) { - unset($backendCfg[$backendName]['name']); - } - foreach ($form->getConfig() as $backendName => $settings) { - unset($settings->{'name'}); - if (isset($backendCfg[$backendName])) { - $this->addErrorMessage('Backend name already exists'); - $this->view->form = $form; - $this->render('authentication/create'); - return; - } - $backendCfg[$backendName] = $settings; - } - if ($this->writeAuthenticationFile($backendCfg)) { - // redirect to overview with success message - Notification::success('Backend Modification Written.'); - $this->redirectNow("config/authentication"); - } - return; - } - - $this->view->messageBox->addForm($form); $this->view->form = $form; + $this->view->tabs->activate('authentication'); $this->render('authentication/create'); } - /** - * Form for editing backends - * - * Mostly the same like the createAuthenticationBackendAction, but with additional checks for backend existence - * and form population + * Action for editing authentication backends */ public function editauthenticationbackendAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new AuthenticationBackendConfigForm(); + $form->setIniConfig(IcingaConfig::app('authentication')); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->setRedirectUrl('config/authentication'); + $form->handleRequest(); - $configArray = IcingaConfig::app('authentication', true)->toArray(); - $authBackend = $this->getParam('auth_backend'); - if (!isset($configArray[$authBackend])) { - $this->addErrorMessage('Can\'t edit: Unknown Authentication Backend Provided'); - $this->configurationerrorAction(); - return; - } - if (!array_key_exists('resource', $configArray[$authBackend])) { - $this->addErrorMessage('Configuration error: Backend "' . $authBackend . '" has no Resource'); - $this->configurationerrorAction(); - return; - } - - $type = ResourceFactory::getResourceConfig($configArray[$authBackend]['resource'])->type; - switch ($type) { - case 'ldap': - $form = new LdapBackendForm(); - break; - case 'db': - $form = new DbBackendForm(); - break; - default: - $this->addErrorMessage('Can\'t edit: backend type "' . $type . '" of given resource not supported.'); - $this->configurationerrorAction(); - return; - } - - $form->setBackendName($this->getParam('auth_backend')); - $form->setBackend(IcingaConfig::app('authentication', true)->$authBackend); - $form->setRequest($this->getRequest()); - - if ($form->isSubmittedAndValid()) { - $backendCfg = IcingaConfig::app('authentication')->toArray(); - foreach ($form->getConfig() as $backendName => $settings) { - $backendCfg[$backendName] = $settings; - // Remove the old section if the backend is renamed - if ($backendName != $authBackend) { - unset($backendCfg[$authBackend]); - } - unset($settings['name']); - } - if ($this->writeAuthenticationFile($backendCfg)) { - // redirect to overview with success message - Notification::success('Backend "' . $authBackend . '" created'); - $this->redirectNow("config/authentication"); - } - return; - } - - $this->view->messageBox->addForm($form); - $this->view->name = $authBackend; $this->view->form = $form; + $this->view->tabs->activate('authentication'); $this->render('authentication/modify'); } /** - * Action for removing a backend from the authentication list. - * - * Redirects to the overview after removal is finished + * Action for removing a backend from the authentication list */ public function removeauthenticationbackendAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new ConfirmRemovalForm(array( + 'onSuccess' => function ($request) { + $configForm = new AuthenticationBackendConfigForm(); + $configForm->setIniConfig(IcingaConfig::app('authentication')); + $authBackend = $request->getQuery('auth_backend'); - $configArray = IcingaConfig::app('authentication', true)->toArray(); - $authBackend = $this->getParam('auth_backend'); - if (!isset($configArray[$authBackend])) { - Notification::error('Can\'t perform removal: Unknown Authentication Backend Provided'); - $this->render('authentication/remove'); - return; - } + try { + $configForm->remove($authBackend); + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } - $form = new ConfirmRemovalForm(); - $form->setRequest($this->getRequest()); - $form->setRemoveTarget('auth_backend', $authBackend); - - if ($form->isSubmittedAndValid()) { - unset($configArray[$authBackend]); - if ($this->writeAuthenticationFile($configArray)) { - Notification::success('Authentication Backend "' . $authBackend . '" Removed'); - $this->redirectNow("config/authentication"); + if ($configForm->save()) { + Notification::success(sprintf( + t('Authentication backend "%s" has been successfully removed'), + $authBackend + )); + } else { + return false; + } } - return; - } + )); + $form->setRedirectUrl('config/authentication'); + $form->handleRequest(); $this->view->form = $form; - $this->view->name = $authBackend; + $this->view->tabs->activate('authentication'); $this->render('authentication/remove'); } - public function resourceAction($showOnly = false) + /** + * Display all available resources and a link to create a new one and to remove existing ones + */ + public function resourceAction() { - $this->view->tabs->activate('resources'); - - $this->view->messageBox = new AlertMessageBox(true); $this->view->resources = IcingaConfig::app('resources', true)->toArray(); - $this->render('resource'); + $this->view->tabs->activate('resources'); } + /** + * Display a form to create a new resource + */ public function createresourceAction() { - $this->view->resourceTypes = $this->resourceTypes; - $resources = IcingaConfig::app('resources', true); - $form = new ResourceForm(); - $form->setRequest($this->_request); - if ($form->isSubmittedAndValid()) { - $name = $form->getName(); - if (isset($resources->{$name})) { - $this->addErrorMessage('Resource name "' . $name .'" already in use.'); - } else { - $resources->{$name} = $form->getConfig(); - if ($this->writeConfigFile($resources, 'resources')) { - $this->addSuccessMessage('Resource "' . $name . '" created.'); - $this->redirectNow("config/resource"); - } - } - } + $form = new ResourceConfigForm(); + $form->setIniConfig(IcingaConfig::app('resources')); + $form->setRedirectUrl('config/resource'); + $form->handleRequest(); - $this->view->messageBox = new AlertMessageBox(true); - $this->view->messageBox->addForm($form); $this->view->form = $form; $this->render('resource/create'); } + /** + * Display a form to edit a existing resource + */ public function editresourceAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new ResourceConfigForm(); + $form->setIniConfig(IcingaConfig::app('resources')); + $form->setRedirectUrl('config/resource'); + $form->handleRequest(); - $resources = ResourceFactory::getResourceConfigs(); - $name = $this->getParam('resource'); - if ($resources->get($name) === null) { - $this->addErrorMessage('Can\'t edit: Unknown Resource Provided'); - $this->render('resource/modify'); - return; - } - $form = new ResourceForm(); - if ($this->_request->isPost() === false) { - $form->setOldName($name); - $form->setName($name); - } - $form->setRequest($this->_request); - $form->setResource($resources->get($name)); - if ($form->isSubmittedAndValid()) { - $oldName = $form->getOldName(); - $name = $form->getName(); - if ($oldName !== $name) { - unset($resources->{$oldName}); - } - $resources->{$name} = $form->getConfig(); - if ($this->writeConfigFile($resources, 'resources')) { - $this->addSuccessMessage('Resource "' . $name . '" edited.'); - $this->redirectNow("config/resource"); - } - return; - } - - $this->view->messageBox->addForm($form); $this->view->form = $form; - $this->view->name = $name; $this->render('resource/modify'); } + /** + * Display a confirmation form to remove a resource + */ public function removeresourceAction() { - $this->view->messageBox = new AlertMessageBox(true); + $form = new ConfirmRemovalForm(array( + 'onSuccess' => function ($request) { + $configForm = new ResourceConfigForm(); + $configForm->setIniConfig(IcingaConfig::app('resources')); + $resource = $request->getQuery('resource'); - $resources = ResourceFactory::getResourceConfigs()->toArray(); - $name = $this->getParam('resource'); - if (!isset($resources[$name])) { - $this->addSuccessMessage('Can\'t remove: Unknown resource provided'); - $this->render('resource/remove'); - return; - } + try { + $configForm->remove($resource); + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } - $form = new ConfirmRemovalForm(); - $form->setRequest($this->getRequest()); - $form->setRemoveTarget('resource', $name); + if ($configForm->save()) { + Notification::success(sprintf(t('Resource "%s" has been successfully removed'), $resource)); + } else { + return false; + } + } + )); + $form->setRedirectUrl('config/resource'); + $form->handleRequest(); // Check if selected resource is currently used for authentication - $authConfig = IcingaConfig::app('authentication', true)->toArray(); + $resource = $this->getRequest()->getQuery('resource'); + $authConfig = IcingaConfig::app('authentication')->toArray(); foreach ($authConfig as $backendName => $config) { - if (array_key_exists('resource', $config) && $config['resource'] === $name) { - $this->addErrorMessage( - 'Warning: The resource "' . $name . '" is currently used for user authentication by "' . $backendName . '". ' . - ' Deleting it could eventally make login impossible.' - ); - } - } - - if ($form->isSubmittedAndValid()) { - unset($resources[$name]); - if ($this->writeConfigFile($resources, 'resources')) { - $this->addSuccessMessage('Resource "' . $name . '" removed.'); - $this->redirectNow('config/resource'); + if (array_key_exists('resource', $config) && $config['resource'] === $resource) { + $form->addError(sprintf( + $this->translate( + 'The resource "%s" is currently in use by the authentication backend "%s". ' . + 'Removing the resource can result in noone being able to log in any longer.' + ), + $resource, + $backendName + )); } - return; } - $this->view->name = $name; $this->view->form = $form; $this->render('resource/remove'); } - - /** - * Redirect target only for error-states - * - * When an error is opened in the side-pane, redirecting this request to the index or the overview will look - * weird. This action returns a clear page containing only an AlertMessageBox. - */ - public function configurationerrorAction() - { - $this->view->messageBox = new AlertMessageBox(true); - $this->render('error/error', null, true); - } - - /** - * Write changes to an authentication file - * - * @param array $config The configuration changes - * - * @return bool True when persisting succeeded, otherwise false - * - * @see writeConfigFile() - */ - private function writeAuthenticationFile($config) { - return $this->writeConfigFile($config, 'authentication'); - } - - /** - * Write changes to a configuration file $file, using the supplied writer or PreservingIniWriter if none is set - * - * @param array|Zend_Config $config The configuration to write - * @param string $file The filename to write to (without .ini) - * @param Zend_Config_Writer $writer An optional writer to use for persisting changes - * - * @return bool True when persisting succeeded, otherwise false - */ - private function writeConfigFile($config, $file, $writer = null) - { - if (is_array($config)) { - $config = new Zend_Config($config); - } - if ($writer === null) { - $writer = new PreservingIniWriter( - array( - 'config' => $config, - 'filename' => IcingaConfig::app($file)->getConfigFile() - ) - ); - } - try { - $writer->write(); - return true; - } catch (Exception $exc) { - $this->view->exceptionMessage = $exc->getMessage(); - $this->view->iniConfigurationString = $writer->render(); - $this->view->file = $file; - $this->render('show-configuration'); - return false; - } - } } diff --git a/application/controllers/DashboardController.php b/application/controllers/DashboardController.php index 45be7aced..4942f5cdb 100644 --- a/application/controllers/DashboardController.php +++ b/application/controllers/DashboardController.php @@ -82,25 +82,29 @@ class DashboardController extends ActionController )->activate('addurl'); $form = new AddUrlForm(); - $form->setRequest($this->getRequest()); - $form->setAction(Url::fromRequest()->setParams(array())->getAbsoluteUrl()); - $this->view->form = $form; + $request = $this->getRequest(); + if ($request->isPost()) { + if ($form->isValid($request->getPost()) && $form->isSubmitted()) { + $dashboard = $this->getDashboard(); + $dashboard->setComponentUrl( + $form->getValue('pane'), + $form->getValue('component'), + ltrim($form->getValue('url'), '/') + ); - if ($form->isSubmittedAndValid()) { - $dashboard = $this->getDashboard(); - $dashboard->setComponentUrl( - $form->getValue('pane'), - $form->getValue('component'), - ltrim($form->getValue('url'), '/') - ); - - $configFile = IcingaConfig::app('dashboard/dashboard')->getConfigFile(); - if ($this->writeConfiguration(new Zend_Config($dashboard->toArray()), $configFile)) { - $this->redirectNow(Url::fromPath('dashboard', array('pane' => $form->getValue('pane')))); - } else { - $this->render('show-configuration'); + $configFile = IcingaConfig::app('dashboard/dashboard')->getConfigFile(); + if ($this->writeConfiguration(new Zend_Config($dashboard->toArray()), $configFile)) { + $this->redirectNow(Url::fromPath('dashboard', array('pane' => $form->getValue('pane')))); + } else { + $this->render('showConfiguration'); + return; + } } + } else { + $form->create()->setDefault('url', htmlspecialchars_decode($request->getParam('url', ''))); } + + $this->view->form = $form; } /** @@ -160,9 +164,9 @@ class DashboardController extends ActionController $writer->write(); } catch (Exception $e) { Logger::error(new ConfiguationError("Cannot write dashboard to $target", 0, $e)); - $this->view->iniConfigurationString = $writer->render(); - $this->view->exceptionMessage = $e->getMessage(); - $this->view->file = $target; + $this->view->configString = $writer->render(); + $this->view->errorMessage = $e->getMessage(); + $this->view->filePath = $target; return false; } diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php index cc2ea4e95..bd76b2466 100644 --- a/application/controllers/PreferenceController.php +++ b/application/controllers/PreferenceController.php @@ -3,11 +3,12 @@ // {{{ICINGA_LICENSE_HEADER}}} use Icinga\Web\Controller\BasePreferenceController; -use Icinga\Web\Widget\Tab; -use Icinga\Application\Config as IcingaConfig; use Icinga\Web\Url; -use Icinga\Form\Preference\GeneralForm; -use Icinga\Web\Notification; +use Icinga\Web\Widget\Tab; +use Icinga\Application\Config; +use Icinga\Form\PreferenceForm; +use Icinga\Exception\ConfigurationError; +use Icinga\User\Preferences\PreferencesStore; /** * Application wide preference controller for user preferences @@ -34,27 +35,22 @@ class PreferenceController extends BasePreferenceController } /** - * General settings for date and time + * Show form to adjust user preferences */ public function indexAction() { - $form = new GeneralForm(); - $this->getTabs()->activate('general'); - $form->setConfiguration(IcingaConfig::app()) - ->setRequest($this->getRequest()); - if ($form->isSubmittedAndValid()) { - try { - $this->savePreferences($form->getPreferences()); - Notification::success(t('Preferences updated successfully')); - // Recreate form to show new values - // TODO(el): It must sufficient to call $form->populate(...) - $form = new GeneralForm(); - $form->setConfiguration(IcingaConfig::app()); - $form->setRequest($this->getRequest()); - } catch (Exception $e) { - Notification::error(sprintf(t('Failed to persist preferences. (%s)'), $e->getMessage())); - } + $storeConfig = Config::app()->preferences; + if ($storeConfig === null) { + throw new ConfigurationError(t('You need to configure how to store preferences first.')); } + + $user = $this->getRequest()->getUser(); + $form = new PreferenceForm(); + $form->setPreferences($user->getPreferences()); + $form->setStore(PreferencesStore::create($storeConfig, $user)); + $form->handleRequest(); + $this->view->form = $form; + $this->getTabs()->activate('general'); } } diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index 0eff8006c..0afc56749 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -13,36 +13,45 @@ use Icinga\Web\Url; class LoginForm extends Form { /** - * Interface how the form should be created + * Initialize this login form */ - protected function create() + public function init() { - $url = Url::fromRequest()->without('renderLayout'); - $this->setName('form_login'); - $this->addElement('text', 'username', array( - 'label' => t('Username'), - 'placeholder' => t('Please enter your username...'), - 'required' => true, - )); - $redir = $this->addElement('hidden', 'redirect'); - $redirectUrl = $url->shift('redirect'); - if ($redirectUrl) { - $this->setDefault('redirect', $redirectUrl); - } + $this->setSubmitLabel(t('Login')); + } - $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 ($this->getRequest()->getPost('username') !== null) { - $this->getElement('password')->setAttrib('class', 'autofocus'); - } else { - $this->getElement('username')->setAttrib('class', 'autofocus'); - } - $this->setAction((string) $url); - $this->setSubmitLabel('Login'); + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'username', + array( + 'required' => true, + 'label' => t('Username'), + 'placeholder' => t('Please enter your username...'), + 'class' => false === isset($formData['username']) ? 'autofocus' : '' + ) + ); + $this->addElement( + 'password', + 'password', + array( + 'required' => true, + 'label' => t('Password'), + 'placeholder' => t('...and your password'), + 'class' => isset($formData['username']) ? 'autofocus' : '' + ) + ); + $this->addElement( + 'hidden', + 'redirect', + array( + 'value' => Url::fromRequest()->getParam('redirect') + ) + ); } } diff --git a/application/forms/Config/Authentication/AutologinBackendForm.php b/application/forms/Config/Authentication/AutologinBackendForm.php new file mode 100644 index 000000000..4f4df73e3 --- /dev/null +++ b/application/forms/Config/Authentication/AutologinBackendForm.php @@ -0,0 +1,89 @@ +setName('form_config_authbackend_autologin'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => t('Backend Name'), + 'description' => t('The name of this authentication backend'), + 'validators' => array( + array( + 'Regex', + false, + array( + 'pattern' => '/^[^\\[\\]:]+$/', + 'messages' => array( + 'regexNotMatch' => 'The backend name cannot contain \'[\', \']\' or \':\'.' + ) + ) + ) + ) + ) + ); + $this->addElement( + 'text', + 'strip_username_regexp', + array( + 'required' => true, + 'label' => t('Backend Domain Pattern'), + 'description' => t('The domain pattern of this authentication backend'), + 'value' => '/\@[^$]+$/', + 'validators' => array( + new Zend_Validate_Callback(function ($value) { + return @preg_match($value, '') !== false; + }) + ) + ) + ); + $this->addElement( + 'hidden', + 'backend', + array( + 'required' => true, + 'value' => 'autologin' + ) + ); + + return $this; + } + + /** + * Validate the configuration by creating a backend and requesting the user count + * + * Returns always true as autologin backends are just "passive" backends. (The webserver authenticates users.) + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not + */ + public function isValidAuthenticationBackend(Form $form) + { + return true; + } +} diff --git a/application/forms/Config/Authentication/BaseBackendForm.php b/application/forms/Config/Authentication/BaseBackendForm.php deleted file mode 100644 index f670b25c1..000000000 --- a/application/forms/Config/Authentication/BaseBackendForm.php +++ /dev/null @@ -1,164 +0,0 @@ -backendName = $name; - } - - /** - * Return the backend name of this form - * - * @return string - */ - public function getBackendName() - { - return $this->backendName; - } - - /** - * Return the backend configuration or a empty Zend_Config object if none is given - * - * @return Zend_Config - */ - public function getBackend() - { - return ($this->backend !== null) ? $this->backend : new Zend_Config(array()); - } - - /** - * Set the backend configuration for initial population - * - * @param Zend_Config $backend The backend to display in this form - */ - public function setBackend(Zend_Config $backend) - { - $this->backend = $backend; - } - - /** - * Set an alternative array of resources that should be used instead of the DBFactory resource set - * - * @param array $resources The resources to use for populating the db selection field - */ - public function setResources(array $resources) - { - $this->resources = $resources; - } - - /** - * Return content of the resources.ini or previously set resources - * - * @return array - */ - public function getResources() - { - if ($this->resources === null) { - return ResourceFactory::getResourceConfigs()->toArray(); - } else { - return $this->resources; - } - } - - /** - * Add checkbox at the beginning of the form which allows to skip logic connection validation - */ - protected function addForceCreationCheckbox() - { - $checkbox = new Zend_Form_Element_Checkbox( - array( - 'name' => 'backend_force_creation', - 'label' => t('Force Changes'), - 'helptext' => t('Check this box to enforce changes without connectivity validation'), - 'order' => 0 - ) - ); - $checkbox->addDecorator(new HelpText()); - $this->addElement($checkbox); - } - - /** - * Validate this form with the Zend validation mechanism and perform a logic validation of the connection. - * - * If logic validation fails, the 'backend_force_creation' checkbox is prepended to the form to allow users to - * skip the logic connection validation. - * - * @param array $data The form input to validate - * - * @return bool Whether validation succeeded or not - */ - public function isValid($data) - { - if (!parent::isValid($data)) { - return false; - } - if (isset($data['backend_force_creation']) && $data['backend_force_creation']) { - return true; - } - if (!$this->isValidAuthenticationBackend()) { - $this->addForceCreationCheckbox(); - return false; - } - return true; - } - - /** - * Return an array containing all sections defined by this form as the key and all settings - * as an key-value sub-array - * - * @return array - */ - abstract public function getConfig(); - - /** - * Validate the configuration state of this backend with the concrete authentication backend. - * - * An implementation should not throw any exception, but use the add/setErrorMessages method of - * Zend_Form. If the 'backend_force_creation' checkbox is set, this method won't be called. - * - * @return bool Whether validation succeeded or not - */ - abstract public function isValidAuthenticationBackend(); -} diff --git a/application/forms/Config/Authentication/DbBackendForm.php b/application/forms/Config/Authentication/DbBackendForm.php index 7184debdb..23335f8fa 100644 --- a/application/forms/Config/Authentication/DbBackendForm.php +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -5,123 +5,116 @@ namespace Icinga\Form\Config\Authentication; use Exception; -use Zend_Config; +use Icinga\Web\Form; +use Icinga\Web\Request; use Icinga\Data\ResourceFactory; -use Icinga\Authentication\DbConnection; use Icinga\Authentication\Backend\DbUserBackend; /** * Form class for adding/modifying database authentication backends */ -class DbBackendForm extends BaseBackendForm +class DbBackendForm extends Form { /** - * Return content of the resources.ini or previously set resources + * The database resource names the user can choose from * - * @return array + * @var array */ - public function getResources() + protected $resources; + + /** + * Initialize this form + */ + public function init() { - if ($this->resources === null) { - $res = ResourceFactory::getResourceConfigs('db')->toArray(); + $this->setName('form_config_authbackend_db'); + } - foreach (array_keys($res) as $key) { - $res[$key] = $key; - } + /** + * Set the resource names the user can choose from + * + * @param array $resources The resources to choose from + * + * @return self + */ + public function setResources(array $resources) + { + $this->resources = $resources; + return $this; + } - return $res; - } else { - return $this->resources; + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => t('Backend Name'), + 'description' => t('The name of this authentication provider'), + ) + ); + $this->addElement( + 'select', + 'resource', + array( + 'required' => true, + 'label' => t('Database Connection'), + 'description' => t('The database connection to use for authenticating with this provider'), + 'multiOptions' => false === empty($this->resources) + ? array_combine($this->resources, $this->resources) + : array() + ) + ); + $this->addElement( + 'hidden', + 'backend', + array( + 'required' => true, + 'value' => 'db' + ) + ); + + return $this; + } + + /** + * Validate that the selected resource is a valid database authentication backend + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (false === $this->isValidAuthenticationBackend($this)) { + return false; } } /** - * Create this form and add all required elements + * Validate the configuration by creating a backend and requesting the user count * - * @see Form::create() + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not */ - public function create() + public function isValidAuthenticationBackend(Form $form) { - $this->setName('form_modify_backend'); - $name = $this->filterName($this->getBackendName()); - $this->addElement( - 'text', - 'backend_' . $name . '_name', - array( - 'required' => true, - 'allowEmpty' => false, - 'label' => t('Backend Name'), - 'helptext' => t('The name of this authentication provider'), - 'value' => $this->getBackendName() - ) - ); + $element = $form->getElement('resource'); - $this->addElement( - 'select', - 'backend_' . $name . '_resource', - array( - 'required' => true, - 'allowEmpty' => false, - 'label' => t('Database Connection'), - 'helptext' => t('The database connection to use for authenticating with this provider'), - 'value' => $this->getBackend()->get('resource'), - 'multiOptions' => $this->getResources() - ) - ); - - $this->addElement( - 'button', - 'btn_submit', - array( - 'type' => 'submit', - 'value' => '1', - 'escape' => false, - 'class' => 'btn btn-cta btn-wide', - 'label' => ' Save Backend' - ) - ); - } - - /** - * Return the datatbase authentication backend configuration for this form - * - * @return array - * - * @see BaseBackendForm::getConfig() - */ - public function getConfig() - { - $prefix = 'backend_' . $this->filterName($this->getBackendName()) . '_'; - $section = $this->getValue($prefix . 'name'); - $cfg = array( - 'backend' => 'db', - 'resource' => $this->getValue($prefix . 'resource'), - ); - - return array($section => $cfg); - } - - /** - * Validate the current configuration by creating a backend and requesting the user count - * - * @return bool Whether validation succeeded or not - * - * @see BaseBackendForm::isValidAuthenticationBackend - */ - public function isValidAuthenticationBackend() - { try { - $dbUserBackend = new DbUserBackend(ResourceFactory::create( - $this->getValue('backend_' . $this->filterName($this->getBackendName()) . '_resource') - )); + $dbUserBackend = new DbUserBackend(ResourceFactory::create($element->getValue())); if ($dbUserBackend->count() < 1) { - $this->addErrorMessage(t("No users found under the specified database backend")); + $element->addError(t('No users found under the specified database backend')); return false; } } catch (Exception $e) { - $this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage())); + $element->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage())); return false; } + return true; } } diff --git a/application/forms/Config/Authentication/LdapBackendForm.php b/application/forms/Config/Authentication/LdapBackendForm.php index 6eaea137c..6056b0911 100644 --- a/application/forms/Config/Authentication/LdapBackendForm.php +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -5,166 +5,134 @@ namespace Icinga\Form\Config\Authentication; use Exception; -use Icinga\Application\Platform; -use Zend_Config; use Icinga\Web\Form; +use Icinga\Web\Request; use Icinga\Data\ResourceFactory; use Icinga\Authentication\Backend\LdapUserBackend; /** - * Form for adding or modifying LDAP authentication backends + * Form class for adding/modifying LDAP authentication backends */ -class LdapBackendForm extends BaseBackendForm +class LdapBackendForm extends Form { /** - * Return content of the resources.ini or previously set resources + * The ldap resource names the user can choose from * - * @return array + * @var array */ - public function getResources() + protected $resources; + + /** + * Initialize this form + */ + public function init() { - if ($this->resources === null) { - $res = ResourceFactory::getResourceConfigs('ldap')->toArray(); - - foreach (array_keys($res) as $key) { - $res[$key] = $key; - } - - return $res; - } else { - return $this->resources; - } + $this->setName('form_config_authbackend_ldap'); } /** - * Create this form and add all required elements + * Set the resource names the user can choose from * - * @see Form::create() + * @param array $resources The resources to choose from + * + * @return self */ - public function create() + public function setResources(array $resources) { - $this->setName('form_modify_backend'); - $name = $this->filterName($this->getBackendName()); - $backend = $this->getBackend(); + $this->resources = $resources; + return $this; + } + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { $this->addElement( 'text', - 'backend_' . $name . '_name', + 'name', array( 'required' => true, - 'allowEmpty' => false, 'label' => t('Backend Name'), - 'helptext' => t('The name of this authentication backend'), - 'value' => $this->getBackendName() + 'description' => t('The name of this authentication backend') ) ); - $this->addElement( 'select', - 'backend_' . $name . '_resource', + 'resource', array( 'required' => true, - 'allowEmpty' => false, 'label' => t('LDAP Resource'), - 'helptext' => t('The resource to use for authenticating with this provider'), - 'value' => $this->getBackend()->get('resource'), - 'multiOptions' => $this->getResources() + 'description' => t('The resource to use for authenticating with this provider'), + 'multiOptions' => false === empty($this->resources) + ? array_combine($this->resources, $this->resources) + : array() ) ); - $this->addElement( 'text', - 'backend_' . $name . '_user_class', + 'user_class', array( - 'required' => true, - 'label' => t('LDAP User Object Class'), - 'helptext' => t('The object class used for storing users on the ldap server'), - 'value' => $backend->get('user_class', 'inetOrgPerson') + 'required' => true, + 'label' => t('LDAP User Object Class'), + 'description' => t('The object class used for storing users on the ldap server'), + 'value' => 'inetOrgPerson' ) ); - $this->addElement( 'text', - 'backend_' . $name . '_user_name_attribute', + 'user_name_attribute', + array( + 'required' => true, + 'label' => t('LDAP User Name Attribute'), + 'description' => t('The attribute name used for storing the user name on the ldap server'), + 'value' => 'uid' + ) + ); + $this->addElement( + 'hidden', + 'backend', array( 'required' => true, - 'label' => t('LDAP User Name Attribute'), - 'helptext' => t('The attribute name used for storing the user name on the ldap server'), - 'value' => $backend->get('user_name_attribute', 'uid') + 'value' => 'ldap' ) ); - $this->addElement( - 'button', - 'btn_submit', - array( - 'type' => 'submit', - 'value' => '1', - 'escape' => false, - 'class' => 'btn btn-cta btn-wide', - 'label' => ' Save Backend' - ) - ); + return $this; } /** - * Return the ldap authentication backend configuration for this form + * Validate that the selected resource is a valid ldap authentication backend * - * @return array - * - * @see BaseBackendForm::getConfig() + * @see Form::onSuccess() */ - public function getConfig() + public function onSuccess(Request $request) { - $prefix = 'backend_' . $this->filterName($this->getBackendName()) . '_'; - $section = $this->getValue($prefix . 'name'); - $cfg = array( - 'backend' => 'ldap', - 'resource' => $this->getValue($prefix . 'resource'), - 'user_class' => $this->getValue($prefix . 'user_class'), - 'user_name_attribute' => $this->getValue($prefix . 'user_name_attribute') - ); - return array($section => $cfg); - } - - /** - * Validate the current configuration by creating a backend and requesting the user count - * - * @return bool Whether validation succeeded or not - * - * @see BaseBackendForm::isValidAuthenticationBacken - */ - public function isValidAuthenticationBackend() - { - if (! Platform::extensionLoaded('ldap')) { - /* - * It should be possible to run icingaweb without the php ldap extension, when - * no ldap backends are needed. When the user tries to create an ldap backend - * without ldap installed we need to show him an error. - */ - $this->addErrorMessage(t('Using ldap is not possible, the php extension "ldap" is not installed.')); + if (false === $this->isValidAuthenticationBackend($this)) { return false; } + } + + /** + * Validate the configuration by creating a backend and requesting the user count + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not + */ + public function isValidAuthenticationBackend(Form $form) + { + $element = $form->getElement('resource'); + try { - $cfg = $this->getConfig(); - $backendName = 'backend_' . $this->filterName($this->getBackendName()) . '_name'; - $backendConfig = new Zend_Config($cfg[$this->getValue($backendName)]); - $backend = ResourceFactory::create($backendConfig->resource); - $testConn = new LdapUserBackend( - $backend, - $backendConfig->user_class, - $backendConfig->user_name_attribute - ); - $testConn->assertAuthenticationPossible(); - /* - if ($testConn->count() === 0) { - throw new IcingaException('No Users Found On Directory Server'); - } - */ - } catch (Exception $exc) { - $this->addErrorMessage( - t('Connection Validation Failed: ' . $exc->getMessage()) + $ldapUserBackend = new LdapUserBackend( + ResourceFactory::create($element->getValue()), + $form->getElement('user_class')->getValue(), + $form->getElement('user_name_attribute')->getValue() ); + $ldapUserBackend->assertAuthenticationPossible(); + } catch (Exception $e) { + $element->addError(sprintf(t('Connection validation failed: %s'), $e->getMessage())); return false; } diff --git a/application/forms/Config/Authentication/ReorderForm.php b/application/forms/Config/Authentication/ReorderForm.php deleted file mode 100644 index b4c3cd9b7..000000000 --- a/application/forms/Config/Authentication/ReorderForm.php +++ /dev/null @@ -1,233 +0,0 @@ -currentOrder = $order; - } - - /** - * Set the name of the authentication backend for which to create the form - * - * @param string $backend The name of the authentication backend - */ - public function setBackendName($backend) - { - $this->backend = $backend; - } - - /** - * Return the name of the currently set backend as it will appear in the form - * - * @return string The name of the backend - */ - public function getBackendName() - { - return $this->filterName($this->backend); - } - - /** - * Create this form - * - * @see Form::create - */ - public function create() - { - if ($this->moveElementUp($this->backend, $this->currentOrder) !== $this->currentOrder) { - $upForm = new Form(); - - $upForm->addElement( - 'hidden', - 'form_backend_order', - array( - 'required' => true, - 'value' => join(',', $this->moveElementUp($this->backend, $this->currentOrder)) - ) - ); - $upForm->addElement( - 'button', - 'btn_' . $this->getBackendName() . '_reorder_up', - array( - 'type' => 'submit', - 'escape' => false, - 'value' => 'btn_' . $this->getBackendName() . '_reorder_up', - 'name' => 'btn_' . $this->getBackendName() . '_reorder_up', - 'label' => $this->getView()->icon('up.png', t('Move up in authentication order')) - ) - ); - - $this->addSubForm($upForm, 'btn_reorder_up'); - } - - if ($this->moveElementDown($this->backend, $this->currentOrder) !== $this->currentOrder) { - $downForm = new Form(); - - $downForm->addElement( - 'hidden', - 'form_backend_order', - array( - 'required' => true, - 'value' => join(',', $this->moveElementDown($this->backend, $this->currentOrder)) - ) - ); - $downForm->addElement( - 'button', - 'btn_' . $this->getBackendName() . '_reorder_down', - array( - 'type' => 'submit', - 'escape' => false, - 'value' => 'btn_' . $this->getBackendName() . '_reorder_down', - 'name' => 'btn_' . $this->getBackendName() . '_reorder_down', - 'label' => $this->getView()->icon('down.png', t('Move down in authentication order')) - ) - ); - - $this->addSubForm($downForm, 'btn_reorder_down'); - } - } - - /** - * Return the flattened result of $this->getValues - * - * @return array The currently set values - * - * @see Form::getValues() - */ - protected function getFlattenedValues() - { - $result = array(); - foreach (parent::getValues() as $key => $value) { - if (is_array($value)) { - $result += $value; - } else { - $result[$key] = $value; - } - } - - return $result; - } - - /** - * Determine whether this form is submitted by testing the submit buttons of both subforms - * - * @return bool Whether the form has been submitted or not - */ - public function isSubmitted() - { - $checkData = $this->getRequest()->getParams(); - return isset($checkData['btn_' . $this->getBackendName() . '_reorder_up']) || - isset($checkData['btn_' . $this->getBackendName() . '_reorder_down']); - } - - /** - * Return the reordered configuration after a reorder button has been submitted - * - * @param Zend_Config $config The configuration to reorder - * - * @return array An array containing the reordered configuration - */ - public function getReorderedConfig(Zend_Config $config) - { - $originalConfig = $config->toArray(); - $newOrder = $this->getFlattenedValues(); - $order = explode(',', $newOrder['form_backend_order']); - - $reordered = array(); - foreach ($order as $key) { - if (isset($originalConfig[$key])) { - $reordered[$key] = $originalConfig[$key]; - } - } - - return $reordered; - } - - /** - * Static helper for moving an element in an array one slot up, if possible - * - * Example: - * - *
-     * $array = array('first', 'second', 'third');
-     * moveElementUp('third', $array); // returns ['first', 'third', 'second']
-     * 
- * - * @param string $key The key to bubble up one slot - * @param array $array The array to work with - * - * @return array The modified array - */ - protected static function moveElementUp($key, array $array) - { - for ($i = 0; $i < count($array) - 1; $i++) { - if ($array[$i + 1] === $key) { - $swap = $array[$i]; - $array[$i] = $array[$i + 1]; - $array[$i + 1] = $swap; - return $array; - } - } - - return $array; - } - - /** - * Static helper for moving an element in an array one slot down, if possible - * - * Example: - * - *
-     * $array = array('first', 'second', 'third');
-     * moveElementDown('first', $array); // returns ['second', 'first', 'third']
-     * 
- * - * @param string $key The key to bubble up one slot - * @param array $array The array to work with - * - * @return array The modified array - */ - protected static function moveElementDown($key, array $array) - { - for ($i = 0; $i < count($array) - 1; $i++) { - if ($array[$i] === $key) { - $swap = $array[$i + 1]; - $array[$i + 1] = $array[$i]; - $array[$i] = $swap; - return $array; - } - } - - return $array; - } -} diff --git a/application/forms/Config/AuthenticationBackendConfigForm.php b/application/forms/Config/AuthenticationBackendConfigForm.php new file mode 100644 index 000000000..e81382cd4 --- /dev/null +++ b/application/forms/Config/AuthenticationBackendConfigForm.php @@ -0,0 +1,322 @@ +setName('form_config_authbackend'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * Set the resource configuration to use + * + * @param Config $resources The resource configuration + * + * @return self + * + * @throws ConfigurationError In case no resources are available for authentication + */ + public function setResourceConfig(Config $resourceConfig) + { + $resources = array(); + foreach ($resourceConfig as $name => $resource) { + $resources[strtolower($resource->type)][] = $name; + } + + if (empty($resources)) { + throw new ConfigurationError(t('Could not find any resources for authentication')); + } + + $this->resources = $resources; + return $this; + } + + /** + * Return a form object for the given backend type + * + * @param string $type The backend type for which to return a form + * + * @return Form + */ + public function getBackendForm($type) + { + if ($type === 'db') { + $form = new DbBackendForm(); + $form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array()); + } elseif ($type === 'ldap') { + $form = new LdapBackendForm(); + $form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array()); + } elseif ($type === 'autologin') { + $form = new AutologinBackendForm(); + } else { + throw new InvalidArgumentException(sprintf(t('Invalid backend type "%s" provided'), $type)); + } + + return $form; + } + + /** + * Add a particular authentication backend + * + * The backend to add is identified by the array-key `name'. + * + * @param array $values The values to extend the configuration with + * + * @return self + * + * @throws InvalidArgumentException In case the backend does already exist + */ + public function add(array $values) + { + $name = isset($values['name']) ? $values['name'] : ''; + if (! $name) { + throw new InvalidArgumentException(t('Authentication backend name missing')); + } elseif ($this->config->get($name) !== null) { + throw new InvalidArgumentException(t('Authentication backend already exists')); + } + + unset($values['name']); + $this->config->{$name} = $values; + return $this; + } + + /** + * Edit a particular authentication backend + * + * @param string $name The name of the backend to edit + * @param array $values The values to edit the configuration with + * + * @return array The edited backend configuration + * + * @throws InvalidArgumentException In case the backend does not exist + */ + public function edit($name, array $values) + { + if (! $name) { + throw new InvalidArgumentException(t('Old authentication backend name missing')); + } elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) { + throw new InvalidArgumentException(t('New authentication backend name missing')); + } elseif (($backendConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown authentication backend provided')); + } + + if ($newName !== $name) { + // Only remove the old entry if it has changed as the order gets screwed when editing backend names + unset($this->config->{$name}); + } + + unset($values['name']); + $this->config->{$newName} = array_merge($backendConfig->toArray(), $values); + return $this->config->{$newName}; + } + + /** + * Remove the given authentication backend + * + * @param string $name The name of the backend to remove + * + * @return array The removed backend configuration + * + * @throws InvalidArgumentException In case the backend does not exist + */ + public function remove($name) + { + if (! $name) { + throw new InvalidArgumentException(t('Authentication backend name missing')); + } elseif (($backendConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown authentication backend provided')); + } + + unset($this->config->{$name}); + return $backendConfig; + } + + /** + * Move the given authentication backend up or down in order + * + * @param string $name The name of the backend to be moved + * @param int $position The new (absolute) position of the backend + * + * @return self + * + * @throws InvalidArgumentException In case the backend does not exist + */ + public function move($name, $position) + { + if (! $name) { + throw new InvalidArgumentException(t('Authentication backend name missing')); + } elseif ($this->config->get($name) === null) { + throw new InvalidArgumentException(t('Unknown authentication backend provided')); + } + + $backendOrder = $this->config->keys(); + array_splice($backendOrder, array_search($name, $backendOrder), 1); + array_splice($backendOrder, $position, 0, $name); + + $newConfig = array(); + foreach ($backendOrder as $backendName) { + $newConfig[$backendName] = $this->config->get($backendName); + } + + $config = new Config($newConfig); + $this->config = $config->setConfigFile($this->config->getConfigFile()); + return $this; + } + + /** + * Add or edit an authentication backend and save the configuration + * + * Performs a connectivity validation using the submitted values. A checkbox is + * added to the form to skip the check if it fails and redirection is aborted. + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) { + $backendForm = $this->getBackendForm($this->getElement('type')->getValue()); + if (false === $backendForm->isValidAuthenticationBackend($this)) { + $this->addElement($this->getForceCreationCheckbox()); + return false; + } + } + + $authBackend = $request->getQuery('auth_backend'); + try { + if ($authBackend === null) { // create new backend + $this->add($this->getValues()); + $message = t('Authentication backend "%s" has been successfully created'); + } else { // edit existing backend + $this->edit($authBackend, $this->getValues()); + $message = t('Authentication backend "%s" has been successfully changed'); + } + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } + + if ($this->save()) { + Notification::success(sprintf($message, $this->getElement('name')->getValue())); + } else { + return false; + } + } + + /** + * Populate the form in case an authentication backend is being edited + * + * @see Form::onRequest() + * + * @throws ConfigurationError In case the backend name is missing in the request or is invalid + */ + public function onRequest(Request $request) + { + $authBackend = $request->getQuery('auth_backend'); + if ($authBackend !== null) { + if ($authBackend === '') { + throw new ConfigurationError(t('Authentication backend name missing')); + } elseif (false === isset($this->config->{$authBackend})) { + throw new ConfigurationError(t('Unknown authentication backend provided')); + } elseif (false === isset($this->config->{$authBackend}->backend)) { + throw new ConfigurationError(sprintf(t('Backend "%s" has no `backend\' setting'), $authBackend)); + } + + $configValues = $this->config->{$authBackend}->toArray(); + $configValues['type'] = $configValues['backend']; + $configValues['name'] = $authBackend; + $this->populate($configValues); + } + } + + /** + * Return a checkbox to be displayed at the beginning of the form + * which allows the user to skip the connection validation + * + * @return Zend_Form_Element + */ + protected function getForceCreationCheckbox() + { + return $this->createElement( + 'checkbox', + 'force_creation', + array( + 'order' => 0, + 'ignore' => true, + 'label' => t('Force Changes'), + 'description' => t('Check this box to enforce changes without connectivity validation') + ) + ); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $backendTypes = array(); + $backendType = isset($formData['type']) ? $formData['type'] : 'db'; + + if (isset($this->resources['db'])) { + $backendTypes['db'] = t('Database'); + } + if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) { + $backendTypes['ldap'] = 'LDAP'; + } + + $autologinBackends = array_filter( + $this->config->toArray(), + function ($authBackendCfg) { + return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin'; + } + ); + if ($backendType === 'autologin' || empty($autologinBackends)) { + $backendTypes['autologin'] = t('Autologin'); + } + + $this->addElement( + 'select', + 'type', + array( + 'ignore' => true, + 'required' => true, + 'autosubmit' => true, + 'label' => t('Backend Type'), + 'description' => t('The type of the resource to use for this authenticaton backend'), + 'multiOptions' => $backendTypes + ) + ); + + if (isset($formData['force_creation']) && $formData['force_creation']) { + // In case another error occured and the checkbox was displayed before + $this->addElement($this->getForceCreationCheckbox()); + } + + $this->addElements($this->getBackendForm($backendType)->createElements($formData)->getElements()); + } +} diff --git a/application/forms/Config/AuthenticationBackendReorderForm.php b/application/forms/Config/AuthenticationBackendReorderForm.php new file mode 100644 index 000000000..3b5e70866 --- /dev/null +++ b/application/forms/Config/AuthenticationBackendReorderForm.php @@ -0,0 +1,68 @@ +setName('form_reorder_authbackend'); + $this->setViewScript('form/reorder-authbackend.phtml'); + } + + /** + * Return the ordered backend names + * + * @return array + */ + public function getBackendOrder() + { + return $this->config->keys(); + } + + /** + * Update the authentication backend order and save the configuration + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $formData = $this->getRequestData($request); + if (isset($formData['backend_newpos'])) { + $configForm = $this->getConfigForm(); + list($backendName, $position) = explode('|', $formData['backend_newpos'], 2); + + try { + if ($configForm->move($backendName, $position)->save()) { + Notification::success(t('Authentication order updated!')); + } else { + return false; + } + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + } + } + } + + /** + * Return the config form for authentication backends + * + * @return ConfigForm + */ + protected function getConfigForm() + { + $form = new AuthenticationBackendConfigForm(); + $form->setIniConfig($this->config); + return $form; + } +} diff --git a/application/forms/Config/ConfirmRemovalForm.php b/application/forms/Config/ConfirmRemovalForm.php deleted file mode 100644 index 65624c7b5..000000000 --- a/application/forms/Config/ConfirmRemovalForm.php +++ /dev/null @@ -1,69 +0,0 @@ -targetName = $name; - $this->removeTarget = $target; - } - - /** - * Create the confirmation form - * - * @see Form::create() - */ - public function create() - { - $this->setName('form_confirm_removal'); - $this->addElement( - 'hidden', - $this->targetName, - array( - 'value' => $this->removeTarget, - 'required' => true - ) - ); - - $this->addElement( - 'button', - 'btn_submit', - array( - 'type' => 'submit', - 'escape' => false, - 'value' => '1', - 'class' => 'btn btn-cta btn-common', - 'label' => ' Confirm Removal' - ) - ); - } -} diff --git a/application/forms/Config/General/ApplicationConfigForm.php b/application/forms/Config/General/ApplicationConfigForm.php new file mode 100644 index 000000000..fda695886 --- /dev/null +++ b/application/forms/Config/General/ApplicationConfigForm.php @@ -0,0 +1,118 @@ +setName('form_config_general_application'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $languages = array(); + foreach (Translator::getAvailableLocaleCodes() as $language) { + $languages[$language] = $language; + } + + $this->addElement( + 'select', + 'global_language', + array( + 'label' => t('Default Language'), + 'required' => true, + 'multiOptions' => $languages, + 'description' => t( + 'Select the language to use by default. Can be overwritten by a user in his preferences.' + ) + ) + ); + + $tzList = array(); + foreach (DateTimeZone::listIdentifiers() as $tz) { + $tzList[$tz] = $tz; + } + + $this->addElement( + 'select', + 'global_timezone', + array( + 'label' => t('Default Application Timezone'), + 'required' => true, + 'multiOptions' => $tzList, + 'description' => t( + 'Select the timezone to be used as the default. User\'s can set their own timezone if' + . ' they like to, but this is the timezone to be used as the default setting .' + ), + 'value' => date_default_timezone_get() + ) + ); + + $this->addElement( + 'text', + 'global_modulePath', + array( + 'label' => t('Module Path'), + 'required' => true, + 'description' => t( + 'Contains the directories that will be searched for available modules, separated by ' + . 'colons. Modules that don\'t exist in these directories can still be symlinked in ' + . 'the module folder, but won\'t show up in the list of disabled modules.' + ), + 'value' => realpath(ICINGAWEB_APPDIR . '/../modules') + ) + ); + + $this->addElement( + 'select', + 'preferences_type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => t('User Preference Storage Type'), + 'multiOptions' => array( + 'ini' => t('File System (INI Files)'), + 'db' => t('Database'), + 'null' => t('Don\'t Store Preferences') + ) + ) + ); + if (isset($formData['preferences_type']) && $formData['preferences_type'] === 'db') { + $backends = array(); + foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) { + if ($resource['type'] === 'db') { + $backends[$name] = $name; + } + } + + $this->addElement( + 'select', + 'preferences_resource', + array( + 'required' => true, + 'multiOptions' => $backends, + 'label' => t('Database Connection') + ) + ); + } + + return $this; + } +} diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php new file mode 100644 index 000000000..733c306b9 --- /dev/null +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -0,0 +1,127 @@ +setName('form_config_general_logging'); + } + + /** + * (non-PHPDoc) + * @see Form::createElements() For the method documentation. + */ + public function createElements(array $formData) + { + $this->addElement( + 'select', + 'logging_log', + array( + 'required' => true, + 'class' => 'autosubmit', + 'label' => t('Logging Type'), + 'description' => t('The type of logging to utilize.'), + 'multiOptions' => array( + 'syslog' => 'Syslog', + 'file' => t('File'), + 'none' => t('None') + ) + ) + ); + + if (! isset($formData['logging_log']) || $formData['logging_log'] !== 'none') { + $this->addElement( + 'select', + 'logging_level', + array( + 'required' => true, + 'label' => t('Logging Level'), + 'description' => t('The maximum logging level to emit.'), + 'multiOptions' => array( + Logger::$levels[Logger::ERROR] => t('Error'), + Logger::$levels[Logger::WARNING] => t('Warning'), + Logger::$levels[Logger::INFO] => t('Information'), + Logger::$levels[Logger::DEBUG] => t('Debug') + ) + ) + ); + } + + if (isset($formData['logging_log']) && $formData['logging_log'] === 'syslog') { + $this->addElement( + 'text', + 'logging_application', + array( + 'required' => true, + 'label' => t('Application Prefix'), + 'description' => t('The name of the application by which to prefix syslog messages.'), + 'value' => 'icingaweb', + 'validators' => array( + array( + 'Regex', + false, + array( + 'pattern' => '/^[^\W]+$/', + 'messages' => array( + 'regexNotMatch' => 'The application prefix cannot contain any whitespaces.' + ) + ) + ) + ) + ) + ); + /* + * Note(el): Since we provide only one possible value for the syslog facility, I opt against exposing + * this configuration. + */ +// $this->addElement( +// 'select', +// 'logging_facility', +// array( +// 'required' => true, +// 'label' => t('Facility'), +// 'description' => t('The syslog facility to utilize.'), +// 'multiOptions' => array( +// 'user' => 'LOG_USER' +// ) +// ) +// ); + } elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') { + $this->addElement( + 'text', + 'logging_file', + array( + 'required' => true, + 'label' => t('File path'), + 'description' => t('The full path to the log file to write messages to.'), + 'value' => $this->getDefaultLogDir(), + 'validators' => array(new WritablePathValidator()) + ) + ); + } + + return $this; + } + + /** + * Return the default logging directory for type 'file' + * + * @return string + */ + protected function getDefaultLogDir() + { + return realpath(Icinga::app()->getApplicationDir('../var/log/icingaweb.log')); + } +} diff --git a/application/forms/Config/GeneralConfigForm.php b/application/forms/Config/GeneralConfigForm.php new file mode 100644 index 000000000..0b55704c2 --- /dev/null +++ b/application/forms/Config/GeneralConfigForm.php @@ -0,0 +1,76 @@ +setName('form_config_general'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $appConfigForm = new ApplicationConfigForm(); + $loggingConfigForm = new LoggingConfigForm(); + $this->addElements($appConfigForm->createElements($formData)->getElements()); + $this->addElements($loggingConfigForm->createElements($formData)->getElements()); + } + + /** + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $sections = array(); + foreach ($this->getValues() as $sectionAndPropertyName => $value) { + list($section, $property) = explode('_', $sectionAndPropertyName); + if (! isset($sections[$section])) { + $sections[$section] = array(); + } + $sections[$section][$property] = $value; + } + foreach ($sections as $section => $config) { + $this->config->{$section} = $config; + } + + if ($this->save()) { + Notification::success(t('New configuration has successfully been stored')); + } else { + return false; + } + } + + /** + * @see Form::onRequest() + */ + public function onRequest(Request $request) + { + $values = array(); + foreach ($this->config as $section => $properties) { + foreach ($properties as $name => $value) { + $values[$section . '_' . $name] = $value; + } + } + + $this->populate($values); + } +} diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php deleted file mode 100644 index debb27d44..000000000 --- a/application/forms/Config/GeneralForm.php +++ /dev/null @@ -1,270 +0,0 @@ -configDir = $dir; - } - - /** - * Return the config path set for this form or the application wide config path if none is set - * - * @return string - * - * @see IcingaConfig::configDir - */ - public function getConfigDir() - { - return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir; - } - - /** - * Set an alternative array of resources that should be used instead of the DBFactory resource set - * (used for testing) - * - * @param array $resources The resources to use for populating the db selection field - */ - public function setResources(array $resources) - { - $this->resources = $resources; - } - - /** - * Return content of the resources.ini or previously set resources for displaying in the database selection field - * - * @return array - */ - public function getResources() - { - if ($this->resources === null) { - return ResourceFactory::getResourceConfigs()->toArray(); - } else { - return $this->resources; - } - } - - /** - * Add a select field for setting the default language - * - * Possible values are determined by Translator::getAvailableLocaleCodes. - * - * @param Zend_Config $cfg The "global" section of the config.ini - */ - private function addLanguageSelection(Zend_Config $cfg) - { - $languages = array(); - foreach (Translator::getAvailableLocaleCodes() as $language) { - $languages[$language] = $language; - } - - $this->addElement( - 'select', - 'language', - array( - 'label' => t('Default Language'), - 'required' => true, - 'multiOptions' => $languages, - 'helptext' => t( - 'Select the language to use by default. Can be overwritten by a user in his preferences.' - ), - 'value' => $cfg->get('language', Translator::DEFAULT_LOCALE) - ) - ); - } - - /** - * Add a select field for setting the default timezone. - * - * Possible values are determined by DateTimeZone::listIdentifiers - * - * @param Zend_Config $cfg The "global" section of the config.ini - */ - private function addTimezoneSelection(Zend_Config $cfg) - { - $tzList = array(); - foreach (DateTimeZone::listIdentifiers() as $tz) { - $tzList[$tz] = $tz; - } - $helptext = 'Select the timezone to be used as the default. User\'s can set their own timezone if' - . ' they like to, but this is the timezone to be used as the default setting .'; - - $this->addElement( - 'select', - 'timezone', - array( - 'label' => 'Default Application Timezone', - 'required' => true, - 'multiOptions' => $tzList, - 'helptext' => $helptext, - 'value' => $cfg->get('timezone', date_default_timezone_get()) - ) - ); - } - - /** - * Add configuration settings for module paths - * - * @param Zend_Config $cfg The "global" section of the config.ini - */ - private function addModuleSettings(Zend_Config $cfg) - { - $this->addElement( - 'text', - 'module_path', - array( - 'label' => 'Module Path', - 'required' => true, - 'helptext' => 'Contains the directories that will be searched for available modules, separated by ' . - ' colons. Modules that don\'t exist in these directories can still be symlinked in the module ' . - ' folder, but won\'t show up in the list of disabled modules.', - 'value' => $cfg->get('modulePath', realpath(ICINGAWEB_APPDIR . '/../modules')) - ) - ); - } - - /** - * Add form elements for setting the user preference storage backend - * - * @param Zend_Config $cfg The Zend_config object of preference section - */ - public function addUserPreferencesDialog(Zend_Config $cfg) - { - $backend = $cfg->get('type', 'ini'); - if ($this->getRequest()->get('preferences_type', null) !== null) { - $backend = $this->getRequest()->get('preferences_type'); - } - $this->addElement( - 'select', - 'preferences_type', - array( - 'label' => 'User Preference Storage Type', - 'required' => true, - 'value' => $backend, - 'multiOptions' => array( - 'ini' => 'File System (INI Files)', - 'db' => 'Database', - 'null' => 'Don\'t Store Preferences' - ) - ) - ); - - $backends = array(); - foreach ($this->getResources() as $name => $resource) { - if ($resource['type'] !== 'db') { - continue; - } - $backends[$name] = $name; - } - - $txtPreferencesDbResource = new Zend_Form_Element_Select( - array( - 'name' => 'preferences_db_resource', - 'label' => 'Database Connection', - 'required' => $backend === 'db', - 'condition' => $backend === 'db', - 'value' => $cfg->get('resource'), - 'multiOptions' => $backends - ) - ); - $validator = new WritablePathValidator(); - $validator->setRequireExistence(); - $this->addElement($txtPreferencesDbResource); - - $txtPreferencesDbResource->addDecorator(new ConditionalHidden()); - $this->enableAutoSubmit( - array( - 'preferences_type' - ) - ); - } - - /** - * Create the general form, using the provided configuration - * - * @see Form::create() - */ - public function create() - { - $config = $this->getConfiguration(); - $global = $config->global; - if ($global === null) { - $global = new Zend_Config(array()); - } - $preferences = $config->preferences; - if ($preferences === null) { - $preferences = new Zend_Config(array()); - } - $this->setName('form_config_general'); - $this->addLanguageSelection($global); - $this->addTimezoneSelection($global); - $this->addModuleSettings($global); - $this->addUserPreferencesDialog($preferences); - - $this->setSubmitLabel('Save Changes'); - } - - /** - * Return an Zend_Config object containing the configuration set in this form - * - * @return Zend_Config - */ - public function getConfig() - { - $config = $this->getConfiguration(); - if ($config->global === null) { - $config->global = new Zend_Config(array(), true); - } - if ($config->preferences === null) { - $config->preferences = new Zend_Config(array(), true); - } - - $values = $this->getValues(); - $cfg = clone $config; - $cfg->global->language = $values['language']; - $cfg->global->timezone = $values['timezone']; - $cfg->global->modulePath = $values['module_path']; - $cfg->preferences->type = $values['preferences_type']; - if ($cfg->preferences->type === 'db') { - $cfg->preferences->resource = $values['preferences_db_resource']; - } - - return $cfg; - } -} diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php deleted file mode 100644 index 1fcb30fa5..000000000 --- a/application/forms/Config/LoggingForm.php +++ /dev/null @@ -1,178 +0,0 @@ -getApplicationDir() . '/../var/log/icingaweb.log'); - } - - /** - * Create this logging configuration form - * - * @see Form::create() - */ - public function create() - { - $this->setName('form_config_logging'); - - $config = $this->getConfiguration(); - if (($loggingConfig = $config->logging) === null) { - $loggingConfig = new Zend_Config(array()); - } - - $this->addElement( - 'checkbox', - 'logging_enable', - array( - 'required' => true, - 'label' => t('Logging Enabled'), - 'helptext' => t('Check this to enable logging.'), - 'value' => $loggingConfig->enable ? 1 : 0 - ) - ); - $this->addElement( - 'select', - 'logging_level', - array( - 'required' => true, - 'label' => t('Logging Level'), - 'helptext' => t('The maximum loglevel to emit.'), - 'value' => intval($loggingConfig->get('level', 0)), - 'multiOptions' => array( - 0 => t('Error'), - 1 => t('Warning'), - 2 => t('Information'), - 3 => t('Debug') - ) - ) - ); - $this->addElement( - 'select', - 'logging_type', - array( - 'required' => true, - 'label' => t('Logging Type'), - 'helptext' => t('The type of logging to utilize.'), - 'value' => $loggingConfig->get('type', 'file'), - 'multiOptions' => array( - 'file' => t('File'), - 'syslog' => 'Syslog' - ) - ) - ); - $this->enableAutoSubmit(array('logging_type')); - - switch ($this->getRequest()->getParam('logging_type', $loggingConfig->get('type', 'file'))) - { - case 'syslog': - $this->addElement( - 'text', - 'logging_application', - array( - 'required' => true, - 'label' => t('Application Prefix'), - 'helptext' => t('The name of the application by which to prefix syslog messages.'), - 'value' => $loggingConfig->get('application', 'icingaweb'), - 'validators' => array( - array( - 'Regex', - false, - array( - 'pattern' => '/^[^\W]+$/', - 'messages' => array( - 'regexNotMatch' => 'The application prefix cannot contain any whitespaces.' - ) - ) - ) - ) - ) - ); - $this->addElement( - 'select', - 'logging_facility', - array( - 'required' => true, - 'label' => t('Facility'), - 'helptext' => t('The Syslog facility to utilize.'), - 'value' => $loggingConfig->get('facility', 'LOG_USER'), - 'multiOptions' => array( - 'LOG_USER' => 'LOG_USER' - ) - ) - ); - break; - case 'file': - default: - $this->addElement( - 'text', - 'logging_target', - array( - 'required' => true, - 'label' => t('Filepath'), - 'helptext' => t('The logfile to write messages to.'), - 'value' => $loggingConfig->target ? $loggingConfig->target : $this->getDefaultLogDir(), - 'validators' => array(new WritablePathValidator()) - ) - ); - } - - $this->setSubmitLabel('{{SAVE_ICON}} Save Changes'); - } - - public function isValid($data) { - foreach ($this->getElements() as $key => $element) { - // Initialize all empty elements with their default values. - if (!isset($data[$key])) { - $data[$key] = $element->getValue(); - } - } - return parent::isValid($data); - } - - /** - * Return a Zend_Config object containing the state defined in this form - * - * @return Zend_Config The config defined in this form - */ - public function getConfig() - { - $values = $this->getValues(); - $cfg = $this->getConfiguration()->toArray(); - - $cfg['logging']['enable'] = $values['logging_enable'] == 1; - $cfg['logging']['level'] = $values['logging_level']; - - switch ($values['logging_type']) - { - case 'file': - $cfg['logging']['type'] = 'file'; - $cfg['logging']['target'] = $values['logging_target']; - break; - case 'syslog': - $cfg['logging']['type'] = 'syslog'; - $cfg['logging']['application'] = $values['logging_application']; - $cfg['logging']['facility'] = $values['logging_facility']; - break; - } - - return new Zend_Config($cfg); - } -} diff --git a/application/forms/Config/Resource/DbResourceForm.php b/application/forms/Config/Resource/DbResourceForm.php new file mode 100644 index 000000000..4fd7297d9 --- /dev/null +++ b/application/forms/Config/Resource/DbResourceForm.php @@ -0,0 +1,130 @@ +setName('form_config_resource_db'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'select', + 'db', + array( + 'required' => true, + 'label' => t('Database Type'), + 'description' => t('The type of SQL database'), + 'multiOptions' => array( + 'mysql' => 'MySQL', + 'pgsql' => 'PostgreSQL' + //'oracle' => 'Oracle' + ) + ) + ); + $this->addElement( + 'text', + 'host', + array ( + 'required' => true, + 'label' => t('Host'), + 'description' => t('The hostname of the database'), + 'value' => 'localhost' + ) + ); + $this->addElement( + new Number( + array( + 'required' => true, + 'name' => 'port', + 'label' => t('Port'), + 'description' => t('The port to use'), + 'value' => 3306 + ) + ) + ); + $this->addElement( + 'text', + 'dbname', + array( + 'required' => true, + 'label' => t('Database Name'), + 'description' => t('The name of the database to use') + ) + ); + $this->addElement( + 'text', + 'username', + array ( + 'required' => true, + 'label' => t('Username'), + 'description' => t('The user name to use for authentication') + ) + ); + $this->addElement( + 'password', + 'password', + array( + 'required' => true, + 'renderPassword' => true, + 'label' => t('Password'), + 'description' => t('The password to use for authentication') + ) + ); + + return $this; + } + + /** + * Validate that the current configuration points to a valid resource + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (false === $this->isValidResource($this)) { + return false; + } + } + + /** + * Validate the resource configuration by trying to connect with it + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not + */ + public function isValidResource(Form $form) + { + try { + $resource = ResourceFactory::createResource(new Zend_Config($form->getValues())); + $resource->getConnection()->getConnection(); + } catch (Exception $e) { + $form->addError(t('Connectivity validation failed, connection to the given resource not possible.')); + return false; + } + + return true; + } +} diff --git a/application/forms/Config/Resource/FileResourceForm.php b/application/forms/Config/Resource/FileResourceForm.php new file mode 100644 index 000000000..d1d33a749 --- /dev/null +++ b/application/forms/Config/Resource/FileResourceForm.php @@ -0,0 +1,50 @@ +setName('form_config_resource_file'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'filename', + array( + 'required' => true, + 'label' => t('Filepath'), + 'description' => t('The filename to fetch information from'), + 'validators' => array(new ReadablePathValidator()) + ) + ); + $this->addElement( + 'text', + 'fields', + array( + 'required' => true, + 'label' => t('Pattern'), + 'description' => t('The regular expression by which to identify columns') + ) + ); + + return $this; + } +} diff --git a/application/forms/Config/Resource/LdapResourceForm.php b/application/forms/Config/Resource/LdapResourceForm.php new file mode 100644 index 000000000..6196f3a17 --- /dev/null +++ b/application/forms/Config/Resource/LdapResourceForm.php @@ -0,0 +1,116 @@ +setName('form_config_resource_ldap'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'hostname', + array( + 'required' => true, + 'label' => t('Host'), + 'description' => t('The hostname or address of the LDAP server to use for authentication'), + 'value' => 'localhost' + ) + ); + $this->addElement( + new Number( + array( + 'required' => true, + 'name' => 'port', + 'label' => t('Port'), + 'description' => t('The port of the LDAP server to use for authentication'), + 'value' => 389 + ) + ) + ); + $this->addElement( + 'text', + 'root_dn', + array( + 'required' => true, + 'label' => t('Root DN'), + 'description' => t('The path where users can be found on the ldap server') + ) + ); + $this->addElement( + 'text', + 'bind_dn', + array( + 'required' => true, + 'label' => t('Bind DN'), + 'description' => t('The user dn to use for querying the ldap server') + ) + ); + $this->addElement( + 'password', + 'bind_pw', + array( + 'required' => true, + 'renderPassword' => true, + 'label' => t('Bind Password'), + 'description' => t('The password to use for querying the ldap server') + ) + ); + + return $this; + } + + /** + * Validate that the current configuration points to a valid resource + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (false === $this->isValidResource($this)) { + return false; + } + } + + /** + * Validate the resource configuration by trying to connect with it + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not + */ + public function isValidResource(Form $form) + { + try { + $resource = ResourceFactory::createResource(new Zend_Config($form->getValues())); + $resource->connect(); + } catch (Exception $e) { + $form->addError(t('Connectivity validation failed, connection to the given resource not possible.')); + return false; + } + + return true; + } +} diff --git a/application/forms/Config/Resource/LivestatusResourceForm.php b/application/forms/Config/Resource/LivestatusResourceForm.php new file mode 100644 index 000000000..92534f220 --- /dev/null +++ b/application/forms/Config/Resource/LivestatusResourceForm.php @@ -0,0 +1,77 @@ +setName('form_config_resource_livestatus'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'socket', + array( + 'required' => true, + 'label' => t('Socket'), + 'description' => t('The path to your livestatus socket used for querying monitoring data'), + 'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/rw/livestatus') + ) + ); + + return $this; + } + + /** + * Validate that the current configuration points to a valid resource + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (false === $this->isValidResource($this)) { + return false; + } + } + + /** + * Validate the resource configuration by trying to connect with it + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether validation succeeded or not + */ + public function isValidResource(Form $form) + { + try { + $resource = ResourceFactory::createResource(new Zend_Config($form->getValues())); + $resource->connect()->disconnect(); + } catch (Exception $e) { + $form->addError(t('Connectivity validation failed, connection to the given resource not possible.')); + return false; + } + + return true; + } +} diff --git a/application/forms/Config/ResourceConfigForm.php b/application/forms/Config/ResourceConfigForm.php new file mode 100644 index 000000000..1aa7edc96 --- /dev/null +++ b/application/forms/Config/ResourceConfigForm.php @@ -0,0 +1,252 @@ +setName('form_config_resource'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * Return a form object for the given resource type + * + * @param string $type The resource type for which to return a form + * + * @return Form + */ + public function getResourceForm($type) + { + if ($type === 'db') { + return new DbResourceForm(); + } elseif ($type === 'ldap') { + return new LdapResourceForm(); + } elseif ($type === 'livestatus') { + return new LivestatusResourceForm(); + } elseif ($type === 'file') { + return new FileResourceForm(); + } else { + throw new InvalidArgumentException(sprintf(t('Invalid resource type "%s" provided'), $type)); + } + } + + /** + * Add a particular resource + * + * The backend to add is identified by the array-key `name'. + * + * @param array $values The values to extend the configuration with + * + * @return self + * + * @thrwos InvalidArgumentException In case the resource does already exist + */ + public function add(array $values) + { + $name = isset($values['name']) ? $values['name'] : ''; + if (! $name) { + throw new InvalidArgumentException(t('Resource name missing')); + } elseif ($this->config->{$name} !== null) { + throw new InvalidArgumentException(t('Resource already exists')); + } + + unset($values['name']); + $this->config->{$name} = $values; + return $this; + } + + /** + * Edit a particular resource + * + * @param string $name The name of the resource to edit + * @param array $values The values to edit the configuration with + * + * @return array The edited configuration + * + * @throws InvalidArgumentException In case the resource does not exist + */ + public function edit($name, array $values) + { + if (! $name) { + throw new InvalidArgumentException(t('Old resource name missing')); + } elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) { + throw new InvalidArgumentException(t('New resource name missing')); + } elseif (($resourceConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown resource provided')); + } + + unset($values['name']); + unset($this->config->{$name}); + $this->config->{$newName} = array_merge($resourceConfig->toArray(), $values); + return $this->config->{$newName}; + } + + /** + * Remove a particular resource + * + * @param string $name The name of the resource to remove + * + * @return array The removed resource configuration + * + * @throws InvalidArgumentException In case the resource does not exist + */ + public function remove($name) + { + if (! $name) { + throw new InvalidArgumentException(t('Resource name missing')); + } elseif (($resourceConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown resource provided')); + } + + unset($this->config->{$name}); + return $resourceConfig; + } + + /** + * Add or edit a resource and save the configuration + * + * Performs a connectivity validation using the submitted values. A checkbox is + * added to the form to skip the check if it fails and redirection is aborted. + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) { + $resourceForm = $this->getResourceForm($this->getElement('type')->getValue()); + if (method_exists($resourceForm, 'isValidResource') && false === $resourceForm->isValidResource($this)) { + $this->addElement($this->getForceCreationCheckbox()); + return false; + } + } + + $resource = $request->getQuery('resource'); + try { + if ($resource === null) { // create new resource + $this->add($this->getValues()); + $message = t('Resource "%s" has been successfully created'); + } else { // edit existing resource + $this->edit($resource, $this->getValues()); + $message = t('Resource "%s" has been successfully changed'); + } + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } + + if ($this->save()) { + Notification::success(sprintf($message, $this->getElement('name')->getValue())); + } else { + return false; + } + } + + /** + * Populate the form in case a resource is being edited + * + * @see Form::onRequest() + * + * @throws ConfigurationError In case the backend name is missing in the request or is invalid + */ + public function onRequest(Request $request) + { + $resource = $request->getQuery('resource'); + if ($resource !== null) { + if ($resource === '') { + throw new ConfigurationError(t('Resource name missing')); + } elseif (false === isset($this->config->{$resource})) { + throw new ConfigurationError(t('Unknown resource provided')); + } + + $configValues = $this->config->{$resource}->toArray(); + $configValues['name'] = $resource; + $this->populate($configValues); + } + } + + /** + * Return a checkbox to be displayed at the beginning of the form + * which allows the user to skip the connection validation + * + * @return Zend_Form_Element + */ + protected function getForceCreationCheckbox() + { + return $this->createElement( + 'checkbox', + 'force_creation', + array( + 'order' => 0, + 'ignore' => true, + 'label' => t('Force Changes'), + 'description' => t('Check this box to enforce changes without connectivity validation') + ) + ); + } + + /** + * @see Form::createElemeents() + */ + public function createElements(array $formData) + { + $resourceType = isset($formData['type']) ? $formData['type'] : 'db'; + + $resourceTypes = array( + 'file' => t('File'), + 'livestatus' => 'Livestatus', + ); + if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) { + $resourceTypes['ldap'] = 'LDAP'; + } + if ($resourceType === 'db' || Platform::extensionLoaded('mysql') || Platform::extensionLoaded('pgsql')) { + $resourceTypes['db'] = t('SQL Database'); + } + + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => t('Resource Name'), + 'description' => t('The unique name of this resource') + ) + ); + $this->addElement( + 'select', + 'type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => t('Resource Type'), + 'description' => t('The type of resource'), + 'multiOptions' => $resourceTypes, + 'value' => $resourceType + ) + ); + + if (isset($formData['force_creation']) && $formData['force_creation']) { + // In case another error occured and the checkbox was displayed before + $this->addElement($this->getForceCreationCheckbox()); + } + + $this->addElements($this->getResourceForm($resourceType)->createElements($formData)->getElements()); + } +} diff --git a/application/forms/Config/ResourceForm.php b/application/forms/Config/ResourceForm.php deleted file mode 100644 index de6be7699..000000000 --- a/application/forms/Config/ResourceForm.php +++ /dev/null @@ -1,507 +0,0 @@ -name = $name; - } - - /** - * Get the current resource name - * - * @return null|string - */ - public function getName() - { - $name = $this->getValue('resource_all_name'); - if (!$name) { - return $this->name; - } - - return $name; - } - - /** - * Set the original name of the resource - * - * @param string $name The name to set - */ - public function setOldName($name) - { - $this->oldName = $name; - } - - /** - * Get the resource name that was initially set - * - * @return null|string - */ - public function getOldName() - { - $oldName = $this->getValue('resource_all_name_old'); - if (!$oldName) { - return $this->oldName; - } - - return $oldName; - } - - /** - * Set the resource configuration to edit. - * - * @param Zend_Config $resource The config to set - */ - public function setResource(Zend_Config $resource) - { - $this->resource = $resource; - } - - /** - * Get the current resource configuration. - * - * @return Zend_Config - */ - public function getResource() - { - if (!isset($this->resource)) { - $this->resource = new Zend_Config(array('type' => 'db')); - } - - return $this->resource; - } - - protected function addDbForm() - { - $this->addElement( - 'select', - 'resource_db_db', - array( - 'required' => true, - 'label' => t('Database Type'), - 'helptext' => t('The type of SQL database you want to create.'), - 'value' => $this->getResource()->get('db', 'mysql'), - 'multiOptions' => array( - 'mysql' => 'MySQL', - 'pgsql' => 'PostgreSQL' - //'oracle' => 'Oracle' - ) - ) - ); - - $this->addElement( - 'text', - 'resource_db_host', - array ( - 'required' => true, - 'label' => t('Host'), - 'helptext' => t('The hostname of the database.'), - 'value' => $this->getResource()->get('host', 'localhost') - ) - ); - - $this->addElement( - new Number( - array( - 'name' => 'resource_db_port', - 'required' => true, - 'label' => t('Port'), - 'helptext' => t('The port to use.'), - 'value' => $this->getResource()->get('port', 3306) - ) - ) - ); - - $this->addElement( - 'text', - 'resource_db_dbname', - array( - 'required' => true, - 'label' => t('Database Name'), - 'helptext' => t('The name of the database to use'), - 'value' => $this->getResource()->get('dbname', '') - ) - ); - - $this->addElement( - 'text', - 'resource_db_username', - array ( - 'required' => true, - 'label' => t('Username'), - 'helptext' => t('The user name to use for authentication.'), - 'value' => $this->getResource()->get('username', '') - ) - ); - - $this->addElement( - 'password', - 'resource_db_password', - array( - 'required' => true, - 'renderPassword' => true, - 'label' => t('Password'), - 'helptext' => t('The password to use for authentication'), - 'value' => $this->getResource()->get('password', '') - ) - ); - } - - protected function addStatusdatForm() - { - $this->addElement( - 'text', - 'resource_statusdat_status_file', - array( - 'required' => true, - 'label' => t('Filepath'), - 'helptext' => t('Location of your icinga status.dat file'), - 'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/status.dat') - ) - ); - - $this->addElement( - 'text', - 'resource_statusdat_object_file', - array( - 'required' => true, - 'label' => t('Filepath'), - 'helptext' => t('Location of your icinga objects.cache file'), - 'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/objects.cache') - ) - ); - } - - protected function addLivestatusForm() - { - $this->addElement( - 'text', - 'resource_livestatus_socket', - array( - 'required' => true, - 'label' => t('Socket'), - 'helptext' => t('The path to your livestatus socket used for querying monitoring data'), - 'value' => $this->getResource()->get('socket', '/usr/local/icinga/var/rw/livestatus') - ) - ); - } - - protected function addLdapForm() - { - $this->addElement( - 'text', - 'resource_ldap_hostname', - array( - 'required' => true, - 'allowEmpty' => false, - 'label' => t('Host'), - 'helptext' => t('The hostname or address of the LDAP server to use for authentication'), - 'value' => $this->getResource()->get('hostname', 'localhost') - ) - ); - - $this->addElement( - 'text', - 'resource_ldap_root_dn', - array( - 'required' => true, - 'label' => t('Root DN'), - 'helptext' => t('The path where users can be found on the ldap server'), - 'value' => $this->getResource()->get('root_dn', 'ou=people,dc=icinga,dc=org') - ) - ); - - $this->addElement( - 'text', - 'resource_ldap_bind_dn', - array( - 'required' => true, - 'label' => t('Bind DN'), - 'helptext' => t('The user dn to use for querying the ldap server'), - 'value' => $this->getResource()->get('bind_dn', 'cn=admin,cn=config') - ) - ); - - $this->addElement( - 'password', - 'resource_ldap_bind_pw', - array( - 'required' => true, - 'renderPassword' => true, - 'label' => t('Bind Password'), - 'helptext' => t('The password to use for querying the ldap server'), - 'value' => $this->getResource()->get('bind_pw', '') - ) - ); - } - - protected function addFileForm() - { - $this->addElement( - 'text', - 'resource_file_filename', - array( - 'required' => true, - 'label' => t('Filepath'), - 'helptext' => t('The filename to fetch information from'), - 'value' => $this->getResource()->get('filename', '') - ) - ); - - $this->addElement( - 'text', - 'resource_file_fields', - array( - 'required' => true, - 'label' => t('Pattern'), - 'helptext' => t('The regular expression by which to identify columns'), - 'value' => $this->getResource()->get('fields', '') - ) - ); - } - - protected function addNameFields() - { - $this->addElement( - 'text', - 'resource_all_name', - array( - 'required' => true, - 'label' => t('Resource Name'), - 'helptext' => t('The unique name of this resource'), - 'value' => $this->getName() - ) - ); - - $this->addElement( - 'hidden', - 'resource_all_name_old', - array( - 'value' => $this->getOldName() - ) - ); - } - - /** - * Add checkbox at the beginning of the form which allows to skip connection validation - */ - protected function addForceCreationCheckbox() - { - $checkbox = new Zend_Form_Element_Checkbox( - array( - 'order' => 0, - 'name' => 'resource_force_creation', - 'label' => t('Force Changes'), - 'helptext' => t('Check this box to enforce changes without connectivity validation') - ) - ); - $checkbox->addDecorator(new HelpText()); - $this->addElement($checkbox); - } - - /** - * Add a select box for choosing the type to use for this backend - */ - protected function addTypeSelectionBox() - { - $this->addElement( - 'select', - 'resource_type', - array( - 'required' => true, - 'label' => t('Resource Type'), - 'helptext' => t('The type of resource'), - 'value' => $this->getResource()->type, - 'multiOptions' => array( - 'db' => t('SQL Database'), - 'ldap' => 'LDAP', - 'statusdat' => 'Status.dat', - 'livestatus' => 'Livestatus', - 'file' => t('File') - ) - ) - ); - $this->enableAutoSubmit(array('resource_type')); - } - - /** - * Validate this form with the Zend validation mechanism and perform a validation of the connection - * - * If validation fails, the 'resource_force_creation' checkbox is prepended to the form to allow users to - * skip the connection validation - * - * @param array $data The form input to validate - * - * @return bool True when validation succeeded, false if not - */ - public function isValid($data) - { - if (!parent::isValid($data)) { - return false; - } - if (isset($data['resource_force_creation']) && $data['resource_force_creation']) { - return true; - } - if (!$this->isValidResource()) { - $this->addForceCreationCheckbox(); - return false; - } - return true; - } - - /** - * Test if the changed resource is a valid resource, by instantiating it and - * checking if a connection is possible - * - * @return bool True when a connection to the resource is possible - */ - public function isValidResource() - { - $config = $this->getConfig(); - - try { - switch ($config->type) { - case 'db': - /* - * It should be possible to run icingaweb without the pgsql or mysql extension or Zend-Pdo-Classes, - * in case they aren't actually used. When the user tries to create a resource that depends on an - * uninstalled extension, an error should be displayed. - */ - if ($config->db === 'mysql' && ! Platform::extensionLoaded('mysql')) { - $this->addErrorMessage( - t('You need to install the php extension "mysql" and the ' . - 'Zend_Pdo_Mysql classes to use MySQL database resources.') - ); - return false; - } - if ($config->db === 'pgsql' && ! Platform::extensionLoaded('pgsql')) { - $this->addErrorMessage( - t('You need to install the php extension "pgsql" and the ' . - 'Zend_Pdo_Pgsql classes to use PostgreSQL database resources.') - ); - return false; - } - - $resource = ResourceFactory::createResource($config); - $resource->getConnection()->getConnection(); - break; - case 'statusdat': - if (!file_exists($config->object_file) || !file_exists($config->status_file)) { - $this->addErrorMessage( - t('Connectivity validation failed, the provided file does not exist.') - ); - return false; - } - break; - case 'livestatus': - $resource = ResourceFactory::createResource($config); - $resource->connect()->disconnect(); - break; - case 'ldap': - $resource = ResourceFactory::createResource($config); - $resource->connect(); - break; - case 'file': - if (!file_exists($config->filename)) { - $this->addErrorMessage( - t('Connectivity validation failed, the provided file does not exist.') - ); - return false; - } - break; - } - } catch (Exception $e) { - $this->addErrorMessage(t('Connectivity validation failed, connection to the given resource not possible.')); - return false; - } - - return true; - } - - public function create() - { - $this->addNameFields(); - $this->addTypeSelectionBox(); - - switch ($this->getRequest()->getParam('resource_type', $this->getResource()->type)) { - case 'db': - $this->addDbForm(); - break; - case 'statusdat': - $this->addStatusdatForm(); - break; - case 'livestatus': - $this->addLivestatusForm(); - break; - case 'ldap': - $this->addLdapForm(); - break; - case 'file': - $this->addFileForm(); - break; - } - - $this->setSubmitLabel('{{SAVE_ICON}} Save Changes'); - } - - /** - * Return a configuration containing the backend settings entered in this form - * - * @return Zend_Config The updated configuration for this backend - */ - public function getConfig() - { - $values = $this->getValues(); - - $result = array('type' => $values['resource_type']); - foreach ($values as $key => $value) { - if ($key !== 'resource_type' && $key !== 'resource_all_name' && $key !== 'resource_all_name_old') { - $configKey = explode('_', $key, 3); - if (count($configKey) === 3) { - $result[$configKey[2]] = $value; - } - } - } - - return new Zend_Config($result); - } -} diff --git a/application/forms/ConfigForm.php b/application/forms/ConfigForm.php new file mode 100644 index 000000000..86790d618 --- /dev/null +++ b/application/forms/ConfigForm.php @@ -0,0 +1,68 @@ +config = $config; + return $this; + } + + /** + * Persist the current configuration to disk + * + * If an error occurs the user is shown a view describing the issue and displaying the raw INI configuration. + * + * @return bool Whether the configuration could be persisted + */ + public function save() + { + $writer = new PreservingIniWriter( + array( + 'config' => $this->config, + 'filename' => $this->config->getConfigFile() + ) + ); + + try { + $writer->write(); + } catch (Exception $e) { + $this->addDecorator('ViewScript', array( + 'viewModule' => 'default', + 'viewScript' => 'showConfiguration.phtml', + 'errorMessage' => $e->getMessage(), + 'configString' => $writer->render(), + 'filePath' => $this->config->getConfigFile() + )); + return false; + } + + return true; + } +} diff --git a/application/forms/ConfirmRemovalForm.php b/application/forms/ConfirmRemovalForm.php new file mode 100644 index 000000000..86a2eb02a --- /dev/null +++ b/application/forms/ConfirmRemovalForm.php @@ -0,0 +1,22 @@ +setName('form_confirm_removal'); + $this->setSubmitLabel(t('Confirm Removal')); + } +} diff --git a/application/forms/Dashboard/AddUrlForm.php b/application/forms/Dashboard/AddUrlForm.php index 09759a265..88195654d 100644 --- a/application/forms/Dashboard/AddUrlForm.php +++ b/application/forms/Dashboard/AddUrlForm.php @@ -5,12 +5,8 @@ namespace Icinga\Form\Dashboard; use Icinga\Application\Config as IcingaConfig; -use Icinga\Web\Form; use Icinga\Web\Widget\Dashboard; -use Zend_Form_Element_Text; -use Zend_Form_Element_Submit; -use Zend_Form_Element_Hidden; -use Zend_Form_Element_Select; +use Icinga\Web\Form; /** * Form to add an url a dashboard pane @@ -18,124 +14,103 @@ use Zend_Form_Element_Select; class AddUrlForm extends Form { /** - * Add a selection box for different panes to the form - * - * @param Dashboard $dashboard The dashboard to retrieve the panes from + * Initialize this form */ - private function addPaneSelectionBox(Dashboard $dashboard) + public function init() { - $selectPane = new Zend_Form_Element_Select( - 'pane', - array( - 'label' => 'Dashboard', - 'required' => true, - 'style' => 'display:inline-block;', - - 'multiOptions' => $dashboard->getPaneKeyTitleArray() - ) - ); - - $newDashboardBtn = new Zend_Form_Element_Submit( - 'create_new_pane', - array( - 'label' => 'Create A New Pane', - 'required' => false, - 'class' => 'btn btn-default', - 'style' => 'display:inline-block' - ) - ); - - $newDashboardBtn->removeDecorator('DtDdWrapper'); - $selectPane->removeDecorator('DtDdWrapper'); - $selectPane->removeDecorator('htmlTag'); - - $this->addElement($selectPane); - $this->addElement($newDashboardBtn); - $this->enableAutoSubmit(array('create_new_pane')); + $this->setName('form_dashboard_addurl'); + $this->setSubmitLabel(t('Add To Dashboard')); } /** - * Add a textfield for creating a new pane to this form + * @see Form::createElements() */ - private function addNewPaneTextField($showExistingButton = true) + public function createElements(array $formData) { - $txtCreatePane = new Zend_Form_Element_Text( - 'pane', - array( - 'label' => 'New Dashboard Title', - 'required' => true, - 'style' => 'display:inline-block' - ) - ); - - // Marks this field as a new pane (and prevents the checkbox being displayed when validation errors occur) - $markAsNewPane = new Zend_Form_Element_Hidden( - 'create_new_pane', - array( - 'required' => true, - 'value' => 1 - ) - ); - - $cancelDashboardBtn = new Zend_Form_Element_Submit( - 'use_existing_dashboard', - array( - 'class' => 'btn', - 'escape' => false, - 'label' => 'Use An Existing Dashboard', - 'required' => false - ) - ); - - - $cancelDashboardBtn->removeDecorator('DtDdWrapper'); - $txtCreatePane->removeDecorator('DtDdWrapper'); - $txtCreatePane->removeDecorator('htmlTag'); - - $this->addElement($txtCreatePane); - if ($showExistingButton) { - $this->addElement($cancelDashboardBtn); - } - $this->addElement($markAsNewPane); - } - - /** - * Add elements to this form (used by extending classes) - */ - protected function create() - { - $dashboard = new Dashboard(); - $this->setName('form_dashboard_add'); - $dashboard->readConfig(IcingaConfig::app('dashboard/dashboard')); $this->addElement( 'text', 'url', array( - 'label' => 'Url', - 'required' => true, - 'value' => htmlspecialchars_decode($this->getRequest()->getParam('url', '')) + 'required' => true, + 'label' => t('Url'), + 'helptext' => t('The url being loaded in the dashlet') ) ); - $elems = $dashboard->getPaneKeyTitleArray(); - if (empty($elems) || // show textfield instead of combobox when no pane is available - ($this->getRequest()->getPost('create_new_pane', '0') && // or when a new pane should be created (+ button) - !$this->getRequest()->getPost('use_existing_dashboard', '0')) // and the user didn't click the 'use - // existing' button + $paneSelectionValues = $this->getDashboardPaneSelectionValues(); + if (empty($paneSelectionValues) || + ((isset($formData['create_new_pane']) && $formData['create_new_pane'] != false) && + (false === isset($formData['use_existing_dashboard']) || $formData['use_existing_dashboard'] != true)) ) { - $this->addNewPaneTextField(!empty($elems)); + $this->addElement( + 'text', + 'pane', + array( + 'required' => true, + 'label' => t("The New Pane's Title"), + 'style' => 'display: inline-block' + ) + ); + $this->addElement( // Prevent the button from being displayed again on validation errors + 'hidden', + 'create_new_pane', + array( + 'value' => 1 + ) + ); + if (false === empty($paneSelectionValues)) { + $this->addElement( + 'submit', + 'use_existing_dashboard', + array( + 'ignore' => true, + 'label' => t('Use An Existing Pane'), + 'style' => 'display: inline-block' + ) + ); + } } else { - $this->addPaneSelectionBox($dashboard); + $this->addElement( + 'select', + 'pane', + array( + 'required' => true, + 'label' => t('Pane'), + 'style' => 'display: inline-block;', + 'multiOptions' => $paneSelectionValues + ) + ); + $this->addElement( + 'submit', + 'create_new_pane', + array( + 'ignore' => true, + 'label' => t('Create A New Pane'), + 'style' => 'display: inline-block' + ) + ); } $this->addElement( 'text', 'component', array( - 'label' => 'Title', - 'required' => true, + 'required' => true, + 'label' => t('Title'), + 'helptext' => t('The title for the dashlet') ) ); - $this->setSubmitLabel("Add To Dashboard"); + } + + /** + * Return the names and titles of the available dashboard panes as key-value array + * + * @return array + */ + protected function getDashboardPaneSelectionValues() + { + $dashboard = new Dashboard(); + $dashboard->readConfig(IcingaConfig::app('dashboard/dashboard')); + return $dashboard->getPaneKeyTitleArray(); } } diff --git a/application/forms/Install/LoggingPage.php b/application/forms/Install/LoggingPage.php deleted file mode 100644 index e41faa43e..000000000 --- a/application/forms/Install/LoggingPage.php +++ /dev/null @@ -1,65 +0,0 @@ -setName('logging'); - } - - /** - * Create and return the logging form - * - * @return LoggingForm - */ - protected function createForm() - { - if ($this->loggingForm === null) { - $this->loggingForm = new LoggingForm(); - $this->loggingForm->hideButtons(); - $this->loggingForm->setTokenDisabled(); - $this->loggingForm->setRequest($this->getRequest()); - $this->loggingForm->setConfiguration($this->getConfiguration()); - } - - return $this->loggingForm; - } - - /** - * Create this wizard page - */ - protected function create() - { - $loggingForm = $this->createForm(); - $loggingForm->buildForm(); // Needs to get called manually as it's nothing that Zend knows about - $this->addSubForm($loggingForm, $loggingForm->getName()); - } - - /** - * Return a config containing all values provided by the user - * - * @return Zend_Config - */ - public function getConfig() - { - return $this->createForm()->getConfig(); - } -} diff --git a/application/forms/Preference/GeneralForm.php b/application/forms/Preference/GeneralForm.php deleted file mode 100644 index c78206c5a..000000000 --- a/application/forms/Preference/GeneralForm.php +++ /dev/null @@ -1,163 +0,0 @@ -getUserPreferences(); - $useDefaultLanguage = $this->getRequest()->getParam('default_language', !$prefs->has('app.language')); - - $this->addElement( - 'checkbox', - 'default_language', - array( - 'label' => t('Use Default Language'), - 'value' => $useDefaultLanguage, - 'required' => true - ) - ); - $selectOptions = array( - 'label' => t('Your Current Language'), - 'required' => !$useDefaultLanguage, - 'multiOptions' => $languages, - 'helptext' => t('Use the following language to display texts and messages'), - 'value' => $prefs->get('app.language', $cfg->get('language', Translator::DEFAULT_LOCALE)) - ); - if ($useDefaultLanguage) { - $selectOptions['disabled'] = 'disabled'; - } - $this->addElement('select', 'language', $selectOptions); - $this->enableAutoSubmit(array('default_language')); - } - - /** - * Add a select field for setting the user's timezone. - * - * Possible values are determined by DateTimeZone::listIdentifiers - * 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 value - */ - private function addTimezoneSelection(Zend_Config $cfg) - { - $prefs = $this->getUserPreferences(); - $useGlobalTimezone = $this->getRequest()->getParam('default_timezone', !$prefs->has('app.timezone')); - $detect = new TimezoneDetect(); - - $tzList = array(); - foreach (DateTimeZone::listIdentifiers() as $tz) { - $tzList[$tz] = $tz; - } - - $helptext = 'Use the following timezone for dates and times'; - - if ($useGlobalTimezone && $detect->success() === true) { - $helptext .= '
' . t('Currently your time was detected to be') - . ': ' - . '' . $detect->getTimezoneName() . ''; - } - - $selectTimezone = new Zend_Form_Element_Select( - array( - 'name' => 'timezone', - 'label' => 'Your Current Timezone', - 'required' => !$useGlobalTimezone, - 'multiOptions' => $tzList, - 'helptext' => $helptext, - 'value' => $prefs->get('app.timezone', $cfg->get('timezone', date_default_timezone_get())) - ) - ); - - $this->addElement( - 'checkbox', - 'default_timezone', - array( - 'label' => 'Use Default Timezone', - 'value' => $useGlobalTimezone, - 'required' => true - ) - ); - if ($useGlobalTimezone) { - $selectTimezone->setAttrib('disabled', 1); - } - - $this->addElement($selectTimezone); - - $this->enableAutoSubmit(array('default_timezone')); - } - - /** - * Create the general form, using the global configuration as fallback values for preferences - * - * @see Form::create() - */ - public function create() - { - $this->setName('form_preference_set'); - - $config = $this->getConfiguration(); - $global = $config->global; - if ($global === null) { - $global = new Zend_Config(array()); - } - - $this->addLanguageSelection($global); - $this->addTimezoneSelection($global); - - $this->setSubmitLabel('Save Changes'); - - $this->addElement( - 'checkbox', - 'show_benchmark', - array( - 'label' => 'Use benchmark', - 'value' => $this->getUserPreferences()->get('app.show_benchmark') - ) - ); - } - - /** - * Return an array containing the preferences set in this form - * - * @return array - */ - public function getPreferences() - { - $values = $this->getValues(); - return array( - 'app.language' => $values['default_language'] ? null : $values['language'], - 'app.timezone' => $values['default_timezone'] ? null : $values['timezone'], - 'app.show_benchmark' => $values['show_benchmark'] === '1' ? true : false - ); - } -} diff --git a/application/forms/PreferenceForm.php b/application/forms/PreferenceForm.php new file mode 100644 index 000000000..cdb6ff288 --- /dev/null +++ b/application/forms/PreferenceForm.php @@ -0,0 +1,225 @@ +setName('form_config_preferences'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * Set preferences to work with + * + * @param Preferences $preferences The preferences to work with + * + * @return self + */ + public function setPreferences(Preferences $preferences) + { + $this->preferences = $preferences; + return $this; + } + + /** + * Set the preference store to use + * + * @param PreferencesStore $store The preference store to use + * + * @return self + */ + public function setStore(PreferencesStore $store) + { + $this->store = $store; + } + + /** + * Persist preferences + * + * @return self + */ + public function save() + { + $this->store->load(); // Necessary for patching existing preferences + $this->store->save($this->preferences); + return $this; + } + + /** + * Adjust preferences and persist them + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $webPreferences = $this->preferences->get('icingaweb', array()); + foreach ($this->getValues() as $key => $value) { + if ($value === null) { + if (isset($webPreferences[$key])) { + unset($webPreferences[$key]); + } + } else { + $webPreferences[$key] = $value; + } + } + $this->preferences->icingaweb = $webPreferences; + + // TODO: Is this even necessary in case the session is written on response? + $session = Session::getSession(); + $session->user->setPreferences($this->preferences); + $session->write(); + + try { + $this->save(); + Notification::success(t('Preferences successfully saved')); + } catch (Exception $e) { + Notification::error($e->getMessage()); + } + } + + /** + * Populate preferences + * + * @see Form::onRequest() + */ + public function onRequest(Request $request) + { + $values = $this->preferences->get('icingaweb', array()); + $values['browser_language'] = false === isset($values['language']); + $values['local_timezone'] = false === isset($values['timezone']); + $this->populate($values); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $languages = array(); + foreach (Translator::getAvailableLocaleCodes() as $language) { + $languages[$language] = $language; + } + + $tzList = array(); + foreach (DateTimeZone::listIdentifiers() as $tz) { + $tzList[$tz] = $tz; + } + + $this->addElement( + 'checkbox', + 'browser_language', + array( + 'ignore' => true, + 'required' => true, + 'autosubmit' => true, + 'value' => true, + 'label' => t('Use your browser\'s language suggestions') + ) + ); + + $useBrowserLanguage = isset($formData['browser_language']) ? $formData['browser_language'] == 1 : true; + $languageSelection = $this->createElement( + 'select', + 'language', + array( + 'required' => false === $useBrowserLanguage, + 'label' => t('Your Current Language'), + 'description' => t('Use the following language to display texts and messages'), + 'multiOptions' => $languages, + 'value' => substr(setlocale(LC_ALL, 0), 0, 5) + ) + ); + if ($useBrowserLanguage) { + $languageSelection->setAttrib('disabled', 'disabled'); + } + $this->addElement($languageSelection); + + $this->addElement( + 'checkbox', + 'local_timezone', + array( + 'ignore' => true, + 'required' => true, + 'autosubmit' => true, + 'value' => true, + 'label' => t('Use your local timezone') + ) + ); + + $useLocalTimezone = isset($formData['local_timezone']) ? $formData['local_timezone'] == 1 : true; + $timezoneSelection = $this->createElement( + 'select', + 'timezone', + array( + 'required' => false === $useLocalTimezone, + 'label' => t('Your Current Timezone'), + 'description' => t('Use the following timezone for dates and times'), + 'multiOptions' => $tzList, + 'value' => $this->getDefaultTimezone() + ) + ); + if ($useLocalTimezone) { + $timezoneSelection->setAttrib('disabled', 'disabled'); + } + $this->addElement($timezoneSelection); + + $this->addElement( + 'checkbox', + 'show_benchmark', + array( + 'required' => true, + 'label' => t('Use benchmark') + ) + ); + } + + /** + * Return the current default timezone + * + * @return string + */ + protected function getDefaultTimezone() + { + $detect = new TimezoneDetect(); + if ($detect->success()) { + return $detect->getTimezoneName(); + } else { + return date_default_timezone_get(); + } + } +} diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index e174b6621..205cd7f3d 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -486,7 +486,7 @@ msgstr "Die Objekt-Klasse welche benutzt wird, um Benutzer auf diesem LDAP-Serve #: /usr/local/src/bugfix.master/application/forms/Config/ResourceForm.php:182 msgid "The password to use for authentication" -msgstr "Das Kennwort welche zur Authentifizierung benutzt werden soll" +msgstr "Das Kennwort welches zur Authentifizierung benutzt werden soll" #: /usr/local/src/bugfix.master/application/forms/Config/ResourceForm.php:270 msgid "The password to use for querying the ldap server" diff --git a/application/views/helpers/DateFormat.php b/application/views/helpers/DateFormat.php index 28cb81f32..d3515d184 100644 --- a/application/views/helpers/DateFormat.php +++ b/application/views/helpers/DateFormat.php @@ -4,7 +4,6 @@ use Icinga\Application\Icinga; use Icinga\Util\DateTimeFactory; -use Icinga\Web\Form\Validator\DateTimeValidator; /** * Helper to format date and time. Utilizes DateTimeFactory to ensure time zone awareness @@ -57,7 +56,7 @@ class Zend_View_Helper_DateFormat extends Zend_View_Helper_Abstract public function format($timestamp, $format) { $dt = DateTimeFactory::create(); - if (DateTimeValidator::isUnixTimestamp($timestamp)) { + if (DateTimeFactory::isUnixTimestamp($timestamp)) { $dt->setTimestamp($timestamp); } else { return $timestamp; diff --git a/application/views/helpers/FormDateTime.php b/application/views/helpers/FormDateTime.php index 07d5fb930..cb760a620 100644 --- a/application/views/helpers/FormDateTime.php +++ b/application/views/helpers/FormDateTime.php @@ -2,18 +2,30 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use \Zend_View_Helper_FormElement; - /** - * Helper to generate a "datetime" element + * Render date-and-time input controls */ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement { /** - * Generate a 'datetime' element + * Format date and time + * + * @param DateTime $dateTime + * @param bool $local + * + * @return string + */ + public function formatDate(DateTime $dateTime, $local) + { + $format = (bool) $local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339; + return $dateTime->format($format); + } + + /** + * Render the date-and-time input control * * @param string $name The element name - * @param int $value The default timestamp + * @param DateTime $value The default timestamp * @param array $attribs Attributes for the element tag * * @return string The element XHTML @@ -21,50 +33,41 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement public function formDateTime($name, $value = null, $attribs = null) { $info = $this->_getInfo($name, $value, $attribs); - extract($info); // name, value, attribs, options, listsep, disable - // Is it disabled? + extract($info); // name, id, value, attribs, options, listsep, disable + /** @var string $id */ + /** @var bool $disable */ $disabled = ''; - if ($disabled) { + if ($disable) { $disabled = ' disabled="disabled"'; } - - $jspicker = (isset($attribs['jspicker']) && $attribs['jspicker'] === true) ? true : false; - - if (isset($value) && !empty($value)) { - if ($jspicker) { - $value = ' value="' . $this->view->dateFormat()->format($value, $attribs['defaultFormat']) . '"'; - } else { - $value = ' value="' . $this->view->dateFormat()->formatDateTime($value) . '"'; - } - } else { - $value = ''; + if ($value instanceof DateTime) { + // If value was valid, it's a DateTime object + $value = $this->formatDate($value, $attribs['local']); } - - // Build the element - $xhtml = '
'; - - $xhtml .= '_htmlAttribs($attribs); - - if ($jspicker === true) { - $xhtml .= 'data-icinga-component="app/datetime"'; + $min = ''; + if (! empty($attribs['min'])) { + $min = sprintf(' min="%s"', $this->formatDate($attribs['min'], $attribs['local'])); } - - $xhtml .= $this->getClosingBracket(); - - if ($jspicker === true) { - $xhtml .= '' - . '' - . '' - . '' - . ''; + unset($attribs['min']); // Unset min to not render it again in $this->_htmlAttribs($attribs) + $max = ''; + if (! empty($attribs['max'])) { + $max = sprintf(' max="%s"', $this->formatDate($attribs['max'], $attribs['local'])); } - - $xhtml .= '
'; - - return $xhtml; + unset($attribs['max']); // Unset max to not render it again in $this->_htmlAttribs($attribs) + $type = $attribs['local'] === true ? 'datetime-local' : 'datetime'; + unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs) + $html5 = sprintf( + 'view->escape($name), + $this->view->escape($id), + $this->view->escape($value), + $min, + $max, + $disabled, + $this->_htmlAttribs($attribs), + $this->getClosingBracket() + ); + return $html5; } } diff --git a/application/views/helpers/FormNumber.php b/application/views/helpers/FormNumber.php index a8768f0b7..c3ccdcfd6 100644 --- a/application/views/helpers/FormNumber.php +++ b/application/views/helpers/FormNumber.php @@ -2,29 +2,79 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} +use \Zend_View_Helper_FormElement; + /** - * Helper to generate a number input + * Render number input controls */ -class Zend_View_Helper_FormNumber extends \Zend_View_Helper_FormText +class Zend_View_Helper_FormNumber extends Zend_View_Helper_FormElement { /** - * Generates a html number input + * Format a number * - * @access public + * @param $number * - * @param string $name The element name. - * @param string $value The default value. - * @param array $attribs Attributes which should be added to the input tag. + * @return string + */ + public function formatNumber($number) + { + if (empty($number)) { + return $number; + } + return $this->view->escape( + sprintf( + ctype_digit((string) $number) ? '%d' : '%F', + $number + ) + ); + } + + /** + * Render the number input control * - * @return string The input tag and options XHTML. + * @param string $name + * @param int $value + * @param array $attribs + * + * @return string The rendered number input control */ public function formNumber($name, $value = null, $attribs = null) { - return '_htmlAttribs($attribs) - . $this->getClosingBracket(); + $info = $this->_getInfo($name, $value, $attribs); + extract($info); // name, id, value, attribs, options, listsep, disable + /** @var string $id */ + /** @var bool $disable */ + $disabled = ''; + if ($disable) { + $disabled = ' disabled="disabled"'; + } + $min = ''; + if (isset($attribs['min'])) { + $min = sprintf(' min="%s"', $this->formatNumber($attribs['min'])); + } + unset($attribs['min']); // Unset min to not render it again in $this->_htmlAttribs($attribs) + $max = ''; + if (isset($attribs['max'])) { + $max = sprintf(' max="%s"', $this->formatNumber($attribs['max'])); + } + unset($attribs['max']); // Unset max to not render it again in $this->_htmlAttribs($attribs) + $step = ''; + if (isset($attribs['step'])) { + $step = sprintf(' step="%s"', $attribs['step'] === 'any' ? 'any' : $this->formatNumber($attribs['step'])); + } + unset($attribs['step']); // Unset step to not render it again in $this->_htmlAttribs($attribs) + $html5 = sprintf( + 'view->escape($name), + $this->view->escape($id), + $this->view->escape($this->formatNumber($value)), + $min, + $max, + $step, + $disabled, + $this->_htmlAttribs($attribs), + $this->getClosingBracket() + ); + return $html5; } } diff --git a/application/views/helpers/Util.php b/application/views/helpers/Util.php index 25b209a36..ccc3e8e1c 100644 --- a/application/views/helpers/Util.php +++ b/application/views/helpers/Util.php @@ -64,6 +64,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract return date('H:i d.m.Y', $timestamp); } + /** + * @deprecated Not used. This is monitoring module stuff. + */ public static function getHostStateClassName($state) { $class = 'unknown'; @@ -82,6 +85,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract return $class; } + /** + * @deprecated Crap. This is monitoring module stuff. + */ public static function getHostStateName($state) { $states = array( @@ -98,6 +104,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract return sprintf('OUT OF BOUNDS (%s)', var_export($state, 1)); } + /** + * @deprecated Crap. This is monitoring module stuff. + */ public static function getServiceStateName($state) { if ($state === null) { $state = 3; } // really? diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml deleted file mode 100644 index a86e97600..000000000 --- a/application/views/scripts/config/authentication.phtml +++ /dev/null @@ -1,45 +0,0 @@ -
-tabs ?> -
- -
-messageBox)) { - // TODO: Get rid of such boxes -> notifications? - echo $this->messageBox->render(); -} -?> - -

- icon('create.png'); ?> Create A New LDAP Authentication Backend -
- icon('create.png'); ?> Create A New DB Authentication Backend -

- - - - - - - - - backends as $backend): ?> - - - - - - -
ResourceRemoveOrder
- - icon('edit.png') ?> escape($backend->name); ?> - - - - icon('remove.png', 'Remove') ?> - - - reorderForm; ?> -
-
diff --git a/application/views/scripts/config/authentication/create.phtml b/application/views/scripts/config/authentication/create.phtml index fd329b63a..427758292 100644 --- a/application/views/scripts/config/authentication/create.phtml +++ b/application/views/scripts/config/authentication/create.phtml @@ -1,15 +1,10 @@ -

- - Create New Authentication Backend -

- - -messageBox)): ?> - messageBox->render() ?> - - +
+ tabs->showOnlyCloseButton() ?> +
+

translate('Create New Authentication Backend'); ?>

- Create a new backend for authenticating your users. This backend will be added at the end of your authentication order. + translate( + 'Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.' + ); ?>

- -form ?> \ No newline at end of file + \ No newline at end of file diff --git a/application/views/scripts/config/authentication/modify.phtml b/application/views/scripts/config/authentication/modify.phtml index ca2cef6f9..b01d7095a 100644 --- a/application/views/scripts/config/authentication/modify.phtml +++ b/application/views/scripts/config/authentication/modify.phtml @@ -1,18 +1,5 @@ -

- - Edit Backend "escape($this->name); ?>" -

- -messageBox)): ?> - messageBox->render() ?> - - -form->getErrorMessages()): ?> -
- form->getErrorMessages() as $error): ?> - escape($error); ?>
- +
+ tabs->showOnlyCloseButton() ?>
- - -form ?> \ No newline at end of file +

translate('Edit Backend'); ?>

+ \ No newline at end of file diff --git a/application/views/scripts/config/authentication/remove.phtml b/application/views/scripts/config/authentication/remove.phtml index ed478d24b..424aff9a0 100644 --- a/application/views/scripts/config/authentication/remove.phtml +++ b/application/views/scripts/config/authentication/remove.phtml @@ -1,10 +1,5 @@ -

- - Remove Backend "escape($this->name); ?>" -

- -messageBox)): ?> - messageBox->render() ?> - - -form ?> \ No newline at end of file +
+ tabs->showOnlyCloseButton() ?> +
+

translate('Remove Backend'); ?>

+ \ No newline at end of file diff --git a/application/views/scripts/config/authentication/reorder.phtml b/application/views/scripts/config/authentication/reorder.phtml new file mode 100644 index 000000000..0ae7850eb --- /dev/null +++ b/application/views/scripts/config/authentication/reorder.phtml @@ -0,0 +1,11 @@ +
+ +
+
+

+ + icon('create.png'); ?>translate('Create A New Authentication Backend'); ?> + +

+ +
\ No newline at end of file diff --git a/application/views/scripts/config/modules.phtml b/application/views/scripts/config/modules.phtml index 086d0a61a..e0204b24f 100644 --- a/application/views/scripts/config/modules.phtml +++ b/application/views/scripts/config/modules.phtml @@ -1,6 +1,6 @@
tabs ?> -

Installed Modules

+

translate('Installed Modules') ?>

paginationControl($modules) ?>
@@ -11,9 +11,9 @@ enabled): ?> - icon('success.png', 'Module is enabled') ?> + icon('success.png', $this->translate('Module is enabled')) ?> - icon('remove.png', 'Module is disabled') ?> + icon('remove.png', $this->translate('Module is disabled')) ?> -tabs ?> +
-
-messageBox)) { - // TODO: Get rid of messageBoxes in favour of notifications - echo $this->messageBox->render(); -} - -?> -

icon('create.png'); ?> Create A New Resource

- - - - - - - +

+ + icon('create.png'); ?> translate('Create A New Resource'); ?> + +

+
ResourceRemove
+ + + + + resources as $name => $resource): ?> - - - - + + + + -
translate('Resource'); ?>translate('Remove'); ?>
icon('edit.png') ?> escape($name); ?>icon('remove.png'); ?>
+ + icon('edit.png'); ?> escape($name); ?> + + + + icon('remove.png'); ?> + +
-
- + + + \ No newline at end of file diff --git a/application/views/scripts/config/resource/create.phtml b/application/views/scripts/config/resource/create.phtml index f82354557..0750a5aa9 100644 --- a/application/views/scripts/config/resource/create.phtml +++ b/application/views/scripts/config/resource/create.phtml @@ -1,14 +1,6 @@ -

- - Create New Resource -

- -messageBox)): ?> - messageBox->render() ?> - - -

- Resources are entities that provide data to Icingaweb. -

- -form ?> \ No newline at end of file +
+ tabs->showOnlyCloseButton() ?> +
+

translate('Create A New Resource'); ?>

+

translate('Resources are entities that provide data to Icinga Web 2.'); ?>

+ \ No newline at end of file diff --git a/application/views/scripts/config/resource/modify.phtml b/application/views/scripts/config/resource/modify.phtml index 5a0871aed..d355aeb4c 100644 --- a/application/views/scripts/config/resource/modify.phtml +++ b/application/views/scripts/config/resource/modify.phtml @@ -1,18 +1,5 @@ -

- - Edit Resource "escape($this->name); ?>" -

- -messageBox)): ?> - messageBox->render() ?> - - -form->getErrorMessages()): ?> -
- form->getErrorMessages() as $error): ?> - escape($error); ?>
- +
+ tabs->showOnlyCloseButton() ?>
- - -form ?> \ No newline at end of file +

translate('Edit Existing Resource'); ?>

+ \ No newline at end of file diff --git a/application/views/scripts/config/resource/remove.phtml b/application/views/scripts/config/resource/remove.phtml index 3b5ffbfc1..14829ab40 100644 --- a/application/views/scripts/config/resource/remove.phtml +++ b/application/views/scripts/config/resource/remove.phtml @@ -1,10 +1,5 @@ -

- - Remove Resource "escape($this->name); ?>" -

- -messageBox)): ?> - messageBox->render() ?> - - -form ?> +
+ tabs->showOnlyCloseButton() ?> +
+

translate('Remove Existing Resource'); ?>

+ \ No newline at end of file diff --git a/application/views/scripts/config/show-configuration.phtml b/application/views/scripts/config/show-configuration.phtml deleted file mode 100644 index a8eda9d86..000000000 --- a/application/views/scripts/config/show-configuration.phtml +++ /dev/null @@ -1,31 +0,0 @@ -tabs->render($this); ?> - - -
-
-

WARNING ICONSaving "escape($this->file); ?>.ini" Failed

-
-

- Your escape($this->file); ?> configuration couldn't be stored (error: "exceptionMessage; ?>").
- This could have one or more of the following reasons: -

- -
- -

- Details can be seen in your application log (if you don't have access to this file, call your administrator in this case). -
- In case you can access the configuration file (config/escape($this->file); ?>.ini) by yourself, you can open it and - insert the config manually: -

-

-

-    
-escape($this->iniConfigurationString); ?>
-    
-  
-

\ No newline at end of file diff --git a/application/views/scripts/error/error.phtml b/application/views/scripts/error/error.phtml index e3a71f829..2a900651b 100644 --- a/application/views/scripts/error/error.phtml +++ b/application/views/scripts/error/error.phtml @@ -11,7 +11,4 @@
escape($stackTrace) ?>
-messageBox)) : ?> - messageBox->render(); ?> -
diff --git a/application/views/scripts/form/reorder-authbackend.phtml b/application/views/scripts/form/reorder-authbackend.phtml new file mode 100644 index 000000000..8153e17d2 --- /dev/null +++ b/application/views/scripts/form/reorder-authbackend.phtml @@ -0,0 +1,40 @@ +
+ + + + + + + +getBackendOrder(); ?> + + + + + + + + +
Backendtranslate('Remove'); ?>translate('Order'); ?>
+ + icon('edit.png'); ?> escape($backendNames[$i]); ?> + + + + icon('remove.png', $this->translate('Remove')); ?> + + + 0): ?> + + + + + +
+ getElement($form->getTokenElementName()); ?> + getElement($form->getUidElementName()); ?> +
\ No newline at end of file diff --git a/application/views/scripts/preference/index.phtml b/application/views/scripts/preference/index.phtml index 256f8623c..163381531 100644 --- a/application/views/scripts/preference/index.phtml +++ b/application/views/scripts/preference/index.phtml @@ -1,7 +1,7 @@
-tabs->render($this); ?> +
-form ?> -
+ + \ No newline at end of file diff --git a/application/views/scripts/showConfiguration.phtml b/application/views/scripts/showConfiguration.phtml new file mode 100644 index 000000000..1dde3b16b --- /dev/null +++ b/application/views/scripts/showConfiguration.phtml @@ -0,0 +1,28 @@ +
+

translate('Saving Configuration Failed'); ?>

+
+

+ translate('The file %s couldn\'t be stored. (Error: "%s")'), + $this->escape($filePath), + $this->escape($errorMessage) + ); ?> +
+ translate('This could have one or more of the following reasons:'); ?> +

+ +
+

+ translate('Details can be found in the application log. (If you don\'t have access to this log, call your administrator in this case)'); ?> +
+ translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?> +

+

+

+    escape($configString); ?>
+  
+

\ No newline at end of file diff --git a/config/authentication.ini.in b/config/authentication.ini.in index 71d9e4402..2a2d2a969 100644 --- a/config/authentication.ini.in +++ b/config/authentication.ini.in @@ -18,6 +18,10 @@ backend = ldap resource = internal_ldap user_class = @ldap_user_objectclass@ user_name_attribute = @ldap_attribute_username@ +group_base_dn = @ldap_group_base_dn@ +group_attribute = @ldap_group_attribute@ +group_member_attribute = @ldap_group_member_attribute@ +group_class = @ldap_group_class@ [internal_db_authentication] @internal_auth_disabled@ diff --git a/icingaweb2.spec b/icingaweb2.spec index 5d5b9ec9c..db0929f7b 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -26,12 +26,12 @@ %define revision 1 -%define configdir %{_sysconfdir}/icingaweb -%define sharedir %{_datadir}/icingaweb -%define prefixdir %{_datadir}/icingaweb -%define logdir %{sharedir}/log +%define configdir %{_sysconfdir}/%{name} +%define sharedir %{_datadir}/%{name} +%define prefixdir %{_datadir}/%{name} %define usermodparam -a -G -%define logdir %{_localstatedir}/log/icingaweb +%define logdir %{_localstatedir}/log/%{name} +%define docdir %{sharedir}/log %if "%{_vendor}" == "suse" %define phpname php5 @@ -172,25 +172,26 @@ install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{ap # install public, library, modules %{__mkdir} -p %{buildroot}/%{sharedir} %{__mkdir} -p %{buildroot}/%{logdir} -%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb +%{__mkdir} -p %{buildroot}/%{docdir} +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name} %{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard -%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules -%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring -%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/enabledModules -%{__cp} -r application library modules public %{buildroot}/%{sharedir}/ +%{__cp} -r application doc library modules public %{buildroot}/%{sharedir}/ ## config # authentication is db only -install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini +install -D -m0644 packages/rpm/etc/%{name}/authentication.ini %{buildroot}/%{_sysconfdir}/%{name}/authentication.ini # custom resource paths -install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini +install -D -m0644 packages/rpm/etc/%{name}/resources.ini %{buildroot}/%{_sysconfdir}/%{name}/resources.ini # monitoring module (icinga2) -install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/backends.ini -install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/instances.ini +install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/backends.ini +install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/instances.ini # enable the monitoring module by default -ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring +ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/%{name}/enabledModules/monitoring ## config # install icingacli @@ -228,6 +229,8 @@ fi %config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir} # logs %attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir} +# shipped docs +%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/doc %files -n php-Icinga %attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/application diff --git a/library/Icinga/Application/ApplicationBootstrap.php b/library/Icinga/Application/ApplicationBootstrap.php index 7736f8cf9..958642940 100644 --- a/library/Icinga/Application/ApplicationBootstrap.php +++ b/library/Icinga/Application/ApplicationBootstrap.php @@ -4,10 +4,10 @@ namespace Icinga\Application; +use ErrorException; use Exception; use Zend_Config; use Icinga\Application\Modules\Manager as ModuleManager; -use Icinga\Application\Config; use Icinga\Data\ResourceFactory; use Icinga\Exception\ConfigurationError; use Icinga\Exception\NotReadableError; @@ -348,11 +348,7 @@ abstract class ApplicationBootstrap Logger::create( new Zend_Config( array( - 'enable' => true, - 'level' => Logger::$ERROR, - 'type' => 'syslog', - 'facility' => 'LOG_USER', - 'application' => 'icingaweb' + 'log' => 'syslog' ) ) ); @@ -383,9 +379,20 @@ abstract class ApplicationBootstrap */ protected function setupErrorHandling() { - error_reporting(E_ALL | E_NOTICE); + error_reporting(E_ALL | E_STRICT); ini_set('display_startup_errors', 1); ini_set('display_errors', 1); + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + if (error_reporting() === 0) { + // Error was suppressed with the @-operator + return false; // Continue with the normal error handler + } + switch($errno) { + case E_STRICT: + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + } + return false; // Continue with the normal error handler + }); return $this; } diff --git a/library/Icinga/Application/Cli.php b/library/Icinga/Application/Cli.php index 58daf5f2f..d329ce439 100644 --- a/library/Icinga/Application/Cli.php +++ b/library/Icinga/Application/Cli.php @@ -51,10 +51,9 @@ class Cli extends ApplicationBootstrap Logger::create( new Zend_Config( array( - 'enable' => true, - 'level' => Logger::$INFO, - 'type' => 'file', - 'target' => 'php://stderr' + 'level' => Logger::INFO, + 'log' => 'file', + 'file' => 'php://stderr' ) ) ); diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php index f30684883..c7998a0bb 100644 --- a/library/Icinga/Application/Config.php +++ b/library/Icinga/Application/Config.php @@ -7,7 +7,6 @@ namespace Icinga\Application; use Zend_Config; use Zend_Config_Ini; use Icinga\Exception\NotReadableError; -use Icinga\Exception\ProgrammingError; /** * Global registry of application and module configuration. @@ -22,11 +21,11 @@ class Config extends Zend_Config public static $configDir; /** - * The INI file this configuration has been loaded from + * The INI file this configuration has been loaded from or should be written to * * @var string */ - private $configFile; + protected $configFile; /** * Application config instances per file @@ -42,30 +41,28 @@ class Config extends Zend_Config */ protected static $modules = array(); - private $instance; - /** - * Load configuration from the config file $filename + * Load configuration from the given INI file + * + * @param string $file The file to parse * - * @param string $filename The filename to parse - * @throws NotReadableError When the file does not exist or cannot be read */ - public function __construct($filename) + public static function fromIni($file) { - parent::__construct(array(), true); - $filepath = realpath($filename); + $config = new static(array(), true); + $filepath = realpath($file); + if ($filepath === false) { - $this->configFile = $filename; + $config->setConfigFile($file); } elseif (is_readable($filepath)) { - $this->configFile = $filepath; - $this->merge(new Zend_Config_Ini($filepath)); + $config->setConfigFile($filepath); + $config->merge(new Zend_Config_Ini($filepath)); } else { - throw new NotReadableError( - 'Cannot read config file "%s". Permission denied', - $filename - ); + throw new NotReadableError('Cannot read config file "%s". Permission denied', $filepath); } + + return $config; } /** @@ -80,7 +77,7 @@ class Config extends Zend_Config public static function app($configname = 'config', $fromDisk = false) { if (!isset(self::$app[$configname]) || $fromDisk) { - self::$app[$configname] = new Config(self::resolvePath($configname . '.ini')); + self::$app[$configname] = Config::fromIni(self::resolvePath($configname . '.ini')); } return self::$app[$configname]; } @@ -113,7 +110,7 @@ class Config extends Zend_Config } $moduleConfigs = self::$modules[$modulename]; if (!isset($moduleConfigs[$configname]) || $fromDisk) { - $moduleConfigs[$configname] = new Config( + $moduleConfigs[$configname] = Config::fromIni( self::resolvePath('modules/' . $modulename . '/' . $configname . '.ini') ); } @@ -138,15 +135,28 @@ class Config extends Zend_Config } /** - * Return the application wide config file + * Return this config's file path * - * @return string + * @return string */ public function getConfigFile() { return $this->configFile; } + /** + * Set this config's file path + * + * @param string $filepath The path to the config file + * + * @return self + */ + public function setConfigFile($filepath) + { + $this->configFile = $filepath; + return $this; + } + /** * Prepend configuration base dir if input is relative * diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 6d66412e6..b1b051a6c 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -918,10 +918,21 @@ class Module * Translate a string with the global mt() * * @param $string + * @param null $context + * * @return mixed|string */ - protected function translate($string) + protected function translate($string, $context = null) { - return mt($this->name, $string); + return mt($this->name, $string, $context); + } + + /** + * (non-PHPDoc) + * @see Translator::translatePlural() For the function documentation. + */ + protected function translatePlural($textSingular, $textPlural, $number, $context = null) + { + return mtp($this->name, $textSingular, $textPlural, $number, $context); } } diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index 6a15d392a..89cad22ab 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -278,7 +278,8 @@ class Web extends ApplicationBootstrap if ($this->user !== null && $this->user->getPreferences() !== null) { $detect = new TimezoneDetect(); - $userTimezone = $this->user->getPreferences()->get('app.timezone', $detect->getTimezoneName()); + $userTimezone = $this->user->getPreferences() + ->getValue('icingaweb', 'timezone', $detect->getTimezoneName()); } try { @@ -302,7 +303,7 @@ class Web extends ApplicationBootstrap { parent::setupInternationalization(); if ($this->user !== null && $this->user->getPreferences() !== null - && (($locale = $this->user->getPreferences()->get('app.language')) !== null) + && (($locale = $this->user->getPreferences()->getValue('icingaweb', 'language')) !== null) ) { try { Translator::setupLocale($locale); diff --git a/library/Icinga/Application/functions.php b/library/Icinga/Application/functions.php index 422dfeab7..304c75093 100644 --- a/library/Icinga/Application/functions.php +++ b/library/Icinga/Application/functions.php @@ -5,23 +5,85 @@ use Icinga\Util\Translator; if (extension_loaded('gettext')) { - function t($messageId) + + /** + * (non-PHPDoc) + * @see Translator::translate() For the function documentation. + */ + function t($messageId, $context = null) { - return Translator::translate($messageId, Translator::DEFAULT_DOMAIN); + return Translator::translate($messageId, Translator::DEFAULT_DOMAIN, $context); } - function mt($domain, $messageId) + /** + * (non-PHPDoc) + * @see Translator::translate() For the function documentation. + */ + function mt($domain, $messageId, $context = null) { - return Translator::translate($messageId, $domain); + return Translator::translate($messageId, $domain, $context); } + + /** + * (non-PHPDoc) + * @see Translator::translatePlural() For the function documentation. + */ + function tp($messageId, $messageId2, $number, $context = null) + { + return Translator::translatePlural($messageId, $messageId2, $number, Translator::DEFAULT_DOMAIN, $context); + } + + /** + * (non-PHPDoc) + * @see Translator::translatePlural() For the function documentation. + */ + function mtp($domain, $messageId, $messageId2, $number, $context = null) + { + return Translator::translatePlural($messageId, $messageId2, $number, $domain, $context); + } + } else { - function t($messageId) + + /** + * (non-PHPDoc) + * @see Translator::translate() For the function documentation. + */ + function t($messageId, $context = null) { return $messageId; } - function mt($domain, $messageId) + /** + * (non-PHPDoc) + * @see Translator::translate() For the function documentation. + */ + function mt($domain, $messageId, $context = null) { return $messageId; } + + /** + * (non-PHPDoc) + * @see Translator::translatePlural() For the function documentation. + */ + function tp($messageId, $messageId2, $number, $context = null) + { + if ((int) $number !== 1) { + return $messageId2; + } + return $messageId; + } + + /** + * (non-PHPDoc) + * @see Translator::translatePlural() For the function documentation. + */ + function mtp($domain, $messageId, $messageId2, $number, $context = null) + { + if ((int) $number !== 1) { + return $messageId2; + } + return $messageId; + } + } diff --git a/library/Icinga/Authentication/AdmissionLoader.php b/library/Icinga/Authentication/AdmissionLoader.php index f961fb87c..098afda81 100644 --- a/library/Icinga/Authentication/AdmissionLoader.php +++ b/library/Icinga/Authentication/AdmissionLoader.php @@ -52,12 +52,8 @@ class AdmissionLoader return $permissions; } foreach ($config as $section) { - if ($this->match($section, $username, $groups)) { - foreach ($section as $key => $value) { - if (strpos($key, 'permission') === 0) { - $permissions = array_merge($permissions, String::trimSplit($value)); - } - } + if ($this->match($section, $username, $groups) && isset($section->permissions)) { + $permissions += String::trimSplit($section->permissions); } } return $permissions; @@ -79,12 +75,12 @@ class AdmissionLoader } catch (NotReadableError $e) { return $restrictions; } - foreach ($config as $section) { + foreach ($config as $name => $section) { if ($this->match($section, $username, $groups)) { if (!array_key_exists($section->name, $restrictions)) { $restrictions[$section->name] = array(); } - $restrictions[$section->name][] = $section->restriction; + $restrictions[$section->name][$name] = $section->restriction; } } return $restrictions; diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index 23a79781d..8ef6c89de 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -4,6 +4,7 @@ namespace Icinga\Authentication\Backend; +use Icinga\Logger\Logger; use Icinga\User; use Icinga\Authentication\UserBackend; use Icinga\Protocol\Ldap\Connection; @@ -23,11 +24,14 @@ class LdapUserBackend extends UserBackend protected $userNameAttribute; - public function __construct(Connection $conn, $userClass, $userNameAttribute) + protected $groupOptions; + + public function __construct(Connection $conn, $userClass, $userNameAttribute, $groupOptions = null) { $this->conn = $conn; $this->userClass = $userClass; $this->userNameAttribute = $userNameAttribute; + $this->groupOptions = $groupOptions; } /** @@ -83,6 +87,43 @@ class LdapUserBackend extends UserBackend } } + /** + * Retrieve the user groups + * + * @TODO: Subject to change, see #7343 + * + * @param string $dn + * + * @return array|null + */ + public function getGroups($dn) + { + if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) { + return null; + } + + $q = $this->conn->select() + ->setBase($this->groupOptions['group_base_dn']) + ->from( + $this->groupOptions['group_class'], + array($this->groupOptions['group_attribute']) + ) + ->where( + $this->groupOptions['group_member_attribute'], + $dn + ); + + $result = $this->conn->fetchAll($q); + + $groups = array(); + + foreach ($result as $group) { + $groups[] = $group->{$this->groupOptions['group_attribute']}; + } + + return $groups; + } + /** * Test whether the given user exists * @@ -127,10 +168,15 @@ class LdapUserBackend extends UserBackend return false; } try { - return $this->conn->testCredentials( - $this->conn->fetchDN($this->createQuery($user->getUsername())), + $userDn = $this->conn->fetchDN($this->createQuery($user->getUsername())); + $authenticated = $this->conn->testCredentials( + $userDn, $password ); + if ($authenticated) { + $user->setGroups($this->getGroups($userDn)); + } + return $authenticated; } catch (LdapException $e) { // Error during authentication of this specific user throw new AuthenticationException( @@ -160,4 +206,3 @@ class LdapUserBackend extends UserBackend ); } } - diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index 75cf81481..efe9b5815 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -113,30 +113,32 @@ class Manager } /** - * Tries to authenticate the user with the current session + * Try to authenticate the user with the current session + * + * Authentication for externally-authenticated users will be revoked if the username changed or external + * authentication is no longer in effect */ public function authenticateFromSession() { $this->user = Session::getSession()->get('user'); - if ($this->user !== null && $this->user->isRemoteUser() === true) { list($originUsername, $field) = $this->user->getRemoteUserInformation(); - if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) { + if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) { $this->removeAuthorization(); } } } /** - * Returns true when the user is currently authenticated + * Whether the user is authenticated * - * @param Boolean $ignoreSession Set to true to prevent authentication by session + * @param bool $ignoreSession True to prevent session authentication * * @return bool */ public function isAuthenticated($ignoreSession = false) { - if ($this->user === null && !$ignoreSession) { + if ($this->user === null && ! $ignoreSession) { $this->authenticateFromSession(); } return is_object($this->user); @@ -145,25 +147,16 @@ class Manager /** * Whether an authenticated user has a given permission * - * This is true if the user owns this permission, false if not. - * Also false if there is no authenticated user - * - * TODO: I'd like to see wildcard support, e.g. module/* - * * @param string $permission Permission name - * @return bool + * + * @return bool True if the user owns the given permission, false if not or if not authenticated */ public function hasPermission($permission) { if (! $this->isAuthenticated()) { return false; } - foreach ($this->user->getPermissions() as $p) { - if ($p === $permission) { - return true; - } - } - return false; + return $this->user->can($permission); } /** diff --git a/library/Icinga/Authentication/UserBackend.php b/library/Icinga/Authentication/UserBackend.php index 9e7abd62a..7829210fd 100644 --- a/library/Icinga/Authentication/UserBackend.php +++ b/library/Icinga/Authentication/UserBackend.php @@ -93,26 +93,44 @@ abstract class UserBackend implements Countable $backend = new DbUserBackend($resource); break; case 'msldap': + $groupOptions = array( + 'group_base_dn' => $backendConfig->group_base_dn, + 'group_attribute' => $backendConfig->group_attribute, + 'group_member_attribute' => $backendConfig->group_member_attribute, + 'group_class' => $backendConfig->group_class + ); $backend = new LdapUserBackend( $resource, $backendConfig->get('user_class', 'user'), - $backendConfig->get('user_name_attribute', 'sAMAccountName') + $backendConfig->get('user_name_attribute', 'sAMAccountName'), + $groupOptions ); break; case 'ldap': - if (($userClass = $backendConfig->user_class) === null) { + if ($backendConfig->user_class === null) { throw new ConfigurationError( 'Authentication configuration for backend "%s" is missing the user_class directive', $name ); } - if (($userNameAttribute = $backendConfig->user_name_attribute) === null) { + if ($backendConfig->user_name_attribute === null) { throw new ConfigurationError( 'Authentication configuration for backend "%s" is missing the user_name_attribute directive', $name ); } - $backend = new LdapUserBackend($resource, $userClass, $userNameAttribute); + $groupOptions = array( + 'group_base_dn' => $backendConfig->group_base_dn, + 'group_attribute' => $backendConfig->group_attribute, + 'group_member_attribute' => $backendConfig->group_member_attribute, + 'group_class' => $backendConfig->group_class + ); + $backend = new LdapUserBackend( + $resource, + $backendConfig->user_class, + $backendConfig->user_name_attribute, + $groupOptions + ); break; default: throw new ConfigurationError( diff --git a/library/Icinga/Cli/Params.php b/library/Icinga/Cli/Params.php index 7236dcca2..5c6cbfe8b 100644 --- a/library/Icinga/Cli/Params.php +++ b/library/Icinga/Cli/Params.php @@ -108,6 +108,18 @@ class Params return $this->standalone; } + /** + * Support isset() and empty() checks on options + * + * @param $name + * + * @return bool + */ + public function __isset($name) + { + return isset($this->params[$name]); + } + /** * @see Params::get() */ diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index a89544be8..94765d98f 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -59,6 +59,13 @@ class DbQuery extends SimpleQuery */ protected $count; + /** + * GROUP BY clauses + * + * @var string|array + */ + protected $group; + protected function init() { $this->db = $this->ds->getDbAdapter(); @@ -89,17 +96,21 @@ class DbQuery extends SimpleQuery public function getSelectQuery() { $select = $this->dbSelect(); - // Add order fields to select for postgres distinct queries (#6351) if ($this->hasOrder() && $this->getDatasource()->getDbType() === 'pgsql' && $select->getPart(Zend_Db_Select::DISTINCT) === true) { foreach ($this->getOrder() as $fieldAndDirection) { - list($alias, $field) = explode('.', $fieldAndDirection[0]); - $this->columns[$field] = $fieldAndDirection[0]; + if (array_search($fieldAndDirection[0], $this->columns) === false) { + $this->columns[] = $fieldAndDirection[0]; + } } } + if ($this->group) { + $select->group($this->group); + } + $select->columns($this->columns); $this->applyFilterSql($select); @@ -117,7 +128,7 @@ class DbQuery extends SimpleQuery return $select; } - protected function applyFilterSql($query) + public function applyFilterSql($query) { $where = $this->renderFilter($this->filter); if ($where !== '') { @@ -223,7 +234,7 @@ class DbQuery extends SimpleQuery */ public function isTimestamp($field) { - return $this; + return false; } public function whereToSql($col, $sign, $expression) @@ -253,6 +264,7 @@ class DbQuery extends SimpleQuery $this->applyFilterSql($count); if ($this->useSubqueryCount) { + $count->columns($this->columns); $columns = array('cnt' => 'COUNT(*)'); return $this->db->select()->from($count, $columns); } @@ -300,4 +312,17 @@ class DbQuery extends SimpleQuery { return (string) $this->getSelectQuery(); } + + /** + * Add a GROUP BY clause + * + * @param string|array $group + * + * @return $this + */ + public function group($group) + { + $this->group = $group; + return $this; + } } diff --git a/library/Icinga/Data/Filter/FilterExpression.php b/library/Icinga/Data/Filter/FilterExpression.php index c29414166..8378b5ae3 100644 --- a/library/Icinga/Data/Filter/FilterExpression.php +++ b/library/Icinga/Data/Filter/FilterExpression.php @@ -12,6 +12,8 @@ class FilterExpression extends Filter public function __construct($column, $sign, $expression) { + $column = trim($column); + $expression = is_array($expression) ? array_map('trim', $expression) : trim($expression); $this->column = $column; $this->sign = $sign; $this->expression = $expression; diff --git a/library/Icinga/Data/ResourceFactory.php b/library/Icinga/Data/ResourceFactory.php index 30597ad2d..e85413615 100644 --- a/library/Icinga/Data/ResourceFactory.php +++ b/library/Icinga/Data/ResourceFactory.php @@ -10,7 +10,6 @@ use Icinga\Util\ConfigAwareFactory; use Icinga\Exception\ConfigurationError; 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; use Icinga\Protocol\File\FileReader; @@ -102,7 +101,7 @@ class ResourceFactory implements ConfigAwareFactory * * @param Zend_Config $config The configuration for the created resource. * - * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader An objects that can be used to access + * @return DbConnection|LdapConnection|LivestatusConnection An object that can be used to access * the given resource. The returned class depends on the configuration property 'type'. * @throws ConfigurationError When an unsupported type is given */ @@ -115,9 +114,6 @@ class ResourceFactory implements ConfigAwareFactory case 'ldap': $resource = new LdapConnection($config); break; - case 'statusdat': - $resource = new StatusdatReader($config); - break; case 'livestatus': $resource = new LivestatusConnection($config->socket); break; @@ -137,7 +133,7 @@ class ResourceFactory implements ConfigAwareFactory * Create a resource from name * * @param string $resourceName - * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader + * @return DbConnection|LdapConnection|LivestatusConnection */ public static function create($resourceName) { diff --git a/library/Icinga/Exception/InvalidPropertyException.php b/library/Icinga/Exception/InvalidPropertyException.php new file mode 100644 index 000000000..f79055f0b --- /dev/null +++ b/library/Icinga/Exception/InvalidPropertyException.php @@ -0,0 +1,10 @@ + 'DEBUG', + Logger::INFO => 'INFO', + Logger::WARNING => 'WARNING', + Logger::ERROR => 'ERROR' + ); + /** * This logger's instance * - * @var Logger + * @var static */ protected static $instance; /** - * The log writer to use + * Log writer * * @var \Icinga\Logger\LogWriter */ protected $writer; /** - * The configured type - * - * @string Type (syslog, file) - */ - protected $type = 'none'; - - /** - * The maximum severity to emit + * Maximum level to emit * * @var int */ - protected $verbosity; - - /** - * The supported severities - */ - public static $ERROR = 0; - public static $WARNING = 1; - public static $INFO = 2; - public static $DEBUG = 3; + protected $level; /** * Create a new logger object * - * @param Zend_Config $config + * @param Zend_Config $config + * + * @throws ConfigurationError If the logging configuration directive 'log' is missing or if the logging level is + * not defined */ public function __construct(Zend_Config $config) { - $this->verbosity = $config->level; + if ($config->log === null) { + throw new ConfigurationError('Required logging configuration directive \'log\' missing'); + } - if ($config->enable) { + if (($level = $config->level) !== null) { + if (is_numeric($level)) { + if (! isset(static::$levels[(int) $level])) { + throw new ConfigurationError( + 'Can\'t set logging level %d. Logging level is not defined. Use one of %s or one of the' + . ' Logger\'s constants.', + $level, + implode(', ', array_keys(static::$levels)) + ); + } + $this->level = static::$levels[(int) $level]; + } else { + $level = strtoupper($level); + $levels = array_flip(static::$levels); + if (! isset($levels[$level])) { + throw new ConfigurationError( + 'Can\'t set logging level "%s". Logging level is not defined. Use one of %s.', + $level, + implode(', ', array_keys($levels)) + ); + } + $this->level = $levels[$level]; + } + } else { + $this->level = static::ERROR; + } + + if (strtolower($config->get('log', 'syslog')) !== 'none') { $this->writer = $this->createWriter($config); } } @@ -67,14 +118,17 @@ class Logger * Create a new logger object * * @param Zend_Config $config + * + * @return static */ public static function create(Zend_Config $config) { static::$instance = new static($config); + return static::$instance; } /** - * Return a log writer + * Create a log writer * * @param Zend_Config $config The configuration to initialize the writer with * @@ -83,28 +137,26 @@ class Logger */ protected function createWriter(Zend_Config $config) { - $class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->type)) . 'Writer'; - if (!class_exists($class)) { + $class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->log)) . 'Writer'; + if (! class_exists($class)) { throw new ConfigurationError( 'Cannot find log writer of type "%s"', - $config->type + $config->log ); } - $this->type = $config->type; - return new $class($config); } /** - * Write a message to the log + * Log a message * - * @param string $message The message to write - * @param int $severity The severity to use + * @param int $level The logging level + * @param string $message The log message */ - public function log($message, $severity) + public function log($level, $message) { - if ($this->writer !== null && $this->verbosity >= $severity) { - $this->writer->log($severity, $message); + if ($this->writer !== null && $this->level >= $level) { + $this->writer->log($level, $message); } } @@ -162,7 +214,7 @@ class Logger public static function error() { if (static::$instance !== null && func_num_args() > 0) { - static::$instance->log(static::formatMessage(func_get_args()), static::$ERROR); + static::$instance->log(static::ERROR, static::formatMessage(func_get_args())); } } @@ -174,7 +226,7 @@ class Logger public static function warning() { if (static::$instance !== null && func_num_args() > 0) { - static::$instance->log(static::formatMessage(func_get_args()), static::$WARNING); + static::$instance->log(static::WARNING, static::formatMessage(func_get_args())); } } @@ -186,7 +238,7 @@ class Logger public static function info() { if (static::$instance !== null && func_num_args() > 0) { - static::$instance->log(static::formatMessage(func_get_args()), static::$INFO); + static::$instance->log(static::INFO, static::formatMessage(func_get_args())); } } @@ -198,7 +250,7 @@ class Logger public static function debug() { if (static::$instance !== null && func_num_args() > 0) { - static::$instance->log(static::formatMessage(func_get_args()), static::$DEBUG); + static::$instance->log(static::DEBUG, static::formatMessage(func_get_args())); } } @@ -212,20 +264,30 @@ class Logger return $this->writer; } + /** + * Is the logger writing to Syslog? + * + * @return bool + */ public static function writesToSyslog() { - return static::$instance && static::$instance->type === 'syslog'; + return static::$instance && static::$instance instanceof SyslogWriter; } + /** + * Is the logger writing to a file? + * + * @return bool + */ public static function writesToFile() { - return static::$instance && static::$instance->type === 'file'; + return static::$instance && static::$instance instanceof FileWriter; } /** * Get this' instance * - * @return Logger + * @return static */ public static function getInstance() { diff --git a/library/Icinga/Logger/Writer/FileWriter.php b/library/Icinga/Logger/Writer/FileWriter.php index 4ab04d85b..527358e1c 100644 --- a/library/Icinga/Logger/Writer/FileWriter.php +++ b/library/Icinga/Logger/Writer/FileWriter.php @@ -5,38 +5,43 @@ namespace Icinga\Logger\Writer; use Exception; -use Icinga\Exception\IcingaException; use Zend_Config; -use Icinga\Util\File; +use Icinga\Exception\ConfigurationError; use Icinga\Logger\Logger; use Icinga\Logger\LogWriter; -use Icinga\Exception\ConfigurationError; +use Icinga\Util\File; /** - * Class to write log messages to a file + * Log to a file */ class FileWriter extends LogWriter { /** - * The path to the file + * Path to the file * * @var string */ - protected $path; + protected $file; /** - * Create a new log writer initialized with the given configuration + * Create a new file log writer * - * @throws ConfigurationError In case the log path does not exist + * @param Zend_Config $config + * + * @throws ConfigurationError If the configuration directive 'file' is missing or if the path to 'file' does + * not exist or if writing to 'file' is not possible */ public function __construct(Zend_Config $config) { - $this->path = $config->target; + if ($config->file === null) { + throw new ConfigurationError('Required logging configuration directive \'file\' missing'); + } + $this->file = $config->file; - if (substr($this->path, 0, 6) !== 'php://' && false === file_exists(dirname($this->path))) { + if (substr($this->file, 0, 6) !== 'php://' && ! file_exists(dirname($this->file))) { throw new ConfigurationError( 'Log path "%s" does not exist', - dirname($this->path) + dirname($this->file) ); } @@ -45,70 +50,32 @@ class FileWriter extends LogWriter } catch (Exception $e) { throw new ConfigurationError( 'Cannot write to log file "%s" (%s)', - $this->path, + $this->file, $e->getMessage() ); } } /** - * Log a message with the given severity + * Log a message * - * @param int $severity The severity to use - * @param string $message The message to log + * @param int $level The logging level + * @param string $message The log message */ - public function log($severity, $message) + public function log($level, $message) { - $this->write(date('c') . ' ' . $this->getSeverityString($severity) . ' ' . $message . PHP_EOL); + $this->write(date('c') . ' - ' . Logger::$levels[$level] . ' - ' . $message . PHP_EOL); } /** - * Return a string representation for the given severity + * Write a message to the log * - * @param string $severity The severity to use - * - * @return string The string representation of the severity - * - * @throws IcingaException In case the given severity is unknown + * @param string $message */ - protected function getSeverityString($severity) + protected function write($message) { - switch ($severity) { - case Logger::$ERROR: - return '- ERROR -'; - case Logger::$WARNING: - return '- WARNING -'; - case Logger::$INFO: - return '- INFO -'; - case Logger::$DEBUG: - return '- DEBUG -'; - default: - throw new IcingaException( - 'Unknown severity "%s"', - $severity - ); - } - } - - /** - * Write a message to the path - * - * @param string $text The message to write - * - * @throws Exception In case write acess to the path failed - */ - protected function write($text) - { - $file = new File($this->path, 'a'); - $file->fwrite($text); + $file = new File($this->file, 'a'); + $file->fwrite($message); $file->fflush(); } - - /** - * @return string - */ - public function getPath() - { - return $this->path; - } } diff --git a/library/Icinga/Logger/Writer/SyslogWriter.php b/library/Icinga/Logger/Writer/SyslogWriter.php index d176e1cbb..f67c618d2 100644 --- a/library/Icinga/Logger/Writer/SyslogWriter.php +++ b/library/Icinga/Logger/Writer/SyslogWriter.php @@ -4,27 +4,24 @@ namespace Icinga\Logger\Writer; -use Exception; use Zend_Config; use Icinga\Logger\Logger; use Icinga\Logger\LogWriter; -use Icinga\Exception\ConfigurationError; -use Icinga\Exception\IcingaException; /** - * Class to write messages to syslog + * Log to the syslog service */ class SyslogWriter extends LogWriter { /** - * The facility where to write messages to + * Syslog facility * - * @var string + * @var int */ protected $facility; /** - * The prefix to prepend to each message + * Prefix to prepend to each message * * @var string */ @@ -35,79 +32,42 @@ class SyslogWriter extends LogWriter * * @var array */ - protected $facilities = array( - 'LOG_USER' => LOG_USER + public static $facilities = array( + 'user' => LOG_USER ); /** - * Create a new log writer initialized with the given configuration + * Log level to syslog severity map + * + * @var array + */ + public static $severityMap = array( + Logger::ERROR => LOG_ERR, + Logger::WARNING => LOG_WARNING, + Logger::INFO => LOG_INFO, + Logger::DEBUG => LOG_DEBUG + ); + + /** + * Create a new syslog log writer + * + * @param Zend_Config $config */ public function __construct(Zend_Config $config) { - if (!array_key_exists($config->facility, $this->facilities)) { - throw new ConfigurationError( - 'Cannot create syslog writer with unknown facility "%s"', - $config->facility - ); - } - - $this->ident = $config->application; - $this->facility = $this->facilities[$config->facility]; + $this->ident = $config->get('application', 'icingaweb'); + $this->facility = static::$facilities['user']; } /** - * Log a message with the given severity + * Log a message * - * @param int $severity The severity to use - * @param string $message The message to log - * - * @throws Exception In case the given severity cannot be mapped to a valid syslog priority + * @param int $level The logging level + * @param string $message The log message */ - public function log($severity, $message) + public function log($level, $message) { - $priorities = array( - Logger::$ERROR => LOG_ERR, - Logger::$WARNING => LOG_WARNING, - Logger::$INFO => LOG_INFO, - Logger::$DEBUG => LOG_DEBUG - ); - - if (!array_key_exists($severity, $priorities)) { - throw new IcingaException( - 'Severity "%s" cannot be mapped to a valid syslog priority', - $severity - ); - } - - $this->open(); - $this->write($priorities[$severity], $message); - $this->close(); - } - - /** - * Open a new syslog connection - */ - protected function open() - { - openlog($this->ident, 0, $this->facility); - } - - /** - * Write a message to the syslog connection - * - * @param int $priority The priority to use - * @param string $message The message to write - */ - protected function write($priority, $message) - { - syslog($priority, $message); - } - - /** - * Close the syslog connection - */ - protected function close() - { - closelog(); + openlog($this->ident, LOG_PID, $this->facility); + syslog(static::$severityMap[$level], $message); } } diff --git a/library/Icinga/Protocol/Commandpipe/Command.php b/library/Icinga/Protocol/Commandpipe/Command.php deleted file mode 100644 index e89169f35..000000000 --- a/library/Icinga/Protocol/Commandpipe/Command.php +++ /dev/null @@ -1,177 +0,0 @@ -withoutHosts = (bool) $state; - return $this; - } - - /** - * Set whether this command should only affect the hosts of a host- or servicegroup - * - * @param bool $state - * @return self - */ - public function excludeServices($state = true) - { - $this->withoutServices = (bool) $state; - return $this; - } - - /** - * Set whether this command should also affect all children hosts of a host - * - * @param bool $state - * @return self - */ - public function includeChildren($state = true) - { - $this->withChildren = (bool) $state; - return $this; - } - - /** - * Set whether this command only affects the services associated with a particular host - * - * @param bool $state - * @return self - */ - public function excludeHost($state = true) - { - $this->onlyServices = (bool) $state; - return $this; - } - - /** - * Getter for flag whether a command is global - * @return bool - */ - public function provideGlobalCommand() - { - return (bool) $this->globalCommand; - } - - /** - * Return this command's arguments in the order expected by the actual command definition - * - * @return array - */ - abstract public function getArguments(); - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - */ - abstract public function getHostCommand($hostname); - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - */ - abstract public function getServiceCommand($hostname, $servicename); - - /** - * Return the command as a string with the given hostgroup being inserted - * - * @param string $hostgroup The name of the hostgroup to insert - * - * @return string The string representation of the command - */ - public function getHostgroupCommand($hostgroup) - { - throw new ProgrammingError( - '%s does not provide a hostgroup command', - get_class($this) - ); - } - - /** - * Return the command as a string with the given servicegroup being inserted - * - * @param string $servicegroup The name of the servicegroup to insert - * - * @return string The string representation of the command - */ - public function getServicegroupCommand($servicegroup) - { - throw new ProgrammingError( - '%s does not provide a servicegroup command', - get_class($this) - ); - } - - /** - * Return the command as a string for the whole instance - * - * @param string $instance - * - * @return string - * @throws ProgrammingError - */ - public function getGlobalCommand($instance = null) - { - throw new ProgrammingError( - '%s does not provide a global command', - getclass($this) - ); - } -} diff --git a/library/Icinga/Protocol/Commandpipe/CommandPipe.php b/library/Icinga/Protocol/Commandpipe/CommandPipe.php deleted file mode 100644 index 8d149dd20..000000000 --- a/library/Icinga/Protocol/Commandpipe/CommandPipe.php +++ /dev/null @@ -1,603 +0,0 @@ -getTransportForConfiguration($config); - $this->name = $config->name; - } - - /** - * Setup the @see Icinga\Protocol\Commandpipe\Transport.php class that will be used for accessing the command pipe - * - * Currently this method uses SecureShell when a host is given, otherwise it assumes the pipe is accessible - * via the machines filesystem - * - * @param \Zend_Config $config The configuration as defined in the instances.ini - */ - private function getTransportForConfiguration(\Zend_Config $config) - { - if (isset($config->host)) { - $this->transport = new SecureShell(); - $this->transport->setEndpoint($config); - } else { - $this->transport = new LocalPipe(); - $this->transport->setEndpoint($config); - } - } - - /** - * Send the command string $command to the icinga pipe - * - * This method just delegates the send command to the underlying transport - * - * @param String $command The command string to send, without the timestamp - */ - public function send($command) - { - $this->transport->send($this->escape($command)); - } - - /** - * Return the given command string with escaped newlines - * - * @param string $command The command string to escape - * - * @return string The escaped command string - */ - public function escape($command) - { - return str_replace(array("\r", "\n"), array('\r', '\n'), $command); - } - - /** - * Send a command to the icinga pipe - * - * @param Command $command - * @param array $objects - */ - public function sendCommand(Command $command, array $objects = array()) - { - if ($command->provideGlobalCommand() === true) { - $this->send($command->getGlobalCommand()); - } else { - foreach ($objects as $object) { - $objectType = $this->getObjectType($object); - if ($objectType === self::TYPE_SERVICE) { - $this->send($command->getServiceCommand($object->host_name, $object->service_description)); - } else { - $this->send($command->getHostCommand($object->host_name)); - } - } - } - } - - /** - * Remove the acknowledgements of the provided objects - * - * @param array $objects An array of mixed service and host objects whose acknowledgments will be removed - */ - public function removeAcknowledge($objects) - { - foreach ($objects as $object) { - if (isset($object->service_description)) { - $this->send("REMOVE_SVC_ACKNOWLEDGEMENT;$object->host_name;$object->service_description"); - } else { - $this->send("REMOVE_HOST_ACKNOWLEDGEMENT;$object->host_name"); - } - } - } - - /** - * Removes the submitted comments - * - * @param array $objectsOrComments An array of hosts and services (to remove all their comments) - * or single comment objects to remove - */ - public function removeComment($objectsOrComments) - { - foreach ($objectsOrComments as $object) { - if (isset($object->comment_id)) { - if (isset($object->service_description)) { - $type = "SERVICE_COMMENT"; - } else { - $type = "HOST_COMMENT"; - } - $this->send("DEL_{$type};" . intval($object->comment_id)); - } else { - if (isset($object->service_description)) { - $type = "SERVICE_COMMENT"; - } else { - $type = "HOST_COMMENT"; - } - $cmd = "DEL_ALL_{$type}S;" . $object->host_name; - if ($type == "SERVICE_COMMENT") { - $cmd .= ";" . $object->service_description; - } - $this->send($cmd); - } - } - } - - /** - * Globally enable notifications for this instance - * - */ - public function enableGlobalNotifications() - { - $this->send("ENABLE_NOTIFICATIONS"); - } - - /** - * Globally disable notifications for this instance - * - */ - public function disableGlobalNotifications() - { - $this->send("DISABLE_NOTIFICATIONS"); - } - - /** - * Return the object type of the provided object (TYPE_SERVICE or TYPE_HOST) - * - * @param $object The object to identify - * @return string TYPE_SERVICE or TYPE_HOST - */ - private function getObjectType($object) - { - //@TODO: This must be refactored once more commands are supported - if (isset($object->service_description)) { - return self::TYPE_SERVICE; - } - return self::TYPE_HOST; - } - - /** - * Remove downtimes for objects - * - * @param array $objects An array containing hosts, service or downtime objects - * @param int $starttime An optional starttime to use for the DEL_DOWNTIME_BY_HOST_NAME command - */ - public function removeDowntime($objects, $starttime = 0) - { - foreach ($objects as $object) { - $type = $this->getObjectType($object); - if (isset($object->downtime_id)) { - $this->send("DEL_" . $type . "_DOWNTIME;" . $object->downtime_id); - continue; - } - $cmd = "DEL_DOWNTIME_BY_HOST_NAME;" . $object->host_name; - if ($type == self::TYPE_SERVICE) { - $cmd .= ";" . $object->service_description; - } - if ($starttime != 0) { - $cmd .= ";" . $starttime; - } - $this->send($cmd); - } - } - - /** - * Restart the icinga instance - * - */ - public function restartIcinga() - { - $this->send("RESTART_PROCESS"); - } - - /** - * Modify monitoring flags for the provided objects - * - * @param array $objects An arry of service and/or host objects to modify - * @param PropertyModifier $flags The Monitoring attributes to modify - */ - public function setMonitoringProperties($objects, PropertyModifier $flags) - { - foreach ($objects as $object) { - $type = $this->getObjectType($object); - $formatArray = $flags->getFormatString($type); - foreach ($formatArray as $format) { - $format .= ";" - . $object->host_name - . ($type == self::TYPE_SERVICE ? ";" . $object->service_description : ""); - $this->send($format); - } - } - } - - /** - * Enable active checks for all provided objects - * - * @param array $objects An array containing services and hosts to enable active checks for - */ - public function enableActiveChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::ACTIVE => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable active checks for all provided objects - * - * @param array $objects An array containing services and hosts to disable active checks - */ - public function disableActiveChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::ACTIVE => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable passive checks for all provided objects - * - * @param array $objects An array containing services and hosts to enable passive checks for - */ - public function enablePassiveChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::PASSIVE => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Enable passive checks for all provided objects - * - * @param array $objects An array containing services and hosts to enable passive checks for - */ - public function disablePassiveChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::PASSIVE => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable flap detection for all provided objects - * - * @param array $objects An array containing services and hosts to enable flap detection - * - */ - public function enableFlappingDetection($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::FLAPPING => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable flap detection for all provided objects - * - * @param array $objects An array containing services and hosts to disable flap detection - * - */ - public function disableFlappingDetection($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::FLAPPING => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable notifications for all provided objects - * - * @param array $objects An array containing services and hosts to enable notification - * - */ - public function enableNotifications($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable flap detection for all provided objects - * - * @param array $objects An array containing services and hosts to disable notifications - * - */ - public function disableNotifications($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable freshness checks for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function enableFreshnessChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::FRESHNESS => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable freshness checks for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function disableFreshnessChecks($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::FRESHNESS => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable event handler for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function enableEventHandler($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable event handler for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function disableEventHandler($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Enable performance data parsing for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function enablePerfdata($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::PERFDATA => PropertyModifier::STATE_ENABLE - ) - ) - ); - } - - /** - * Disable performance data parsing for all provided objects - * - * @param array $objects An array of hosts and/or services - */ - public function disablePerfdata($objects) - { - $this->setMonitoringProperties( - $objects, - new PropertyModifier( - array( - PropertyModifier::PERFDATA => PropertyModifier::STATE_DISABLE - ) - ) - ); - } - - /** - * Disable notifications for all services of the provided hosts - * - * @param array $objects An array of hosts - */ - public function disableNotificationsForServices($objects) - { - foreach ($objects as $host) { - $msg = 'DISABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name; - $this->send($msg); - } - } - - /** - * Enable notifications for all services of the provided hosts - * - * @param array $objects An array of hosts - */ - public function enableNotificationsForServices($objects) - { - foreach ($objects as $host) { - $msg = 'ENABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name; - $this->send($msg); - } - } - - /** - * Disable active checks for all services of the provided hosts - * - * @param array $objects An array of hosts - */ - public function disableActiveChecksWithChildren($objects) - { - foreach ($objects as $host) { - $msg = 'DISABLE_HOST_SVC_CHECKS;'.$host->host_name; - $this->send($msg); - } - } - - /** - * Enable active checks for all services of the provided hosts - * - * @param array $objects An array of hosts - */ - public function enableActiveChecksWithChildren($objects) - { - foreach ($objects as $host) { - $msg = 'ENABLE_HOST_SVC_CHECKS;'.$host->host_name; - $this->send($msg); - } - } - - /** - * Reset modified attributes for all provided objects - * - * @param array $objects An array of hosts and services - */ - public function resetAttributes($objects) - { - foreach ($objects as $object) { - $type = $this->getObjectType($object); - if ($type === self::TYPE_SERVICE) { - $this->send('CHANGE_SVC_MODATTR;'.$object->host_name.';'.$object->service_description.';0'); - } else { - $this->send('CHANGE_HOST_MODATTR;'.$object->host_name.';0'); - } - } - } - - /** - * Return the transport handler that handles actual sending of commands - * - * @return Transport - */ - public function getTransport() - { - return $this->transport; - } -} diff --git a/library/Icinga/Protocol/Commandpipe/Comment.php b/library/Icinga/Protocol/Commandpipe/Comment.php deleted file mode 100644 index 2b8caf52b..000000000 --- a/library/Icinga/Protocol/Commandpipe/Comment.php +++ /dev/null @@ -1,61 +0,0 @@ -author = $author; - $this->content = $content; - $this->persistent = $persistent; - } - - /** - * Return this comment's properties as list of command parameters - * - * @param bool $ignorePersistentFlag Whether the persistent flag should be included or not - * @return array - */ - public function getArguments($ignorePersistentFlag = false) - { - if ($ignorePersistentFlag) { - return array($this->author, $this->content); - } else { - return array($this->persistent ? '1' : '0', $this->author, $this->content); - } - } -} diff --git a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php b/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php deleted file mode 100644 index 662a156e9..000000000 --- a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php +++ /dev/null @@ -1,12 +0,0 @@ - self::STATE_KEEP, - self::ACTIVE => self::STATE_KEEP, - self::PASSIVE => self::STATE_KEEP, - self::NOTIFICATIONS => self::STATE_KEEP, - self::FRESHNESS => self::STATE_KEEP, - self::EVENTHANDLER => self::STATE_KEEP - ); - - /** - * Create a new PropertyModified object using the given flags - * - * @param array $flags Flags to enable/disable/keep different monitoring attributes - */ - public function __construct(array $flags) - { - foreach ($flags as $type => $value) { - if (isset($this->flags[$type])) { - $this->flags[$type] = $value; - } - } - } - - /** - * Return this object as a template for the given object type - * - * @param $type Either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE - * @return array An array of external command templates for the given type representing the containers state - */ - public function getFormatString($type) - { - $cmd = array(); - foreach ($this->flags as $cmdTemplate => $setting) { - if ($setting == self::STATE_KEEP) { - continue; - } - $commandString = ($setting == self::STATE_ENABLE ? "ENABLE_" : "DISABLE_"); - $targetString = $type; - if ($type == CommandPipe::TYPE_SERVICE && $cmdTemplate == self::FRESHNESS) { - // the external command definition is inconsistent here.. - $targetString = "SERVICE"; - } - $commandString .= sprintf($cmdTemplate, $targetString); - $cmd[] = $commandString; - } - return $cmd; - } -} diff --git a/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php b/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php deleted file mode 100644 index 32f745565..000000000 --- a/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php +++ /dev/null @@ -1,70 +0,0 @@ -path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd'; - } - - /** - * @see Transport::send() - */ - public function send($message) - { - Logger::debug('Attempting to send external icinga command %s to local command file ', $message, $this->path); - - try { - $file = new File($this->path, $this->openMode); - $file->fwrite('[' . time() . '] ' . $message . PHP_EOL); - $file->fflush(); - } catch (Exception $e) { - throw new ConfigurationError( - 'Could not open icinga command pipe at "%s" (%s)', - $this->path, - $e->getMessage() - ); - } - - Logger::debug('Command sent: [' . time() . '] ' . $message . PHP_EOL); - } - - /** - * Overwrite the open mode (useful for testing) - * - * @param string $mode The mode to use to access the pipe - */ - public function setOpenMode($mode) - { - $this->openMode = $mode; - } -} diff --git a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php deleted file mode 100644 index f1505c2ef..000000000 --- a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php +++ /dev/null @@ -1,102 +0,0 @@ -host = isset($config->host) ? $config->host : 'localhost'; - $this->port = isset($config->port) ? $config->port : 22; - $this->user = isset($config->user) ? $config->user : null; - $this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd'; - } - - /** - * Write the given external command to the command pipe - * - * @param string $command - * - * @throws RuntimeException When the command could not be sent to the remote Icinga host - * @see Transport::send() - */ - public function send($command) - { - $retCode = 0; - $output = array(); - Logger::debug( - 'Icinga instance is on different host, attempting to send command %s via ssh to %s:%s/%s', - $command, - $this->host, - $this->port, - $this->path - ); - $hostConnector = $this->user ? $this->user . "@" . $this->host : $this->host; - $command = escapeshellarg('['. time() .'] ' . $command); - $sshCommand = sprintf( - 'ssh -o BatchMode=yes -o KbdInteractiveAuthentication=no %s -p %d' - . ' "echo %s > %s" 2>&1', - $hostConnector, - $this->port, - $command, - $this->path - ); - - exec($sshCommand, $output, $retCode); - Logger::debug("Command '%s' exited with %d: %s", $sshCommand, $retCode, $output); - - if ($retCode != 0) { - $msg = 'Could not send command to remote Icinga host: ' - . implode(PHP_EOL, $output) - . " (returncode $retCode)"; - Logger::error($msg); - throw new RuntimeException($msg); - } - } -} diff --git a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php deleted file mode 100644 index 59119cf13..000000000 --- a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php +++ /dev/null @@ -1,27 +0,0 @@ -filename, $this->fields) - ); + return new FileIterator($this->filename, $this->fields); } /** diff --git a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php b/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php deleted file mode 100644 index 0947e65bc..000000000 --- a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php +++ /dev/null @@ -1,13 +0,0 @@ -ref = & $obj; - $this->reader = & $reader; - } - - /** - * @param $attribute - * @return \stdClass - */ - public function __get($attribute) - { - $exploded = explode(".", $attribute); - $result = $this->ref; - - foreach ($exploded as $elem) { - if (isset($result->$elem)) { - $result = $result->$elem; - } else { - return null; - } - } - return $result; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Parser.php b/library/Icinga/Protocol/Statusdat/Parser.php deleted file mode 100644 index 5b3694518..000000000 --- a/library/Icinga/Protocol/Statusdat/Parser.php +++ /dev/null @@ -1,422 +0,0 @@ -file = $file; - $this->icingaState = $baseState; - } - - /** - * Parse the given file handle as an objects file and read object information - */ - public function parseObjectsFile() - { - $DEFINE = strlen('define '); - $this->icingaState = array(); - foreach ($this->file as $line) { - $line = trim($line); - $this->lineCtr++; - if ($line === '' || $line[0] === '#') { - continue; - } - $this->currentObjectType = trim(substr($line, $DEFINE, -1)); - if (!isset($this->icingaState[$this->currentObjectType])) { - $this->icingaState[$this->currentObjectType] = array(); - } - $this->readCurrentObject(); - } - $this->processDeferred(); - } - - /** - * Parse the given file as an status.dat file and read runtime information - * - * @param File $file The file to parse or null to parse the one passed to the constructor - */ - public function parseRuntimeState(File $file = null) - { - if ($file != null) { - $this->file = $file; - } else { - $file = $this->file; - } - - if (!$this->icingaState) { - throw new ProgrammingError('Tried to read runtime state without existing objects data'); - } - $this->overwrites = array(); - foreach ($file as $line) { - $line = trim($line); - $this->lineCtr++; - if ($line === '' || $line[0] === '#') { - continue; - } - $this->currentStateType = trim(substr($line, 0, -1)); - $this->readCurrentState(); - } - } - - /** - * Read the next object from the object.cache file handle - * - * @throws ParsingException - */ - private function readCurrentObject() - { - $monitoringObject = new PrintableObject(); - foreach ($this->file as $line) { - $line = explode("\t", trim($line), 2); - $this->lineCtr++; - if (!$line) { - continue; - } - - // End of object - if ($line[0] === '}') { - $this->registerObject($monitoringObject); - return; - } - if (!isset($line[1])) { - $line[1] = ''; - } - $monitoringObject->{$line[0]} = trim($line[1]); - } - throw new ParsingException('Unexpected EOF in objects.cache, line ' . $this->lineCtr); - } - - /** - * Read the next state from the status.dat file handler - * - * @throws Exception\ParsingException - */ - private function readCurrentState() - { - $statusdatObject = new RuntimeStateContainer(); - - $objectType = $this->getObjectTypeForState(); - - if ($objectType != 'host' && $objectType != 'service') { - $this->skipObject(); // ignore unknown objects - return; - } - if (!isset($this->icingaState[$this->currentObjectType])) { - throw new ParsingException("No $this->currentObjectType objects registered in objects.cache"); - } - $base = & $this->icingaState[$this->currentObjectType]; - $state = $this->skipObject(true); - $statusdatObject->runtimeState = & $state; - $name = $this->getObjectIdentifier($statusdatObject); - - if (!isset($base[$name])) { - throw new ParsingException( - "Unknown object $name " . $this->currentObjectType . ' - ' - . print_r( - $statusdatObject, - true - ) - . "\n" . print_r($base, true) - ); - } - $type = substr($this->currentStateType, strlen($objectType)); - - if ($type == 'status') { - // directly set the status to the status field of the given object - $base[$name]->status = & $statusdatObject; - } else { - if (!isset($base[$name]->$type) || !in_array($base[$name]->$type, $this->overwrites)) { - $base[$name]->$type = array(); - $this->overwrites[] = & $base[$name]->$type; - } - array_push($base[$name]->$type, $statusdatObject); - $this->currentObjectType = $type; - if (!isset($this->icingaState[$type])) { - $this->icingaState[$type] = array(); - } - $this->icingaState[$type][] = &$statusdatObject; - $id = $this->getObjectIdentifier($statusdatObject); - if ($id !== false && isset($this->icingaState[$objectType][$id])) { - $statusdatObject->$objectType = $this->icingaState[$objectType][$id]; - } - } - - return; - - } - - /** - * Get the corresponding object type name for the given state - * - * @return string - */ - private function getObjectTypeForState() - { - $pos = strpos($this->currentStateType, 'service'); - - if ($pos === false) { - $pos = strpos($this->currentStateType, 'host'); - } else { - $this->currentObjectType = 'service'; - return 'service'; - } - - if ($pos === false) { - return $this->currentStateType; - } else { - $this->currentObjectType = 'host'; - return 'host'; - } - - return $this->currentObjectType; - } - - /** - * Skip the current object definition - * - * @param bool $returnString If true, the object string will be returned - * @return string The skipped object if $returnString is true - */ - protected function skipObject($returnString = false) - { - if (!$returnString) { - while (trim($this->file->fgets()) !== '}') { - } - return null; - } else { - $str = ''; - while (($val = trim($this->file->fgets())) !== '}') { - $str .= $val . "\n"; - } - return $str; - } - } - - /** - * Register the given object in the icinga state - * - * @param object $object The monitoring object to register - */ - protected function registerObject(&$object) - { - - $name = $this->getObjectIdentifier($object); - if ($name !== false) { - $this->icingaState[$this->currentObjectType][$name] = &$object; - } - $this->registerObjectAsProperty($object); - } - - /** - * Register the given object as a property in related objects - * - * This registers for example hosts underneath their hostgroup and vice cersa - * - * @param object $object The object to register as a property - */ - protected function registerObjectAsProperty(&$object) - { - if ($this->currentObjectType == 'service' - || $this->currentObjectType == 'host' - || $this->currentObjectType == 'contact') { - return null; - } - $isService = strpos($this->currentObjectType, 'service') !== false; - $isHost = strpos($this->currentObjectType, 'host') !== false; - $isContact = strpos($this->currentObjectType, 'contact') !== false; - $name = $this->getObjectIdentifier($object); - - if ($isService === false && $isHost === false && $isContact === false) { - // this would be error in the parser implementation - return null; - } - $property = $this->currentObjectType; - if ($isService) { - $this->currentObjectType = 'service'; - $property = substr($property, strlen('service')); - } elseif ($isHost) { - $this->currentObjectType = 'host'; - $property = substr($property, strlen('host')); - } elseif ($isContact) { - $this->currentObjectType = 'contact'; - $property = substr($property, strlen('contact')); - } - - if (!isset($this->icingaState[$this->currentObjectType])) { - return $this->deferRegistration($object, $this->currentObjectType . $property); - } - - // @TODO: Clean up, this differates between 1:n and 1:1 references - if (strpos($property, 'group') !== false) { - $sourceIdentifier = $this->getMembers($object); - foreach ($sourceIdentifier as $id) { - $source = $this->icingaState[$this->currentObjectType][$id]; - if (!isset($source->$property)) { - $source->$property = array(); - } - $type = $this->currentObjectType; - if (!isset($object->$type)) { - $object->$type = array(); - } - // add the member to the group object - array_push($object->$type, $source); - // add the group to the member object - array_push($source->$property, $name); - } - } else { - $source = $this->icingaState[$this->currentObjectType][$this->getObjectIdentifier($object)]; - if (!isset($source->$property)) { - $source->$property = array(); - } - - array_push($source->$property, $object); - } - - return null; - } - - /** - * Defer registration of the given object - * - * @param object $object The object to defer - * @param String $objType The name of the object type - */ - protected function deferRegistration($object, $objType) - { - $this->deferred[] = array($object, $objType); - } - - /** - * Process deferred objects - */ - protected function processDeferred() - { - foreach ($this->deferred as $obj) { - $this->currentObjectType = $obj[1]; - $this->registerObjectAsProperty($obj[0]); - } - } - - /** - * Return the resolved members directive of an object - * - * @param object $object The object to get the members from - * @return array An array of member names - */ - protected function getMembers(&$object) - { - if (!isset($object->members)) { - return array(); - } - - $members = explode(',', $object->members); - - if ($this->currentObjectType == 'service') { - $res = array(); - for ($i = 0; $i < count($members); $i += 2) { - $res[] = $members[$i] . ';' . $members[$i + 1]; - } - return $res; - } else { - return $members; - } - - } - - /** - * Return the unique name of the given object - * - * @param object $object The object to retrieve the name from - * @return string The name of the object or null if no name can be retrieved - */ - protected function getObjectIdentifier(&$object) - { - if ($this->currentObjectType == 'contact') { - return $object->contact_name; - } - - if ($this->currentObjectType == 'service') { - return $object->host_name . ';' . $object->service_description; - } - $name = $this->currentObjectType . '_name'; - if (isset($object->{$name})) { - return $object->{$name}; - } - if (isset($object->service_description)) { - return $object->host_name . ';' . $object->service_description; - } elseif (isset($object->host_name)) { - return $object->host_name; - } - return null; - - } - - /** - * Return the internal state of the parser - * - * @return null - */ - public function getRuntimeState() - { - return $this->icingaState; - } -} diff --git a/library/Icinga/Protocol/Statusdat/PrintableObject.php b/library/Icinga/Protocol/Statusdat/PrintableObject.php deleted file mode 100644 index 840a7aa2a..000000000 --- a/library/Icinga/Protocol/Statusdat/PrintableObject.php +++ /dev/null @@ -1,20 +0,0 @@ -contact_name)) { - return $this->contact_name; - } elseif (isset($this->service_description)) { - return $this->service_description; - } elseif (isset($this->host_name)) { - return $this->host_name; - } - return ''; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query.php b/library/Icinga/Protocol/Statusdat/Query.php deleted file mode 100644 index f3b5ea85e..000000000 --- a/library/Icinga/Protocol/Statusdat/Query.php +++ /dev/null @@ -1,460 +0,0 @@ - array('host'), - 'services' => array('service'), - 'downtimes' => array('downtime'), - 'groups' => array('hostgroup', 'servicegroup'), - 'hostgroups' => array('hostgroup'), - 'servicegroups' => array('servicegroup'), - 'comments' => array('comment'), - 'contacts' => array('contact'), - 'contactgroups' => array('contactgroup') - ); - - /** - * The current StatusDat query that will be applied upon calling fetchAll - * - * @var IQueryPart - */ - private $queryFilter = null; - - /** - * The current query source being used - * - * @var string - */ - private $source = ''; - - /** - * An array containing all columns used for sorting - * - * @var array - */ - protected $orderColumns = array(); - - /** - * An array containig all columns used for (simple) grouping - * - * @var array - */ - private $groupColumns = array(); - - /** - * An optional function callback to use for more specific grouping - * - * @var array - */ - private $groupByFn = null; - - /** - * The scope index for the callback function - */ - const FN_SCOPE = 0; - - /** - * The name index for the callback function - */ - const FN_NAME = 1; - - /** - * Return true if columns are set for this query - * - * @return bool - */ - public function hasColumns() - { - $columns = $this->getColumns(); - return !empty($columns); - } - - /** - * Set the status.dat specific IQueryPart filter to use - * - * @param IQueryPart $filter - */ - public function setQueryFilter($filter) - { - $this->queryFilter = $filter; - } - - /** - * Order the query result by the given columns - * - * @param String|array $columns An array of columns to order by - * @param String $dir The direction (asc or desc) in string form - * - * @return $this Fluent interface - */ - public function order($columns, $dir = null, $isFunction = false) - { - if ($dir && strtolower($dir) == 'desc') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - if (!is_array($columns)) { - $columns = array($columns); - } - - foreach ($columns as $col) { - if (($pos = strpos($col, ' ')) !== false) { - $dir = strtoupper(substr($col, $pos + 1)); - if ($dir === 'DESC') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - $col = substr($col, 0, $pos); - } else { - $col = $col; - } - - $this->orderColumns[] = array($col, $dir); - } - return $this; - } - - /** - * Order the query result using the callback to retrieve values for items - * - * @param array $columns A scope, function array to use for retrieving the values when ordering - * @param String $dir The direction (asc or desc) in string form - * - * @return $this Fluent interface - */ - public function orderByFn(array $callBack, $dir = null) - { - if ($dir && strtolower($dir) == 'desc') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - $this->orderColumns[] = array($callBack, $dir); - } - - - - /** - * Set the query target - * - * @param String $table The table/target to select the query from - * @param array $columns An array of attributes to use (required for fetchPairs()) - * - * @return $this Fluent interface - * @throws IcingaException If the target is unknonw - */ - public function from($table, array $attributes = null) - { - if (!$this->getColumns() && $attributes) { - $this->setColumns($attributes); - } - if (isset(self::$VALID_TARGETS[$table])) { - $this->source = $table; - } else { - throw new IcingaException( - 'Unknown from target for status.dat :%s', - $table - ); - } - return $this; - } - - /** - * Return an index of all objects matching the filter of this query - * - * This index will be used for ordering, grouping and limiting - */ - private function getFilteredIndices($classType = '\Icinga\Protocol\Statusdat\Query\Group') - { - $baseGroup = $this->queryFilter; - $state = $this->ds->getState(); - $result = array(); - $source = self::$VALID_TARGETS[$this->source]; - - foreach ($source as $target) { - - if (! isset($state[$target])) { - continue; - } - - $indexes = array_keys($state[$target]); - if ($baseGroup) { - $baseGroup->setQuery($this); - $idx = array_keys($state[$target]); - $indexes = $baseGroup->filter($state[$target], $idx); - } - if (!isset($result[$target])) { - $result[$target] = $indexes; - } else { - array_merge($result[$target], $indexes); - } - } - return $result; - } - - /** - * Order the given result set - * - * @param array $indices The result set of the query that should be ordered - */ - private function orderIndices(array &$indices) - { - if (!empty($this->orderColumns)) { - foreach ($indices as $type => &$subindices) { - $this->currentType = $type; - usort($subindices, array($this, 'orderResult')); - } - } - } - - /** - * Start a query - * - * This is just a dummy function to allow a more convenient syntax - * - * @return self Fluent interface - */ - public function select() - { - return $this; - } - - /** - * Order implementation called by usort - * - * @param String $a The left object index - * @param Strinv $b The right object index - * @return int 0, 1 or -1, see usort for detail - */ - private function orderResult($a, $b) - { - $o1 = $this->ds->getObjectByName($this->currentType, $a); - $o2 = $this->ds->getObjectByName($this->currentType, $b); - $result = 0; - - foreach ($this->orderColumns as &$col) { - if (is_array($col[0])) { - // sort by function - $result += $col[1] * strnatcasecmp( - $col[0][0]->$col[0][1]($o1), - $col[0][0]->$col[0][1]($o2) - ); - } else { - $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]}); - } - } - return $result; - } - - /** - * Limit the given resultset - * - * @param array $indices The filtered, ordered indices - */ - private function limitIndices(array &$indices) - { - foreach ($indices as $type => $subindices) { - $indices[$type] = array_slice($subindices, $this->getOffset(), $this->getLimit()); - } - } - - /** - * Register the given function for grouping the result - * - * @param String $fn The function to use for grouping - * @param Object $scope An optional scope to use instead of $this - * - * @return self Fluent interface - */ - public function groupByFunction($fn, $scope = null) - { - $this->groupByFn = array($scope ? $scope : $this, $fn); - return $this; - } - - /** - * Group by the given column - * - * @param array|string $columns The columns to use for grouping - * @return self Fluent interface - * @see Query::columnGroupFn() The implementation used for grouping - */ - public function groupByColumns($columns) - { - if (!is_array($columns)) { - $columns = array($columns); - } - $this->groupColumns = $columns; - $this->groupByFn = array($this, 'columnGroupFn'); - return $this; - } - - /** - * The internal handler function used by the group function - * - * @param array $indices The indices to group - * @return array The grouped result set - */ - private function columnGroupFn(array &$indices) - { - $cols = $this->groupColumns; - $result = array(); - foreach ($indices as $type => $subindices) { - foreach ($subindices as $objectIndex) { - $r = $this->ds->getObjectByName($type, $objectIndex); - $hash = ''; - $cols = array(); - foreach ($this->groupColumns as $col) { - $hash = md5($hash . $r->$col); - $cols[$col] = $r->$col; - } - if (!isset($result[$hash])) { - $result[$hash] = (object)array( - 'columns' => (object)$cols, - 'count' => 0 - ); - } - $result[$hash]->count++; - } - } - return array_values($result); - } - - /** - * Query Filter, Order, Group, Limit and return the result set - * - * @return array The resultset matching this query - */ - public function getResult() - { - $indices = $this->getFilteredIndices(); - $this->orderIndices($indices); - if ($this->groupByFn) { - $scope = $this->groupByFn[self::FN_SCOPE]; - $fn = $this->groupByFn[self::FN_NAME]; - - return $scope->$fn($indices); - } - - $this->limitIndices($indices); - - $result = array(); - $state = $this->ds->getState(); - - foreach ($indices as $type => $subindices) { - foreach ($subindices as $index) { - $result[] = & $state[$type][$index]; - } - } - return $result; - } - - - /** - * Apply all filters of this filterable on the datasource - */ - public function applyFilter() - { - $parser = new TreeToStatusdatQueryParser(); - if ($this->getFilter()) { - $query = $parser->treeToQuery($this->getFilter(), $this); - $this->setQueryFilter($query); - } - - } - - /** - * Return only the first row fetched from the result set - * - * @return MonitoringObjectList The monitoring object matching this query - */ - public function fetchRow() - { - $rs = $this->fetchAll(); - $rs->rewind(); - return $rs->current(); - } - - /** - * Fetch the result as an associative array using the first column as the key and the second as the value - * - * @return array An associative array with the result - * @throws IcingaException If no attributes are defined - */ - public function fetchPairs() - { - $result = array(); - if (count($this->getColumns()) < 2) { - throw new IcingaException( - 'Status.dat "fetchPairs()" query expects at least columns to be set in the query expression' - ); - } - $attributes = $this->getColumns(); - - $param1 = $attributes[0]; - $param2 = $attributes[1]; - foreach ($this->fetchAll() as $resultList) { - $result[$resultList->$param1] = $resultList->$param2; - } - - return $result; - } - - /** - * Fetch all results - * - * @return MonitoringObjectList An MonitoringObjectList wrapping the given resultset - */ - public function fetchAll() - { - $this->applyFilter(); - if (!isset($this->cursor)) { - $result = $this->getResult(); - $this->cursor = new MonitoringObjectList($result, $this); - } - return $this->cursor; - } - - /** - * Return the value of the first column for the first row fetched from the result set - */ - public function fetchOne() - { - throw new ProgrammingError('Statusdat/Query::fetchOne() is not implemented yet'); - } - - /** - * Count the number of results - * - * @return int - */ - public function count() - { - $q = clone $this; - $q->limit(null, null); - return count($q->fetchAll()); - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/Expression.php b/library/Icinga/Protocol/Statusdat/Query/Expression.php deleted file mode 100644 index 1db48df33..000000000 --- a/library/Icinga/Protocol/Statusdat/Query/Expression.php +++ /dev/null @@ -1,415 +0,0 @@ -": - $this->CB = "isGreater"; - break; - case "<": - $this->CB = "isLess"; - break; - case ">=": - $this->CB = "isGreaterEq"; - break; - case "<=": - $this->CB = "isLessEq"; - break; - case "=": - $this->CB = "isEqual"; - break; - case "LIKE": - $this->CB = "isLike"; - break; - case "NOT_LIKE": - $this->CB = "isNotLike"; - break; - case "!=": - $this->CB = "isNotEqual"; - break; - case "IN": - $this->CB = "isIn"; - break; - case "NOT_IN": - $this->CB = "isNotIn"; - break; - default: - throw new IcingaException( - 'Unknown operator %s in expression %s !', - $token, - $this->expression - ); - } - } - - /** - * @param $tokens - * @return mixed - */ - private function extractAggregationFunction(&$tokens) - { - $token = $tokens[0]; - $value = array(); - if (preg_match("/COUNT\{(.*)\}/", $token, $value) == false) { - return $token; - } - $this->function = "count"; - $tokens[0] = $value[1]; - - return null; - } - - /** - * @param $values - */ - private function parseExpression(&$values) - { - $tokenized = preg_split("/ +/", trim($this->expression), 3); - $this->extractAggregationFunction($tokenized); - if (count($tokenized) != 3) { - echo( - "Currently statusdat query expressions must be in " - . "the format FIELD OPERATOR ? or FIELD OPERATOR :value_name" - ); - } - - $this->fields = explode(".", trim($tokenized[0])); - $this->field = $this->fields[count($this->fields) - 1]; - - $this->getOperatorType(trim($tokenized[1])); - $tokenized[2] = trim($tokenized[2]); - - if ($tokenized[2][0] === ":") { - $this->name = substr($tokenized, 1); - $this->value = $values[$this->name]; - } else { - if ($tokenized[2] === "?") { - $this->value = array_shift($values); - } else { - $this->value = trim($tokenized[2]); - } - } - - } - - /** - * @param $expression - * @param $values - * @return $this - */ - public function fromString($expression, &$values) - { - $this->expression = $expression; - $this->parseExpression($values); - return $this; - } - - /** - * @param null $expression - * @param array $values - */ - public function __construct($expression = null, &$values = array()) - { - if ($expression) { - if (!is_array($values)) { - $values = array($values); - } - $this->fromString($expression, $values); - } - - } - - /** - * @param array $base - * @param array $idx - * @return array|mixed - */ - public function filter(array &$base, &$idx = array()) - { - if (!$idx) { - $idx = array_keys($base); - } - $this->basedata = $base; - return array_filter($idx, array($this, "filterFn")); - } - - /** - * @return string - */ - public function getValue() - { - return $this->value; - } - - /** - * @return null - */ - public function getField() - { - return $this->field; - } - - /** - * @param $idx - * @return bool - */ - protected function filterFn($idx) - { - $values = $this->getFieldValues($idx); - - if ($values === false) { - return false; - } - - if ($this->CB == "isIn" || $this->CB == "isNotIn") { - $cmpValues = is_array($this->value) ? $this->value : array($this->value); - foreach ($cmpValues as $cmpValue) { - $this->value = $cmpValue; - foreach ($values as $value) { - if ($this->CB == "isIn" && $this->isLike($value)) { - $this->value = $cmpValues; - return true; - } elseif ($this->CB == "isNotIn" && $this->isNotLike($value)) { - $this->value = $cmpValues; - return true; - } - } - } - $this->value = $cmpValues; - return false; - } - - if ($this->function) { - $values = call_user_func($this->function, $values); - if (!is_array($values)) { - $values = array($values); - } - } - foreach ($values as $val) { - - if (!is_string($val) && !is_numeric($val) && is_object($val)) { - if (isset($val->service_description)) { - $val = $val->service_description; - } elseif (isset($val->host_name)) { - $val = $val->host_name; - } else { - return false; - } - } - if ($this->{$this->CB}($val)) { - return true; - } - } - return false; - } - - /** - * @param $idx - * @return array - */ - private function getFieldValues($idx) - { - $res = $this->basedata[$idx]; - - foreach ($this->fields as $field) { - if (!is_array($res)) { - if ($this->query) { - $res = $this->query->get($res, $field); - continue; - } - - if (!isset($res->$field)) { - $res = array(); - break; - } - $res = $res->$field; - continue; - } - - // it can be that an element contains more than one value, like it - // happens when using comments, in this case we have to create a new - // array that contains the values/objects we're searching - $swap = array(); - foreach ($res as $sub) { - if ($this->query) { - $swap[] = $this->query->get($sub, $field); - continue; - } - if (!isset($sub->$field)) { - continue; - } - if (!is_array($sub->$field)) { - $swap[] = $sub->$field; - } else { - $swap = array_merge($swap, $sub->$field); - } - } - $res = $swap; - } - if (!is_array($res)) { - return array($res); - } - - return $res; - } - - /** - * @param $value - * @return bool - */ - public function isGreater($value) - { - return $value > $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLess($value) - { - return $value < $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLike($value) - { - return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; - } - - /** - * @param $value - * @return bool - */ - public function isNotLike($value) - { - return !preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; - } - - /** - * @param $value - * @return bool - */ - public function isEqual($value) - { - if (!is_numeric($value)) { - return strtolower($value) == strtolower($this->value); - } - return $value == $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isNotEqual($value) - { - return $value != $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isGreaterEq($value) - { - return $value >= $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLessEq($value) - { - return $value <= $this->value; - } - - /** - * Add additional information about the query this filter belongs to - * - * @param $query - * @return mixed - */ - public function setQuery($query) - { - $this->query = $query; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/Group.php b/library/Icinga/Protocol/Statusdat/Query/Group.php deleted file mode 100644 index 2e110843f..000000000 --- a/library/Icinga/Protocol/Statusdat/Query/Group.php +++ /dev/null @@ -1,397 +0,0 @@ -value = $value; - } - - /** - * @return array - */ - public function getItems() - { - return $this->items; - } - - /** - * @return string - */ - public function getType() - { - return $this->type ? $this->type : self::TYPE_AND; - } - - /** - * @param $type - */ - public function setType($type) - { - $this->type = $type; - } - - /** - * @throws IcingaException - */ - private function tokenize() - { - $token = 0; - $subgroupCount = 0; - while ($token != self::EOF) { - - $token = $this->getNextToken(); - - if ($token === self::GROUP_BEGIN) { - - /** - * check if this is a nested group, if so then it's - * considered part of the subexpression - */ - if ($subgroupCount == 0) { - $this->startNewSubExpression(); - } - $subgroupCount++; - continue; - } - if ($token === self::GROUP_END) { - if ($subgroupCount < 1) { - throw new IcingaException( - 'Invalid Query: unexpected \')\' at pos %s', - $this->parsePos - ); - } - $subgroupCount--; - /* - * check if this is a nested group, if so then it's - * considered part of the subexpression - */ - if ($subgroupCount == 0) { - $this->addSubgroupFromExpression(); - } - continue; - } - - if ($token === self::CONJUNCTION_AND && $subgroupCount == 0) { - $this->startNewSubExpression(); - if ($this->type != self::TYPE_AND && $this->type != "") { - $this->createImplicitGroup(self::TYPE_AND); - break; - } else { - $this->type = self::TYPE_AND; - } - continue; - } - if ($token === self::CONJUNCTION_OR && $subgroupCount == 0) { - $this->startNewSubExpression(); - if ($this->type != self::TYPE_OR && $this->type != "") { - $this->createImplicitGroup(self::TYPE_OR); - break; - } else { - - $this->type = self::TYPE_OR; - } - continue; - } - - $this->subExpressionLength = $this->parsePos - $this->subExpressionStart; - } - if ($subgroupCount > 0) { - throw new IcingaException('Unexpected end of query, are you missing a parenthesis?'); - } - - $this->startNewSubExpression(); - } - - /** - * @param $type - */ - private function createImplicitGroup($type) - { - $group = new Group(); - $group->setType($type); - $group->addItem(array_pop($this->items)); - - $group->fromString(substr($this->expression, $this->parsePos), $this->value, $this->expressionClass); - $this->items[] = $group; - $this->parsePos = strlen($this->expression); - - } - - /** - * - */ - private function startNewSubExpression() - { - if ($this->getCurrentSubExpression() != "") { - if (!$this->expressionClass) { - $this->items[] = new Expression($this->getCurrentSubExpression(), $this->value); - } else { - $this->items[] = new $this->expressionClass($this->getCurrentSubExpression(), $this->value); - } - } - - $this->subExpressionStart = $this->parsePos; - $this->subExpressionLength = 0; - } - - /** - * @return string - */ - private function getCurrentSubExpression() - { - - return substr($this->expression, $this->subExpressionStart, $this->subExpressionLength); - } - - /** - * - */ - private function addSubgroupFromExpression() - { - - if (!$this->expressionClass) { - $this->items[] = new Group($this->getCurrentSubExpression(), $this->value); - } else { - $group = new Group(); - $group->fromString($this->getCurrentSubExpression(), $this->value, $this->expressionClass); - $this->items[] = $group; - } - $this->subExpressionStart = $this->parsePos; - $this->subExpressionLength = 0; - } - - /** - * @return bool - */ - private function isEOF() - { - if ($this->parsePos >= strlen($this->expression)) { - return true; - } - return false; - } - - /** - * @return int|string - */ - private function getNextToken() - { - if ($this->isEOF()) { - return self::EOF; - } - - // skip whitespaces - while ($this->expression[$this->parsePos] == " ") { - $this->parsePos++; - if ($this->isEOF()) { - return self::EOF; - } - } - if ($this->expression[$this->parsePos] == self::GROUP_BEGIN) { - $this->parsePos++; - return self::GROUP_BEGIN; - } - if ($this->expression[$this->parsePos] == self::GROUP_END) { - $this->parsePos++; - return self::GROUP_END; - } - if (substr_compare( - $this->expression, - self::CONJUNCTION_AND, - $this->parsePos, - strlen(self::CONJUNCTION_AND), - true - ) === 0) { - $this->parsePos += strlen(self::CONJUNCTION_AND); - return self::CONJUNCTION_AND; - } - if (substr_compare( - $this->expression, - self::CONJUNCTION_OR, - $this->parsePos, - strlen(self::CONJUNCTION_OR), - true - ) === 0) { - $this->parsePos += strlen(self::CONJUNCTION_OR); - return self::CONJUNCTION_OR; - } - $this->parsePos++; - return self::EXPRESSION; - } - - /** - * @param $ex - * @return $this - */ - public function addItem($ex) - { - $this->items[] = $ex; - return $this; - } - - /** - * @param $expression - * @param array $value - * @param null $expressionClass - * @return $this - */ - public function fromString($expression, &$value = array(), $expressionClass = null) - { - $this->expression = $expression; - $this->value = & $value; - $this->expressionClass = $expressionClass; - - $this->tokenize(); - return $this; - } - - /** - * @param null $expression - * @param array $value - */ - public function __construct($expression = null, &$value = array()) - { - if ($expression) { - $this->fromString($expression, $value); - } - } - - /** - * @param array $base - * @param null $idx - * @return array|null - */ - public function filter(array &$base, &$idx = null) - { - if ($this->type == self::TYPE_OR) { - $idx = array(); - foreach ($this->items as &$subFilter) { - $baseKeys = array_keys($base); - $subFilter->setQuery($this->query); - $idx += $subFilter->filter($base, $baseKeys); - } - } else { - if (!$idx) { - $idx = array_keys($base); - } - foreach ($this->items as $subFilter) { - $subFilter->setQuery($this->query); - $idx = array_intersect($idx, $subFilter->filter($base, $idx)); - } - } - - return $idx; - } - - /** - * Add additional information about the query this filter belongs to - * - * @param $query - * @return mixed - */ - public function setQuery($query) - { - $this->query = $query; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php b/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php deleted file mode 100644 index 1dcd434df..000000000 --- a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php +++ /dev/null @@ -1,36 +0,0 @@ -noCache = $noCache; - if (isset($config->no_cache)) { - $this->noCache = $config->no_cache; - } - $this->config = $config; - $this->parser = $parser; - - if (!$this->noCache) { - $this->cache = $this->initializeCaches($config); - if ($this->fromCache()) { - $this->createHostServiceConnections(); - return; - } - } - - if (!$this->lastState) { - $this->parseObjectsCacheFile(); - } - if (!$this->hasRuntimeState) { - - } - $this->parseStatusDatFile(); - if (!$noCache && $this->newState) { - $this->statusCache->save($this->parser->getRuntimeState(), 'object' . md5($this->config->object_file)); - } - $this->createHostServiceConnections(); - - } - - /** - * Initialize the internal caches if enabled - * - * @throws ConfigurationError - */ - private function initializeCaches() - { - $defaultCachePath = self::STATUSDAT_DEFAULT_CACHE_PATH; - $cachePath = $this->config->get('cache_path', $defaultCachePath); - $maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME)); - $cachingEnabled = true; - if (!is_writeable($cachePath)) { - Logger::warning( - 'Can\'t cache Status.dat backend; make sure cachepath %s is writable by the web user. ' - . 'Caching is now disabled', - $cachePath - ); - $cachePath = null; - } - $backendOptions = array( - 'cache_dir' => $cachePath - ); - // the object cache might exist for months and is still valid - $this->objectCache = $this->initCache($this->config->object_file, $backendOptions, null, $cachingEnabled); - $this->statusCache = $this->initCache( - $this->config->status_file, - $backendOptions, - $maxCacheLifetime, - $cachingEnabled - ); - } - - /** - * Init the Cache backend in Zend - * - * @param String $file The file to use as the cache master file - * @param Zend_Config $backend The backend configuration to use - * @param integer $lifetime The lifetime of the cache - * - * @return \Zend_Cache_Core|\Zend_Cache_Frontend - */ - private function initCache($file, $backendConfig, $lifetime) - { - $frontendOptions = array( - 'lifetime' => $lifetime, - 'automatic_serialization' => true, - 'master_files' => array($file) - ); - return \Zend_Cache::factory('Core', 'File', $frontendOptions, $backendConfig); - } - - /** - * Read the current cache state - * - * @return bool True if the state is the same as the icinga state - */ - private function fromCache() - { - if (!$this->readObjectsCache()) { - $this->newState = true; - return false; - } - if (!$this->readStatusCache()) { - $this->newState = true; - return false; - } - return true; - } - - /** - * Read the object.cache file from the Zend_Cache backend - * - * @return bool True if the file could be loaded from cache - */ - private function readObjectsCache() - { - $this->lastState = $this->objectCache->load('object' . md5($this->config->object_file)); - if ($this->lastState == false) { - return false; - } - - return true; - } - - /** - * Read the status.dat file from the Zend_Cache backend - * - * @return bool True if the file could be loaded from cache - */ - private function readStatusCache() - { - if (!isset($this->stateCache)) { - return true; - } - $statusInfo = $this->stateCache->load('state' . md5($this->config->status_file)); - if ($statusInfo == false) { - return false; - } - - $this->hasRuntimeState = true; - return true; - } - - /** - * Take the status.dat and objects.cache and connect all services to hosts - * - */ - private function createHostServiceConnections() - { - if (!isset($this->lastState["service"])) { - return; - } - foreach ($this->lastState["host"] as &$host) { - $host->host = $host; - } - foreach ($this->lastState["service"] as &$service) { - $service->service = &$service; // allow easier querying - $host = &$this->lastState["host"][$service->host_name]; - if (!isset($host->services)) { - $host->services = array(); - } - $host->services[$service->service_description] = & $service; - $service->host = & $host; - } - } - - /** - * Parse the object.cache file and update the current state - * - * @throws ConfigurationError If the object.cache couldn't be read - */ - private function parseObjectsCacheFile() - { - if (!is_readable($this->config->object_file)) { - throw new ConfigurationError( - 'Can\'t read object-file "%s", check your configuration', - $this->config->object_file - ); - } - if (!$this->parser) { - $this->parser = new Parser(new File($this->config->object_file, 'r')); - } - $this->parser->parseObjectsFile(); - $this->lastState = $this->parser->getRuntimeState(); - } - - /** - * Parse the status.dat file and update the current state - * - * @throws ConfigurationError If the status.dat couldn't be read - */ - private function parseStatusDatFile() - { - if (!is_readable($this->config->status_file)) { - throw new ConfigurationError( - 'Can\'t read status-file %s, check your configuration', - $this->config->status_file - ); - } - if (!$this->parser) { - $this->parser = new Parser(new File($this->config->status_file, 'r'), $this->lastState); - } - $this->parser->parseRuntimeState(new File($this->config->status_file, 'r')); - $this->lastState = $this->parser->getRuntimeState(); - if (!$this->noCache) { - $this->statusCache->save(array("true" => true), "state" . md5($this->config->object_file)); - } - } - - /** - * Create a new Query - * - * @return Query The query to operate on - */ - public function select() - { - return new Query($this); - } - - /** - * Return the internal state of the status.dat - * - * @return mixed The internal status.dat representation - */ - public function getState() - { - return $this->lastState; - } - - - /** - * Return the object with the given name and type - * - * @param String $type The type of the object to return (service, host, servicegroup...) - * @param String $name The name of the object - * - * @return ObjectContainer An object container wrapping the result or null if the object doesn't exist - */ - public function getObjectByName($type, $name) - { - if (isset($this->lastState[$type]) && isset($this->lastState[$type][$name])) { - return new ObjectContainer($this->lastState[$type][$name], $this); - } - return null; - } - - /** - * Get an array containing all names of monitoring objects with the given type - * - * @param String $type The type of object to get the names for - * @return array An array of names or null if the type does not exist - */ - public function getObjectNames($type) - { - return isset($this->lastState[$type]) ? array_keys($this->lastState[$type]) : null; - } -} diff --git a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php b/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php deleted file mode 100644 index b15dfe4bb..000000000 --- a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php +++ /dev/null @@ -1,72 +0,0 @@ -runtimeState = $str; - } - - /** - * Return true if the argument exists - * - * @param String $attr The argument to retrieve - * @return bool True if it exists, otherwise false - */ - public function __isset($attr) - { - try { - $this->__get($attr); - return true; - } catch (\InvalidArgumentException $e) { - return false; - } - } - - /** - * Return the given attribute - * - * If the container string is not yet parsed, this will happen here - * - * @param String $attr The attribute to retrieve - * @return mixed The value of the attribute - * @throws \InvalidArgumentException When the attribute does not exist - */ - public function __get($attr) - { - $start = strpos($this->runtimeState, $attr . "="); - if ($start === false) { - throw new \InvalidArgumentException("Unknown property $attr"); - } - - $start += strlen($attr . "="); - $len = strpos($this->runtimeState, "\n", $start) - $start; - $this->$attr = trim(substr($this->runtimeState, $start, $len)); - - return $this->$attr; - } -} diff --git a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php b/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php deleted file mode 100644 index 4e4bc76a1..000000000 --- a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php +++ /dev/null @@ -1,42 +0,0 @@ -dataSet = $dataset; - $this->position = 0; - $this->dataView = $dataView; - } - - public function count() - { - return count($this->dataSet); - } - - public function setPosition($pos) - { - $this->position = $pos; - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the current element - * @link http://php.net/manual/en/iterator.current.php - * @return mixed Can return any type. - */ - public function current() - { - if ($this->dataView) { - return $this; - } - - return $this->dataSet[$this->position]; - } - - /** - * (PHP 5 >= 5.0.0)
- * Move forward to next element - * @link http://php.net/manual/en/iterator.next.php - * @return void Any returned value is ignored. - */ - public function next() - { - $this->position++; - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the key of the current element - * @link http://php.net/manual/en/iterator.key.php - * @return mixed scalar on success, or null on failure. - */ - public function key() - { - return $this->position; - } - - /** - * (PHP 5 >= 5.0.0)
- * Checks if current position is valid - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean The return value will be casted to boolean and then evaluated. - * Returns true on success or false on failure. - */ - public function valid() - { - return $this->position < count($this->dataSet); - } - - /** - * (PHP 5 >= 5.0.0)
- * Rewind the Iterator to the first element - * @link http://php.net/manual/en/iterator.rewind.php - * @return void Any returned value is ignored. - */ - public function rewind() - { - $this->position = 0; - } - - public function __isset($name) - { - return $this->dataView->exists($this->dataSet[$this->position], $name); - } - - public function __get($name) - { - return $this->dataView->get($this->dataSet[$this->position], $name); - } - - public function __set($name, $value) - { - throw new IcingaException('Setting is currently not available for objects'); - } - - public function offsetExists($offset) - { - return count($this->dataSet) < $offset; - } - - public function offsetGet($offset) - { - $res = new MonitoringObjectList($this->dataSet, $this->dataView); - $res->position = $offset; - return $res; - } - - public function offsetSet($offset, $value) - { - // non mutable - } - - public function offsetUnset($offset) - { - // non mutable - } -} diff --git a/library/Icinga/Test/BaseTestCase.php b/library/Icinga/Test/BaseTestCase.php index b72bdee62..a735550cb 100644 --- a/library/Icinga/Test/BaseTestCase.php +++ b/library/Icinga/Test/BaseTestCase.php @@ -25,20 +25,16 @@ namespace Icinga\Test { use RuntimeException; use Mockery; use Zend_Config; - use Zend_Controller_Request_Abstract; - use Zend_Controller_Request_HttpTestCase; use PHPUnit_Framework_TestCase; use Icinga\Application\Icinga; use Icinga\Util\DateTimeFactory; use Icinga\Data\ResourceFactory; use Icinga\Data\Db\DbConnection; - use Icinga\User\Preferences; - use Icinga\Web\Form; /** * Class BaseTestCase */ - class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest, FormTest + class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest { /** * Path to application/ @@ -82,13 +78,6 @@ namespace Icinga\Test { */ public static $moduleDir; - /** - * Store request for form tests - * - * @var Zend_Controller_Request_HttpTestCase - */ - private $request; - /** * Resource configuration for different database types * @@ -151,28 +140,54 @@ namespace Icinga\Test { public function setUp() { parent::setUp(); - - $requestMock = Mockery::mock('Icinga\Web\Request'); - $requestMock->shouldReceive('getPathInfo')->andReturn('') - ->shouldReceive('getBaseUrl')->andReturn('/') - ->shouldReceive('getQuery')->andReturn(array()); - $this->setupIcingaMock($requestMock); + $this->setupIcingaMock(); } /** * Setup mock object for the application's bootstrap * - * @param Zend_Controller_Request_Abstract $request The request to be returned by - * Icinga::app()->getFrontController()->getRequest() + * @return Mockery\Mock */ - protected function setupIcingaMock(Zend_Controller_Request_Abstract $request) + protected function setupIcingaMock() { + $requestMock = Mockery::mock('Icinga\Web\Request')->shouldDeferMissing(); + $requestMock->shouldReceive('getPathInfo')->andReturn('')->byDefault() + ->shouldReceive('getBaseUrl')->andReturn('/')->byDefault() + ->shouldReceive('getQuery')->andReturn(array())->byDefault() + ->shouldReceive('getParam')->with(Mockery::type('string'), Mockery::type('string')) + ->andReturnUsing(function ($name, $default) { return $default; })->byDefault(); + + $responseMock = Mockery::mock('Icinga\Web\Response')->shouldDeferMissing(); + + // Can't express this as demeter chains. See: https://github.com/padraic/mockery/issues/59 $bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing(); - $bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing( - function () use ($request) { return $request; } - )->shouldReceive('getApplicationDir')->andReturn(self::$appDir); + $bootstrapMock->shouldReceive('getFrontController')->andReturn($bootstrapMock) + ->shouldReceive('getApplicationDir')->andReturn(self::$appDir) + ->shouldReceive('getRequest')->andReturn($requestMock) + ->shouldReceive('getResponse')->andReturn($responseMock); Icinga::setApp($bootstrapMock, true); + return $bootstrapMock; + } + + /** + * Return the currently active request mock object + * + * @return Icinga\Web\Request + */ + public function getRequestMock() + { + return Icinga::app()->getFrontController()->getRequest(); + } + + /** + * Return the currently active response mock object + * + * @return Icinga\Web\Response + */ + public function getResponseMock() + { + return Icinga::app()->getFrontController()->getResponse(); } /** @@ -298,58 +313,6 @@ namespace Icinga\Test { $adapter->exec('DROP TABLE ' . $table . ';'); } } - - /** - * Instantiate a form - * - * If the form has CSRF protection enabled, creates the form's token element and adds the generated token to the - * request data - * - * @param string $formClass Qualified class name of the form to create. Note that the class has to be - * defined as no attempt is made to require the class before instantiating. - * @param array $requestData Request data for the form - * - * @return Form - * @throws RuntimeException - */ - public function createForm($formClass, array $requestData = array()) - { - $form = new $formClass; - // If the form has CSRF protection enabled, add the token to the request data, else all calls to - // isSubmittedAndValid will fail - $form->setSessionId('1234'); - $form->initCsrfToken(); - $token = $form->getValue($form->getTokenElementName()); - if ($token !== null) { - $requestData[$form->getTokenElementName()] = $token; - } - $request = $this->getRequest(); - $request->setMethod('POST'); - $request->setPost($requestData); - $form->setRequest($request); - $form->setUserPreferences( - new Preferences( - array() - ) - ); - return $form; - } - - /** - * Retrieve test case request object - * - * This is a mock methods borrowed from Zend Controller Test Case to handle form tests properly (#6106) - * - * @return Zend_Controller_Request_HttpTestCase - */ - public function getRequest() - { - if (null === $this->request) { - require_once 'Zend/Controller/Request/HttpTestCase.php'; - $this->request = new Zend_Controller_Request_HttpTestCase; - } - return $this->request; - } } BaseTestCase::setupTimezone(); diff --git a/library/Icinga/Test/FormTest.php b/library/Icinga/Test/FormTest.php deleted file mode 100644 index e8b070483..000000000 --- a/library/Icinga/Test/FormTest.php +++ /dev/null @@ -1,23 +0,0 @@ -permissions = $permissions; + natcasesort($permissions); + if (! empty($permissions)) { + $this->permissions = array_combine($permissions, $permissions); + } + return $this; } /** @@ -382,38 +380,6 @@ class User return new DateTimeZone($tz); } - /** - * Add a message that can be accessed from future requests, to this user. - * - * This function does NOT automatically write to the session, messages will not be persisted until you do. - * - * @param Message $msg The message - */ - public function addMessage(Message $msg) - { - $this->messages[] = $msg; - } - - /** - * Get all currently pending messages - * - * @return array The messages - */ - public function getMessages() - { - return isset($this->messages) ? $this->messages : array(); - } - - /** - * Remove all messages from this user - * - * This function does NOT automatically write the session, messages will not be persisted until you do. - */ - public function clearMessages() - { - $this->messages = null; - } - /** * Set additional remote user information * @@ -442,6 +408,33 @@ class User */ public function isRemoteUser() { - return (count($this->remoteUserInformation)) ? true : false; + return ! empty($this->remoteUserInformation); + } + + /** + * Whether the user has a given permission + * + * @param string $permission + * + * @return bool + */ + public function can($permission) + { + if (isset($this->permissions['*']) || isset($this->permissions[$permission])) { + return true; + } + foreach ($this->permissions as $permitted) { + $wildcard = strpos($permitted, '*'); + if ($wildcard !== false) { + if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) { + return true; + } else { + if ($permission === $permitted) { + return true; + } + } + } + } + return false; } } diff --git a/library/Icinga/User/Message.php b/library/Icinga/User/Message.php deleted file mode 100644 index 83d780563..000000000 --- a/library/Icinga/User/Message.php +++ /dev/null @@ -1,59 +0,0 @@ -message = $message; - $this->level = $level; - } - - /** - * @return string - */ - public function getMessage() - { - return $this->message; - } - - /** - * @return The - */ - public function getLevel() - { - return $this->level; - } -} diff --git a/library/Icinga/User/Preferences.php b/library/Icinga/User/Preferences.php index b1dae867a..364112a9e 100644 --- a/library/Icinga/User/Preferences.php +++ b/library/Icinga/User/Preferences.php @@ -79,19 +79,38 @@ class Preferences implements Countable } /** - * Retrieve a preference and return $default if the preference is not set + * Retrieve a preference section * * @param string $name - * @param mixed $default * - * @return mixed + * @return array|null */ - public function get($name, $default = null) + public function get($name) { if (array_key_exists($name, $this->preferences)) { return $this->preferences[$name]; } + return null; + } + + /** + * Retrieve a value from a specific section + * + * @param string $section + * @param string $name + * @param null $default + * + * @return array|null + */ + public function getValue($section, $name, $default = null) + { + if (array_key_exists($section, $this->preferences) + && array_key_exists($name, $this->preferences[$section]) + ) { + return $this->preferences[$section][$name]; + } + return $default; } diff --git a/library/Icinga/Util/DateTimeFactory.php b/library/Icinga/Util/DateTimeFactory.php index 724b54770..4a482dc0a 100644 --- a/library/Icinga/Util/DateTimeFactory.php +++ b/library/Icinga/Util/DateTimeFactory.php @@ -76,4 +76,18 @@ class DateTimeFactory implements ConfigAwareFactory { return new DateTime($time, $timeZone !== null ? $timeZone : self::$timeZone); } + + /** + * Check whether a variable is a Unix timestamp + * + * @param mixed $timestamp + * + * @return bool + */ + public static function isUnixTimestamp($timestamp) + { + return (is_int($timestamp) || ctype_digit($timestamp)) + && ($timestamp <= PHP_INT_MAX) + && ($timestamp >= ~PHP_INT_MAX); + } } diff --git a/library/Icinga/Util/Enumerate.php b/library/Icinga/Util/Enumerate.php deleted file mode 100644 index 0861f7f65..000000000 --- a/library/Icinga/Util/Enumerate.php +++ /dev/null @@ -1,62 +0,0 @@ -iterator = $iterator; - } - - public function rewind() - { - $this->iterator->rewind(); - $this->key = 0; - } - - public function next() - { - $this->iterator->next(); - ++$this->key; - } - - public function valid() - { - return $this->iterator->valid(); - } - - public function current() - { - return $this->iterator->current(); - } - - public function key() - { - return $this->key; - } -} diff --git a/library/Icinga/Util/EnumeratingFilterIterator.php b/library/Icinga/Util/EnumeratingFilterIterator.php new file mode 100644 index 000000000..44ef9b0c8 --- /dev/null +++ b/library/Icinga/Util/EnumeratingFilterIterator.php @@ -0,0 +1,37 @@ +index = 0; + } + + /** + * @return int + */ + public function key() + { + return $this->index++; + } +} diff --git a/library/Icinga/Util/File.php b/library/Icinga/Util/File.php index b7aff9743..9ce1ec893 100644 --- a/library/Icinga/Util/File.php +++ b/library/Icinga/Util/File.php @@ -131,7 +131,7 @@ class File extends SplFileObject set_error_handler( function ($errno, $errstr, $errfile, $errline) { restore_error_handler(); - throw new ErrorException($errno, $errstr, $errfile, $errline); + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); }, E_WARNING ); diff --git a/library/Icinga/Util/String.php b/library/Icinga/Util/String.php index b64d9e524..0bebb06e7 100644 --- a/library/Icinga/Util/String.php +++ b/library/Icinga/Util/String.php @@ -21,4 +21,18 @@ class String { return array_map('trim', explode($delimiter, $value)); } + + /** + * Uppercase the first character of each word in a string assuming and removing the underscore as word separator + * + * Converts 'first_name' to 'firstName' for example. + * + * @param string $name + * + * @return string + */ + public static function cname($name) + { + return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name)))); + } } diff --git a/library/Icinga/Util/Translator.php b/library/Icinga/Util/Translator.php index b6cf533d9..755203474 100644 --- a/library/Icinga/Util/Translator.php +++ b/library/Icinga/Util/Translator.php @@ -34,13 +34,22 @@ class Translator * * Falls back to the default domain in case the string cannot be translated using the given domain * - * @param string $text The string to translate - * @param string $domain The primary domain to use + * @param string $text The string to translate + * @param string $domain The primary domain to use + * @param string|null $context Optional parameter for context based translation * - * @return string The translated string + * @return string The translated string */ - public static function translate($text, $domain) + public static function translate($text, $domain, $context = null) { + if ($context !== null) { + $res = self::pgettext($text, $domain, $context); + if ($res === $text && $domain !== self::DEFAULT_DOMAIN) { + $res = self::pgettext($text, self::DEFAULT_DOMAIN, $context); + } + return $res; + } + $res = dgettext($domain, $text); if ($res === $text && $domain !== self::DEFAULT_DOMAIN) { return dgettext(self::DEFAULT_DOMAIN, $text); @@ -48,6 +57,86 @@ class Translator return $res; } + /** + * Translate a plural string + * + * Falls back to the default domain in case the string cannot be translated using the given domain + * + * @param string $textSingular The string in singular form to translate + * @param string $textPlural The string in plural form to translate + * @param integer $number The number to get the plural or singular string + * @param string $domain The primary domain to use + * @param string|null $context Optional parameter for context based translation + * + * @return string The translated string + */ + public static function translatePlural($textSingular, $textPlural, $number, $domain, $context = null) + { + if ($context !== null) { + $res = self::pngettext($textSingular, $textPlural, $number, $domain, $context); + if (($res === $textSingular || $res === $textPlural) && $domain !== self::DEFAULT_DOMAIN) { + $res = self::pngettext($textSingular, $textPlural, $number, self::DEFAULT_DOMAIN, $context); + } + return $res; + } + + $res = dngettext($domain, $textSingular, $textPlural, $number); + if (($res === $textSingular || $res === $textPlural) && $domain !== self::DEFAULT_DOMAIN) { + $res = dngettext(self::DEFAULT_DOMAIN, $textSingular, $textPlural, $number); + } + return $res; + } + + /** + * Emulated pgettext() + * + * @link http://php.net/manual/de/book.gettext.php#89975 + * + * @param $text + * @param $domain + * @param $context + * + * @return string + */ + public static function pgettext($text, $domain, $context) + { + $contextString = "{$context}\004{$text}"; + + $translation = dcgettext($domain, $contextString, LC_MESSAGES); + + if ($translation == $contextString) { + return $text; + } else { + return $translation; + } + } + + /** + * Emulated pngettext() + * + * @link http://php.net/manual/de/book.gettext.php#89975 + * + * @param $textSingular + * @param $textPlural + * @param $number + * @param $domain + * @param $context + * + * @return string + */ + public static function pngettext($textSingular, $textPlural, $number, $domain, $context) + { + $contextString = "{$context}\004{$textSingular}"; + + $translation = dcngettext($domain, $contextString, $textPlural, $number, LC_MESSAGES); + + if ($translation == $contextString || $translation == $textPlural) { + return ($number == 1 ? $textSingular : $textPlural); + } else { + return $translation; + } + } + /** * Register a new gettext domain * diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index aead4043a..315209232 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -39,10 +39,6 @@ class ActionController extends Zend_Controller_Action */ protected $requiresAuthentication = true; - private $config; - - private $configs = array(); - private $autorefreshInterval; private $reloadCss = false; @@ -91,23 +87,29 @@ class ActionController extends Zend_Controller_Action } $this->view->tabs = new Tabs(); + $this->prepareInit(); $this->init(); } + /** + * Prepare controller initialization + * + * As it should not be required for controllers to call the parent's init() method, base controllers should use + * prepareInit() in order to prepare the controller initialization. + * + * @see \Zend_Controller_Action::init() For the controller initialization method. + */ + protected function prepareInit() + { + } + public function Config($file = null) { if ($file === null) { - if ($this->config === null) { - $this->config = Config::app(); - } - return $this->config; + return Config::app(); } else { - if (! array_key_exists($file, $this->configs)) { - $this->configs[$file] = Config::module($module, $file); - } - return $this->configs[$file]; + return Config::app($file); } - return $this->config; } public function Auth() @@ -216,13 +218,29 @@ class ActionController extends Zend_Controller_Action * * Autoselects the module domain, if any, and falls back to the global one if no translation could be found. * - * @param string $text The string to translate + * @param string $text The string to translate + * @param string|null $context Optional parameter for context based translation * - * @return string The translated string + * @return string The translated string */ - public function translate($text) + public function translate($text, $context = null) { - return Translator::translate($text, $this->view->translationDomain); + return Translator::translate($text, $this->view->translationDomain, $context); + } + + /** + * Translate a plural string + * + * @param string $textSingular The string in singular form to translate + * @param string $textPlural The string in plural form to translate + * @param string $number The number to get the plural or singular string + * @param string|null $context Optional parameter for context based translation + * + * @return string The translated string + */ + public function translatePlural($textSingular, $textPlural, $number, $context = null) + { + return Translator::translatePlural($textSingular, $textPlural, $number, $this->view->translationDomain, $context); } protected function ignoreXhrBody() @@ -347,7 +365,7 @@ class ActionController extends Zend_Controller_Action if ($user = $req->getUser()) { // Cast preference app.show_benchmark to bool because preferences loaded from a preferences storage are // always strings - if ((bool) $user->getPreferences()->get('app.show_benchmark', false) === true) { + if ((bool) $user->getPreferences()->getValue('icingaweb', 'show_benchmark', false) === true) { if (!$this->_helper->viewRenderer->getNoRender()) { $layout->benchmark = $this->renderBenchmark(); } diff --git a/library/Icinga/Web/Controller/BaseConfigController.php b/library/Icinga/Web/Controller/BaseConfigController.php deleted file mode 100644 index 98941686e..000000000 --- a/library/Icinga/Web/Controller/BaseConfigController.php +++ /dev/null @@ -1,61 +0,0 @@ -getUser()->addMessage( - new Message($msg, Zend_Log::INFO) - ); - Session::getSession()->write(); - } - - /** - * Send a message with the logging level Zend_Log::ERR to the current user and - * commit the changes to the underlying session. - * - * @param $msg The message content - */ - protected function addErrorMessage($msg) - { - AuthenticationManager::getInstance()->getUser()->addMessage( - new Message($msg, Zend_Log::ERR) - ); - Session::getSession()->write(); - } - - /* - * Return an array of tabs provided by this configuration controller. - * - * Those tabs will automatically be added to the application's configuration dialog - * - * @return array - */ - public static function createProvidedTabs() - { - return array(); - } -} diff --git a/library/Icinga/Web/Controller/BasePreferenceController.php b/library/Icinga/Web/Controller/BasePreferenceController.php index a83b8e6c0..f7d45aa5f 100644 --- a/library/Icinga/Web/Controller/BasePreferenceController.php +++ b/library/Icinga/Web/Controller/BasePreferenceController.php @@ -4,11 +4,6 @@ namespace Icinga\Web\Controller; -use Icinga\Application\Config as IcingaConfig; -use Icinga\Exception\ConfigurationError; -use Icinga\Web\Session; -use Icinga\User\Preferences\PreferencesStore; - /** * Base class for Preference Controllers * @@ -42,27 +37,4 @@ class BasePreferenceController extends ActionController parent::init(); $this->view->tabs = ControllerTabCollector::collectControllerTabs('PreferenceController'); } - - protected function savePreferences(array $preferences) - { - $session = Session::getSession(); - $currentPreferences = $session->user->getPreferences(); - foreach ($preferences as $key => $value) { - if ($value === null) { - $currentPreferences->remove($key); - } else { - $currentPreferences->{$key} = $value; - } - } - $session->write(); - - if (($preferencesConfig = IcingaConfig::app()->preferences) === null) { - throw new ConfigurationError( - 'Cannot save preferences changes since you\'ve not configured a preferences backend' - ); - } - $store = PreferencesStore::create($preferencesConfig, $session->user); - $store->load(); // Necessary for patching existing preferences - $store->save($currentPreferences); - } } diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php index 55663123b..0f540c2a0 100644 --- a/library/Icinga/Web/Controller/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -6,9 +6,10 @@ namespace Icinga\Web\Controller; use Icinga\Application\Config; use Icinga\Application\Icinga; -use Zend_Controller_Request_Abstract as Request; -use Zend_Controller_Response_Abstract as Response; +/** + * Base class for module action controllers + */ class ModuleActionController extends ActionController { private $config; @@ -17,34 +18,42 @@ class ModuleActionController extends ActionController private $module; + /** + * Module name + * + * @var string + */ protected $moduleName; - public function __construct( - Request $request, - Response $response, - array $invokeArgs = array() - ) { - parent::__construct($request, $response, $invokeArgs); - $this->moduleName = $request->getModuleName(); + /** + * (non-PHPDoc) + * @see \Icinga\Web\Controller\ActionController For the method documentation. + */ + protected function prepareInit() + { + $this->moduleName = $this->_request->getModuleName(); $this->_helper->layout()->moduleName = $this->moduleName; $this->view->translationDomain = $this->moduleName; $this->moduleInit(); } + /** + * Prepare module action controller initialization + */ + protected function moduleInit() + { + } + public function Config($file = null) { - $module = $this->getRequest()->getModuleName(); - - $this->moduleName = $module; - if ($file === null) { if ($this->config === null) { - $this->config = Config::module($module); + $this->config = Config::module($this->moduleName); } return $this->config; } else { if (! array_key_exists($file, $this->configs)) { - $this->configs[$file] = Config::module($module, $file); + $this->configs[$file] = Config::module($this->moduleName, $file); } return $this->configs[$file]; } @@ -58,19 +67,13 @@ class ModuleActionController extends ActionController return $this->module; } - public function postDispatch() - { - $req = $this->getRequest(); - $resp = $this->getResponse(); - - if ($this->isXhr()) { - $resp->setHeader('X-Icinga-Module', $this->moduleName); - } - - parent::postDispatch(); - } - - protected function moduleInit() + /** + * (non-PHPDoc) + * @see \Icinga\Web\Controller\ActionController::postDispatchXhr() For the method documentation. + */ + public function postDispatchXhr() { + parent::postDispatchXhr(); + $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName); } } diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 65dc7c8d1..631b2eb07 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -4,47 +4,65 @@ namespace Icinga\Web; -use Zend_Controller_Request_Abstract; -use Zend_Form; +use LogicException; use Zend_Config; -use Zend_Form_Element_Submit; -use Zend_Form_Element_Reset; +use Zend_Form; use Zend_View_Interface; -use Icinga\Web\Session; -use Icinga\Web\Form\Element\Note; -use Icinga\Exception\ProgrammingError; -use Icinga\Web\Form\Decorator\HelpText; -use Icinga\Web\Form\Decorator\BootstrapForm; -use Icinga\Web\Form\InvalidCSRFTokenException; -use Icinga\Application\Config as IcingaConfig; +use Icinga\Application\Icinga; +use Icinga\Web\Form\Decorator\NoScriptApply; +use Icinga\Web\Form\Element\CsrfCounterMeasure; /** * Base class for forms providing CSRF protection, confirmation logic and auto submission + * + * @method $this setDefaults(array $defaults) { + * Use `Form::populate()' for setting default values for elements instead because `Form::setDefaults()' does not + * create the form via `Form::create()'. + * + * Due to a BC introduced with https://github.com/mhujer/zf1/commit/244e3d3f88a363ee0ca49cf63eee31f925f515cd + * we cannot override this function without running into a strict standards violation on Zend version 1.12.7. + * + * @param array $defaults + * + * @return $this + * } */ class Form extends Zend_Form { /** - * The form's request object + * Whether this form has been created * - * @var Zend_Controller_Request_Abstract + * @var bool */ - protected $request; + protected $created = false; /** - * Main configuration + * The callback to call instead of Form::onSuccess() * - * Used as fallback if user preferences are not available. - * - * @var IcingaConfig + * @var Callback */ - protected $config; + protected $onSuccess; /** - * The preference object to use instead of the one from the user (used for testing) + * Label to use for the standard submit button * - * @var Zend_Config + * @var string */ - protected $preferences; + protected $submitLabel; + + /** + * The url to redirect to upon success + * + * @var string|Url + */ + protected $redirectUrl; + + /** + * The view script to use when rendering this form + * + * @var string + */ + protected $viewScript; /** * Whether this form should NOT add random generated "challenge" tokens that are associated with the user's current @@ -56,83 +74,184 @@ class Form extends Zend_Form protected $tokenDisabled = false; /** - * Name of the CSRF token element (used to create non-colliding hashes) + * Name of the CSRF token element * * @var string */ protected $tokenElementName = 'CSRFToken'; /** - * Flag to indicate that form is already build + * Whether this form should add a UID element being used to distinct different forms posting to the same action * * @var bool */ - protected $created = false; + protected $uidDisabled = false; /** - * Session id used for CSRF token generation + * Name of the form identification element * * @var string */ - protected $sessionId; + protected $uidElementName = 'formUID'; /** - * Label for submit button + * Default element decorators * - * If omitted, no button will be shown - * - * @var string + * @var array */ - protected $submitLabel; + public static $defaultElementDecorators = array( + 'ViewHelper', + 'Errors', + array('Description', array('tag' => 'span', 'class' => 'description')), + 'Label', + array('HtmlTag', array('tag' => 'div')) + ); /** - * Label for cancel button + * Create a new form * - * If omitted, no button will be shown + * Accepts an additional option `onSuccess' which is a callback that is called instead of this + * form's method. It is called using the following signature: (Request $request, Form $form). * - * @var string + * @see Zend_Form::__construct() + * + * @throws LogicException In case `onSuccess' is not callable */ - protected $cancelLabel; + public function __construct($options = null) + { + if (is_array($options) && isset($options['onSuccess'])) { + $this->onSuccess = $options['onSuccess']; + unset($options['onSuccess']); + } elseif (isset($options->onSuccess)) { + $this->onSuccess = $options->onSuccess; + unset($options->onSuccess); + } + + if ($this->onSuccess !== null && false === is_callable($this->onSuccess)) { + throw new LogicException('The option `onSuccess\' is not callable'); + } + + parent::__construct($options); + } /** - * Last used note-id + * Set the label to use for the standard submit button * - * Helper to generate unique names for note elements + * @param string $label The label to use for the submit button * - * @var int + * @return self */ - protected $last_note_id = 0; + public function setSubmitLabel($label) + { + $this->submitLabel = $label; + return $this; + } /** - * Getter for the session ID - * - * If the ID has never been set, the ID from session_id() is returned + * Return the label being used for the standard submit button * * @return string */ - public function getSessionId() + public function getSubmitLabel() { - if (!$this->sessionId) { - $this->sessionId = Session::getSession()->getId(); + return $this->submitLabel; + } + + /** + * Set the url to redirect to upon success + * + * @param string|Url $url The url to redirect to + * + * @return self + */ + public function setRedirectUrl($url) + { + $this->redirectUrl = $url; + return $this; + } + + /** + * Return the url to redirect to upon success + * + * @return string|Url + */ + public function getRedirectUrl() + { + if ($this->redirectUrl === null) { + $url = Url::fromRequest(array(), $this->getRequest()); + // Be sure to remove all form dependent params because we do not want to submit it again + $this->redirectUrl = $url->without(array_keys($this->getElements())); } - return $this->sessionId; + return $this->redirectUrl; } /** - * Setter for the session ID + * Set the view script to use when rendering this form * - * This method should be used for testing purposes only + * @param string $viewScript The view script to use * - * @param string $sessionId + * @return self */ - public function setSessionId($sessionId) + public function setViewScript($viewScript) { - $this->sessionId = $sessionId; + $this->viewScript = $viewScript; + return $this; } /** - * Return the HTML element name of the CSRF token field + * Return the view script being used when rendering this form + * + * @return string + */ + public function getViewScript() + { + return $this->viewScript; + } + + /** + * Disable CSRF counter measure and remove its field if already added + * + * @param bool $disabled Set true in order to disable CSRF protection for this form, otherwise false + * + * @return self + */ + public function setTokenDisabled($disabled = true) + { + $this->tokenDisabled = (bool) $disabled; + + if ($disabled && $this->getElement($this->tokenElementName) !== null) { + $this->removeElement($this->tokenElementName); + } + + return $this; + } + + /** + * Return whether CSRF counter measures are disabled for this form + * + * @return bool + */ + public function getTokenDisabled() + { + return $this->tokenDisabled; + } + + /** + * Set the name to use for the CSRF element + * + * @param string $name The name to set + * + * @return self + */ + public function setTokenElementName($name) + { + $this->tokenElementName = $name; + return $this; + } + + /** + * Return the name of the CSRF element * * @return string */ @@ -142,427 +261,383 @@ class Form extends Zend_Form } /** - * Render the form to HTML + * Disable form identification and remove its field if already added * - * @param Zend_View_Interface $view - * - * @return string - */ - public function render(Zend_View_Interface $view = null) - { - // Elements must be there to render the form - $this->buildForm(); - return parent::render($view); - } - - /** - * Add elements to this form (used by extending classes) - */ - protected function create() - { - - } - - /** - * Method called before validation - */ - protected function preValidation(array $data) - { - - } - - /** - * Setter for the request - * - * @param Zend_Controller_Request_Abstract $request - */ - public function setRequest(Zend_Controller_Request_Abstract $request) - { - $this->request = $request; - } - - /** - * Getter for the request - * - * @return Zend_Controller_Request_Abstract - */ - public function getRequest() - { - return $this->request; - } - - /** - * Set the configuration to be used for this form when no preferences are set yet - * - * @param IcingaConfig $cfg + * @param bool $disabled Set true in order to disable identification for this form, otherwise false * * @return self */ - public function setConfiguration($cfg) + public function setUidDisabled($disabled = true) { - $this->config = $cfg; + $this->uidDisabled = (bool) $disabled; + + if ($disabled && $this->getElement($this->uidElementName) !== null) { + $this->removeElement($this->uidElementName); + } + return $this; } /** - * Get the main configuration + * Return whether identification is disabled for this form * - * Returns the set configuration or an empty default one. - * - * @return Zend_Config + * @return bool */ - public function getConfiguration() + public function getUidDisabled() { - if ($this->config === null) { - $this->config = new Zend_Config(array(), true); - } - - return $this->config; + return $this->uidDisabled; } /** - * Set preferences to be used instead of the one from the user object (used for testing) + * Set the name to use for the form identification element * - * @param Zend_Config $prefs + * @param string $name The name to set + * + * @return self */ - public function setUserPreferences($prefs) + public function setUidElementName($name) { - $this->preferences = $prefs; + $this->uidElementName = $name; + return $this; } /** - * Return the preferences of the user or the overwritten ones + * Return the name of the form identification element * - * @return Zend_Config + * @return string */ - public function getUserPreferences() + public function getUidElementName() { - if ($this->preferences) { - return $this->preferences; - } - - return $this->getRequest()->getUser()->getPreferences(); + return $this->uidElementName; } /** - * Create the form if not done already + * Create this form * - * Adds all elements to the form + * @param array $formData The data sent by the user + * + * @return self */ - public function buildForm() + public function create(array $formData = array()) { - if ($this->created === false) { - $this->initCsrfToken(); - $this->create(); + if (false === $this->created) { + $this->createElements($formData); + $this->addFormIdentification() + ->addCsrfCounterMeasure() + ->addSubmitButton(); - if ($this->submitLabel) { - $this->addSubmitButton(); - } - - if ($this->cancelLabel) { - $this->addCancelButton(); - } - - // Empty action if not safe - if (!$this->getAction() && $this->getRequest()) { - $this->setAction($this->getRequest()->getRequestUri()); + if ($this->getAction() === '') { + // We MUST set an action as JS gets confused otherwise, if + // this form is being displayed in an additional column + $this->setAction(Url::fromRequest()->without(array_keys($this->getElements()))); } $this->created = true; } + + return $this; } /** - * Setter for the cancel label + * Create and add elements to this form * - * @param string $cancelLabel + * Intended to be implemented by concrete form classes. + * + * @param array $formData The data sent by the user */ - public function setCancelLabel($cancelLabel) + public function createElements(array $formData) { - $this->cancelLabel = $cancelLabel; + } /** - * Add cancel button to form + * Perform actions after this form was submitted using a valid request + * + * Intended to be implemented by concrete form classes. The base implementation returns always FALSE. + * + * @param Request $request The valid request used to process this form + * + * @return null|bool Return FALSE in case no redirect should take place */ - protected function addCancelButton() + public function onSuccess(Request $request) { - $this->addElement( - new Zend_Form_Element_Reset( - array( - 'name' => 'btn_reset', - 'label' => $this->cancelLabel, - 'class' => 'btn pull-right' - ) - ) - ); + return false; } /** - * Setter for the submit label + * Perform actions when no form dependent data was sent * - * @param string $submitLabel + * Intended to be implemented by concrete form classes. + * + * @param Request $request The current request */ - public function setSubmitLabel($submitLabel) + public function onRequest(Request $request) { - $this->submitLabel = $submitLabel; + } /** - * Add submit button to form + * Add a submit button to this form + * + * Uses the label previously set with Form::setSubmitLabel(). Overwrite this + * method in order to add multiple submit buttons or one with a custom name. + * + * @return self */ - protected function addSubmitButton() + public function addSubmitButton() { - $this->addElement( - new Zend_Form_Element_Submit( - array( - 'name' => 'btn_submit', - 'label' => $this->submitLabel - ) - ) - ); - } - - /** - * Add message to form - * - * @param string $message The message to be displayed - * @param int $headingType Whether it should be displayed as heading (1-6) or not (null) - */ - public function addNote($message, $headingType = null) - { - $this->addElement( - new Note( - array( - 'escape' => $headingType === null ? false : true, - 'name' => sprintf('note_%s', $this->last_note_id++), - 'value' => $headingType === null ? $message : sprintf( - '%2$s', - $headingType, - $message - ) - ) - ) - ); - } - - /** - * Enable automatic form submission on the given elements - * - * Enables automatic submission of this form once the user edits specific elements - * - * @param array $triggerElements The element names which should auto-submit the form - * - * @throws ProgrammingError When an element is found which does not yet exist - */ - public function enableAutoSubmit($triggerElements) - { - foreach ($triggerElements as $elementName) { - $element = $this->getElement($elementName); - if ($element !== null) { - $class = $element->getAttrib('class'); - if ($class === null) { - $class = 'autosubmit'; - } else { - $class .= ' autosubmit'; - } - $element->setAttrib('class', $class); - } else { - throw new ProgrammingError( - 'You need to add the element "%s" to the form before automatic submission can be enabled!', - $elementName - ); - } - } - } - - /** - * Check whether the form was submitted with a valid request - * - * Ensures that the current request method is POST, that the form was manually submitted and that the data provided - * in the request is valid and gets repopulated in case its invalid. - * - * @return bool True when the form is submitted and valid, otherwise false - */ - public function isSubmittedAndValid() - { - if ($this->getRequest()->isPost() === false) { - return false; - } - - $this->buildForm(); - $checkData = $this->getRequest()->getParams(); - $this->assertValidCsrfToken($checkData); - - if ($this->isSubmitted()) { - // perform full validation if submitted - $this->preValidation($checkData); - return $this->isValid($checkData); - } else { - // only populate if not submitted - $this->populate($checkData); - $this->setAttrib('data-icinga-form-modified', 'true'); - return false; - } - } - - /** - * Check whether this form has been submitted - * - * Per default, this checks whether the button set with the 'setSubmitLabel' method - * is being submitted. For custom submission logic, this method must be overwritten - * - * @return bool True when the form is marked as submitted, otherwise false - */ - public function isSubmitted() - { - // TODO: There are some missunderstandings and missconceptions to be - // found in this class. If populate() etc would have been used as - // designed this function would read as simple as: - // return $this->getElement('btn_submit')->isChecked(); - - if ($this->submitLabel) { - $checkData = $this->getRequest()->getParams(); - return isset($checkData['btn_submit']) && $checkData['btn_submit']; - } - return true; - } - - /** - * Disable CSRF counter measure and remove its field if already added - * - * This method should be used for testing purposes only - * - * @param bool $disabled Set true in order to disable CSRF tokens in - * this form (default: true), otherwise false - */ - public function setTokenDisabled($disabled = true) - { - $this->tokenDisabled = (boolean) $disabled; - - if ($disabled === true) { - $this->removeElement($this->tokenElementName); - } - } - - /** - * Add CSRF counter measure field to form - */ - public function initCsrfToken() - { - if (!$this->tokenDisabled && $this->getElement($this->tokenElementName) === null) { + $submitLabel = $this->getSubmitLabel(); + if ($submitLabel) { $this->addElement( - 'hidden', - $this->tokenElementName, + 'submit', + 'btn_submit', array( - 'value' => $this->generateCsrfTokenAsString() + 'ignore' => true, + 'label' => $submitLabel, + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div')) + ) ) ); } + + return $this; } /** - * Test the submitted data for a correct CSRF token + * Add a subform * - * @param array $checkData The POST data send by the user + * @param Zend_Form $form The subform to add + * @param string $name The name of the subform or null to use the name of $form + * @param int $order The location where to insert the form * - * @throws InvalidCSRFTokenException When CSRF Validation fails + * @return Zend_Form */ - public function assertValidCsrfToken(array $checkData) + public function addSubForm(Zend_Form $form, $name = null, $order = null) { - if (!$this->tokenDisabled) { - if (!isset($checkData[$this->tokenElementName]) - || !$this->hasValidCsrfToken($checkData[$this->tokenElementName]) - ) { - throw new InvalidCSRFTokenException(); + if ($form instanceof self) { + $form->removeDecorator('Form'); + $form->setSubmitLabel(''); + $form->setTokenDisabled(); + $form->setUidDisabled(); + } + + if ($name === null) { + $name = $form->getName(); + } + + return parent::addSubForm($form, $name, $order); + } + + /** + * Create a new element + * + * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set the + * `disableLoadDefaultDecorators' option to any other value than `true'. For loading custom element decorators use + * the 'decorators' option. + * + * @param string $type String element type + * @param string $name The name of the element to add + * @param mixed $options The options for the element + * + * @return Zend_Form_Element + * + * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators. + */ + public function createElement($type, $name, $options = null) + { + if ($options !== null) { + if ($options instanceof Zend_Config) { + $options = $options->toArray(); } - } - } - - /** - * Check whether the form's CSRF token-field has a valid value - * - * @param string $elementValue Value from the form element - * - * @return bool - */ - protected function hasValidCsrfToken($elementValue) - { - if ($this->getElement($this->tokenElementName) === null || strpos($elementValue, '|') === false) { - return false; + if (! isset($options['decorators']) + && ! array_key_exists('disabledLoadDefaultDecorators', $options) + ) { + $options['decorators'] = static::$defaultElementDecorators; + } + } else { + $options = array('decorators' => static::$defaultElementDecorators); } - list($seed, $token) = explode('|', $elementValue); + $el = parent::createElement($type, $name, $options); - if (!is_numeric($seed)) { - return false; + if ($el && $el->getAttrib('autosubmit')) { + $el->addDecorator(new NoScriptApply()); // Non-JS environments + $class = $el->getAttrib('class'); + if (is_array($class)) { + $class[] = 'autosubmit'; + } elseif ($class === null) { + $class = 'autosubmit'; + } else { + $class .= ' autosubmit'; + } + $el->setAttrib('class', $class); // JS environments + unset($el->autosubmit); } - return $token === hash('sha256', $this->getSessionId() . $seed); + return $el; } /** - * Generate a new (seed, token) pair - * - * @return array - */ - public function generateCsrfToken() - { - $seed = mt_rand(); - $hash = hash('sha256', $this->getSessionId() . $seed); - - return array($seed, $hash); - } - - /** - * Return the string representation of the CSRF seed/token pair - * - * @return string - */ - public function generateCsrfTokenAsString() - { - list ($seed, $token) = $this->generateCsrfToken($this->getSessionId()); - return sprintf('%s|%s', $seed, $token); - } - - /** - * Add a new element - * - * Additionally, all DtDd tags will be removed and the Bootstrap compatible - * BootstrapForm decorator will be added to the elements - * - * @param string|Zend_Form_Element $element String element type, or an object of type Zend_Form_Element - * @param string $name The name of the element to add if $element is a string - * @param array $options The settings for the element if $element is a string + * Add a field with a unique and form specific ID * * @return self - * @see Zend_Form::addElement() */ - public function addElement($element, $name = null, $options = null) + public function addFormIdentification() { - parent::addElement($element, $name, $options); - $el = $name !== null ? $this->getElement($name) : $element; - - if ($el) { - if (strpos(strtolower(get_class($el)), 'hidden') !== false) { - // Do not add structural elements to invisible elements which produces ugly views - $el->setDecorators(array('ViewHelper')); - } else { - $el->removeDecorator('HtmlTag'); - $el->removeDecorator('Label'); - $el->removeDecorator('DtDdWrapper'); - $el->addDecorator(new BootstrapForm()); - $el->addDecorator(new HelpText()); - } + if (false === $this->uidDisabled && $this->getElement($this->uidElementName) === null) { + $this->addElement( + 'hidden', + $this->uidElementName, + array( + 'ignore' => true, + 'value' => $this->getName(), + 'decorators' => array('ViewHelper') + ) + ); } return $this; } + /** + * Add CSRF counter measure field to this form + * + * @return self + */ + public function addCsrfCounterMeasure() + { + if (false === $this->tokenDisabled && $this->getElement($this->tokenElementName) === null) { + $this->addElement(new CsrfCounterMeasure($this->tokenElementName)); + } + + return $this; + } + + /** + * Populate the elements with the given values + * + * @param array $defaults The values to populate the elements with + */ + public function populate(array $defaults) + { + $this->create($defaults); + return parent::populate($defaults); + } + + /** + * Process the given request using this form + * + * Redirects to the url set with setRedirectUrl() upon success. See onSuccess() + * and onRequest() wherewith you can customize the processing logic. + * + * @param Request $request The request to be processed + * + * @return Request The request supposed to be processed + */ + public function handleRequest(Request $request = null) + { + if ($request === null) { + $request = $this->getRequest(); + } + + $formData = $this->getRequestData($request); + if ($this->getUidDisabled() || $this->wasSent($formData)) { + $this->populate($formData); // Necessary to get isSubmitted() to work + if (! $this->getSubmitLabel() || $this->isSubmitted()) { + if ($this->isValid($formData) + && (($this->onSuccess !== null && false !== call_user_func($this->onSuccess, $request, $this)) + || ($this->onSuccess === null && false !== $this->onSuccess($request)))) { + $this->getResponse()->redirectAndExit($this->getRedirectUrl()); + } + } else { + // The form can't be processed but we want to show validation errors though + $this->isValidPartial($formData); + } + } else { + $this->onRequest($request); + } + + return $request; + } + + /** + * Return whether the submit button of this form was pressed + * + * When overwriting Form::addSubmitButton() be sure to overwrite this method as well. + * + * @return bool True in case it was pressed, False otherwise or no submit label was set + */ + public function isSubmitted() + { + if ($this->getSubmitLabel()) { + return $this->getElement('btn_submit')->isChecked(); + } + + return false; + } + + /** + * Return whether the data sent by the user refers to this form + * + * Ensures that the correct form gets processed in case there are multiple forms + * with equal submit button names being posted against the same route. + * + * @param array $formData The data sent by the user + * + * @return bool Whether the given data refers to this form + */ + public function wasSent(array $formData) + { + return isset($formData[$this->uidElementName]) && $formData[$this->uidElementName] === $this->getName(); + } + + /** + * Return whether the given values (possibly incomplete) are valid + * + * Unlike Zend_Form::isValid() this will not set NULL as value for + * an element that is not present in the given data. + * + * @param array $formData The data to validate + * + * @return bool + */ + public function isValidPartial(array $formData) + { + $this->create($formData); + return parent::isValidPartial($formData); + } + + /** + * Return whether the given values are valid + * + * @param array $formData The data to validate + * + * @return bool + */ + public function isValid($formData) + { + $this->create($formData); + return parent::isValid($formData); + } + + /** + * Remove all elements of this form + * + * @return self + */ + public function clearElements() + { + $this->created = false; + return parent::clearElements(); + } + /** * Load the default decorators * - * Overwrites Zend_Form::loadDefaultDecorators to avoid having the HtmlTag-Decorator added + * Overwrites Zend_Form::loadDefaultDecorators to avoid having + * the HtmlTag-Decorator added and to provide viewscript usage * * @return self */ @@ -574,11 +649,84 @@ class Form extends Zend_Form $decorators = $this->getDecorators(); if (empty($decorators)) { - $this->addDecorator('FormElements') - //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')) - ->addDecorator('Form'); + if ($this->viewScript) { + $this->addDecorator('ViewScript', array( + 'viewScript' => $this->viewScript, + 'form' => $this + )); + } else { + $this->addDecorator('FormErrors', array('onlyCustomFormErrors' => true)) + ->addDecorator('FormElements') + //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')) + ->addDecorator('Form'); + } } return $this; } + + /** + * Return the name of this form + * + * @return string + */ + public function getName() + { + $name = parent::getName(); + if (! $name) { + $name = get_class($this); + $this->setName($name); + $name = parent::getName(); + } + return $name; + } + + /** + * Return the request data based on this form's request method + * + * @param Request $request The request to fetch the data from + * + * @return array + */ + public function getRequestData(Request $request) + { + if (strtolower($request->getMethod()) === $this->getMethod()) { + return $request->{'get' . ($request->isPost() ? 'Post' : 'Query')}(); + } + + return array(); + } + + /** + * Return the current request + * + * @return Request + */ + public function getRequest() + { + return Icinga::app()->getFrontController()->getRequest(); + } + + /** + * Return the current Response + * + * @return Response + */ + public function getResponse() + { + return Icinga::app()->getFrontController()->getResponse(); + } + + /** + * Render this form + * + * @param Zend_View_Interface $view The view context to use + * + * @return string + */ + public function render(Zend_View_Interface $view = null) + { + $this->create(); + return parent::render($view); + } } diff --git a/library/Icinga/Web/Form/Decorator/BootstrapForm.php b/library/Icinga/Web/Form/Decorator/BootstrapForm.php deleted file mode 100644 index dfb60de2c..000000000 --- a/library/Icinga/Web/Form/Decorator/BootstrapForm.php +++ /dev/null @@ -1,80 +0,0 @@ - dom added per default - * - * @var array - */ - private static $noLabel = array( - 'Zend_Form_Element_Hidden', - 'Zend_Form_Element_Button', - 'Zend_Form_Element_Submit' - ); - - /** - * Return the DOM for the element label - * - * @param String $elementName The name of the element - * - * @return String The DOM for the form element's label - */ - public function getLabel($elementName) - { - $label = $this->getElement()->getLabel(); - if (!$label) { - $label = ' '; - } - if (in_array($this->getElement()->getType(), self::$noLabel) - && !$this->getElement()->getAttrib('addLabelPlaceholder', false)) { - $label = ''; - } else { - if (in_array($this->getElement()->getType(), self::$noLabel)) { - $label = ' '; - } - $label = ''; - } - return $label; - } - - /** - * Render this element - * - * Renders as the following: - *
- * $dtLabel - * $dtElement - *
- * - * @param String $content The content of the form element - * - * @return String The decorated form element - */ - public function render($content) - { - $el = $this->getElement(); - $elementName = $el->getName(); - $label = $this->getLabel($elementName); - return '
' - . $label - . $content - . '
'; - } -} diff --git a/library/Icinga/Web/Form/Decorator/HelpText.php b/library/Icinga/Web/Form/Decorator/HelpText.php deleted file mode 100644 index 970da5cfb..000000000 --- a/library/Icinga/Web/Form/Decorator/HelpText.php +++ /dev/null @@ -1,37 +0,0 @@ -getElement()->getAttribs(); - $visible = true; - if (isset($attributes['condition'])) { - $visible = $attributes['condition'] == '1'; - } - if (isset($attributes['helptext']) && $visible) { - $content = $content - . '

' - . $attributes['helptext'] - . '

'; - } - return $content; - } -} diff --git a/library/Icinga/Web/Form/Decorator/NoScriptApply.php b/library/Icinga/Web/Form/Decorator/NoScriptApply.php new file mode 100644 index 000000000..509c3531e --- /dev/null +++ b/library/Icinga/Web/Form/Decorator/NoScriptApply.php @@ -0,0 +1,35 @@ +'; + } + + return $content; + } +} diff --git a/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php b/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php new file mode 100644 index 000000000..e9bc37edd --- /dev/null +++ b/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php @@ -0,0 +1,100 @@ +addDecorator('ViewHelper'); + $this->setValue($this->generateCsrfToken()); + } + + /** + * Check whether $value is a valid CSRF token + * + * @param string $value The value to check + * @param mixed $context Context to use + * + * @return bool True, in case the CSRF token is valid + * + * @throws InvalidCSRFTokenException In case the CSRF token is not valid + */ + public function isValid($value, $context = null) + { + if (parent::isValid($value, $context) && $this->isValidCsrfToken($value)) { + return true; + } + + throw new InvalidCSRFTokenException(); + } + + /** + * Check whether the given value is a valid CSRF token for the current session + * + * @param string $token The CSRF token + * + * @return bool + */ + protected function isValidCsrfToken($token) + { + if (strpos($token, '|') === false) { + return false; + } + + list($seed, $hash) = explode('|', $token); + + if (false === is_numeric($seed)) { + return false; + } + + return $hash === hash('sha256', Session::getSession()->getId() . $seed); + } + + /** + * Generate a new (seed, token) pair + * + * @return string + */ + protected function generateCsrfToken() + { + $seed = mt_rand(); + $hash = hash('sha256', Session::getSession()->getId() . $seed); + return sprintf('%s|%s', $seed, $hash); + } +} diff --git a/library/Icinga/Web/Form/Element/DateTimePicker.php b/library/Icinga/Web/Form/Element/DateTimePicker.php index dd48bd7f2..728262827 100644 --- a/library/Icinga/Web/Form/Element/DateTimePicker.php +++ b/library/Icinga/Web/Form/Element/DateTimePicker.php @@ -4,100 +4,133 @@ namespace Icinga\Web\Form\Element; +use DateTime; +use Icinga\Web\Form; +use Icinga\Web\Form\FormElement; use Icinga\Web\Form\Validator\DateTimeValidator; -use Zend_Form_Element_Text; -use Zend_Form_Element; -use Icinga\Util\DateTimeFactory; /** - * Datetime form element which returns the input as Unix timestamp after the input has been proven valid. Utilizes - * DateTimeFactory to ensure time zone awareness - * - * @see isValid() + * A date-and-time input control */ -class DateTimePicker extends Zend_Form_Element_Text +class DateTimePicker extends FormElement { /** - * Default format used my js picker + * Form view helper to use for rendering * * @var string */ - public $defaultFormat = 'Y-m-d H:i:s'; - - /** - * JS picker support on or off - * @var bool - */ - public $jspicker = true; - - /** - * View helper to use - * @var string - */ public $helper = 'formDateTime'; /** - * The validator used for datetime validation - * @var DateTimeValidator + * @var bool */ - private $dateValidator; + protected $local = true; /** - * Valid formats to check user input against - * @var array - */ - public $patterns = array(); - - /** - * Create a new DateTimePicker + * The expected lower bound for the element’s value * - * @param array|string|\Zend_Config $spec - * @param null $options - * @see Zend_Form_Element::__construct() + * @var DateTime|null */ - public function __construct($spec, $options = null) + protected $min; + + /** + * The expected upper bound for the element’s + * + * @var DateTime|null + */ + protected $max; + + /** + * (non-PHPDoc) + * @see Zend_Form_Element::init() For the method documentation. + */ + public function init() { - parent::__construct($spec, $options); + $this->addValidator( + new DateTimeValidator($this->local), true // true for breaking the validator chain on failure + ); + if ($this->min !== null) { + $this->addValidator('GreaterThan', true, array('min' => $this->min)); + } + if ($this->max !== null) { + $this->addValidator('LessThan', true, array('max' => $this->max)); + } + } - $this->patterns[] = $this->defaultFormat; + public function setLocal($local) + { + $this->local = (bool) $local; + return $this; + } - $this->dateValidator = new DateTimeValidator($this->patterns); - $this->addValidator($this->dateValidator); + public function getLocal() + { + return $this->local; } /** - * Validate filtered date/time strings + * Set the expected lower bound for the element’s value * - * Expects one or more valid formats being set in $this->patterns. Sets element value as Unix timestamp - * if the input is considered valid. Utilizes DateTimeFactory to ensure time zone awareness. + * @param DateTime $min + * + * @return $this + */ + public function setMin(DateTime $min) + { + $this->min = $min; + return $this; + } + + /** + * Get the expected lower bound for the element’s value + * + * @return DateTime|null + */ + public function getMin() + { + return $this->min; + } + + /** + * Set the expected upper bound for the element’s value + * + * @param DateTime $max + * + * @return $this + */ + public function setMax(DateTime $max) + { + $this->max = $max; + return $this; + } + + /** + * Get the expected upper bound for the element’s value + * + * @return DateTime|null + */ + public function getMax() + { + return $this->max; + } + + /** + * Is the date and time valid? + * + * @param string|DateTime $value + * @param mixed $context * - * @param string $value - * @param mixed $context * @return bool */ public function isValid($value, $context = null) { - // Overwrite the internal validator to use - - if (!parent::isValid($value, $context)) { + if (! parent::isValid($value, $context)) { return false; } - $pattern = $this->dateValidator->getValidPattern(); - if (!$pattern) { - $this->setValue($value); - return true; + if (! $value instanceof DateTime) { + $format = $this->local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339; + $this->setValue(DateTime::createFromFormat($format, $value)); } - $this->setValue(DateTimeFactory::parse($value, $pattern)->getTimestamp()); return true; } - - public function enableJsPicker() - { - $this->jspicker = true; - } - - public function disableJsPicker() - { - $this->jspicker = false; - } } diff --git a/library/Icinga/Web/Form/Element/Note.php b/library/Icinga/Web/Form/Element/Note.php index 5788c200d..78881ab44 100644 --- a/library/Icinga/Web/Form/Element/Note.php +++ b/library/Icinga/Web/Form/Element/Note.php @@ -4,25 +4,43 @@ namespace Icinga\Web\Form\Element; -use Zend_Form_Element_Xhtml; +use Zend_Form_Element; +use Icinga\Web\Form; /** - * Implements note element for Zend forms + * A note */ -class Note extends Zend_Form_Element_Xhtml +class Note extends Zend_Form_Element { /** - * Name of the view helper + * Form view helper to use for rendering * * @var string */ public $helper = 'formNote'; /** - * Return true to ensure redrawing + * Ignore element when retrieving values at form level * - * @param mixed $value The value of to validate (ignored) - * @return bool Always true + * @var bool + */ + protected $_ignore = true; + + /** + * (non-PHPDoc) + * @see Zend_Form_Element::init() For the method documentation. + */ + public function init() + { + $this->setDecorators(Form::$defaultElementDecorators); + } + + /** + * Validate element value (pseudo) + * + * @param mixed $value Ignored + * + * @return bool Always true */ public function isValid($value) { diff --git a/library/Icinga/Web/Form/Element/Number.php b/library/Icinga/Web/Form/Element/Number.php index d87a6d09f..836142514 100644 --- a/library/Icinga/Web/Form/Element/Number.php +++ b/library/Icinga/Web/Form/Element/Number.php @@ -4,38 +4,127 @@ namespace Icinga\Web\Form\Element; -use Zend_Form_Element_Xhtml; +use Icinga\Web\Form\FormElement; /** - * Number form element + * A number input control */ -class Number extends Zend_Form_Element_Xhtml +class Number extends FormElement { /** - * Default form view helper to use for rendering + * Form view helper to use for rendering * * @var string */ - public $helper = "formNumber"; + public $helper = 'formNumber'; /** - * Check whether $value is of type integer + * The expected lower bound for the element’s value * - * @param string $value The value to check - * @param mixed $context Context to use - * - * @return bool + * @var float|null */ - public function isValid($value, $context = null) + protected $min; + + /** + * The expected upper bound for the element’s + * + * @var float|null + */ + protected $max; + + /** + * The value granularity of the element’s value + * + * Normally, number input controls are limited to an accuracy of integer values. + * + * @var float|string|null + */ + protected $step; + + /** + * (non-PHPDoc) + * @see \Zend_Form_Element::init() For the method documentation. + */ + public function init() { - if (parent::isValid($value, $context)) { - if (is_numeric($value)) { - return true; - } - - $this->addError(t('Please enter a number.')); + $this->addValidator('Float', true); // true for breaking the validator chain on failure + if ($this->min !== null) { + $this->addValidator('GreaterThan', true, array('min' => $this->min)); } + if ($this->max !== null) { + $this->addValidator('LessThan', true, array('max' => $this->max)); + } + } - return false; + /** + * Set the expected lower bound for the element’s value + * + * @param float $min + * + * @return $this + */ + public function setMin($min) + { + $this->min = (float) $min; + return $this; + } + + /** + * Get the expected lower bound for the element’s value + * + * @return float|null + */ + public function getMin() + { + return $this->min; + } + + /** + * Set the expected upper bound for the element’s value + * + * @param float $max + * + * @return $this + */ + public function setMax($max) + { + $this->max = (int) $max; + return $this; + } + + /** + * Get the expected upper bound for the element’s value + * + * @return float|null + */ + public function getMax() + { + return $this->max; + } + + /** + * Set the value granularity of the element’s value + * + * @param float|string $step + * + * @return $this + */ + public function setStep($step) + { + if ($step !== 'any') { + $step = (float) $step; + } + $this->step = $step; + return $this; + } + + /** + * Get the value granularity of the element’s value + * + * @return float|string|null + */ + public function getStep() + { + return $this->step; } } diff --git a/library/Icinga/Web/Form/FormElement.php b/library/Icinga/Web/Form/FormElement.php new file mode 100644 index 000000000..690be1acf --- /dev/null +++ b/library/Icinga/Web/Form/FormElement.php @@ -0,0 +1,62 @@ +_disableLoadDefaultDecorators === true; + } + + /** + * Load default decorators + * + * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set + * FormElement::$_disableLoadDefaultDecorators to false. + * + * @return this + * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators. + */ + public function loadDefaultDecorators() + { + if ($this->loadDefaultDecoratorsIsDisabled()) { + return $this; + } + + if (! isset($this->_disableLoadDefaultDecorators)) { + $decorators = $this->getDecorators(); + if (empty($decorators)) { + // Load Icinga Web 2's default element decorators + $this->addDecorators(Form::$defaultElementDecorators); + } + } else { + // Load Zend's default decorators + parent::loadDefaultDecorators(); + } + return $this; + } +} diff --git a/library/Icinga/Web/Form/Validator/DateTimeValidator.php b/library/Icinga/Web/Form/Validator/DateTimeValidator.php index f1952f7d4..6b59d5263 100644 --- a/library/Icinga/Web/Form/Validator/DateTimeValidator.php +++ b/library/Icinga/Web/Form/Validator/DateTimeValidator.php @@ -4,127 +4,52 @@ namespace Icinga\Web\Form\Validator; -use Icinga\Util\DateTimeFactory; +use DateTime; use Zend_Validate_Abstract; -use Icinga\Exception\ProgrammingError; /** - * Validator that checks if a textfield contains a correct date format + * Validator for date-and-time input controls + * + * @see \Icinga\Web\Form\Element\DateTimePicker For the date-and-time input control. */ class DateTimeValidator extends Zend_Validate_Abstract { - /** - * Array of allowed patterns for datetime input - * - * @var array - */ - private $patterns = array(); + protected $local; /** - * If the input is not a timestamp this contains the pattern that - * matched the input + * Create a new date-and-time input control validator * - * @var string|bool + * @param bool $local */ - private $validPattern = false; - - /** - * Error templates - * - * @var array - * - * @see Zend_Validate_Abstract::$_messageTemplates - */ - protected $_messageTemplates = array(); - - /** - * Create this validator - * - * @param array $patterns Array containing all allowed patterns as strings - */ - public function __construct(array $patterns) + public function __construct($local) { - $this->patterns = $patterns; - $this->_messageTemplates = array( - 'INVALID_TYPE' => 'Invalid type given. Date/time string or Unix timestamp expected', - 'NO_MATCHING_PATTERN' => 'Invalid format given, valid formats are ' . $this->getAllowedPatternList() - ); + $this->local = (bool) $local; } /** - * Check whether a variable is a Unix timestamp + * Is the date and time valid? + * + * @param string|DateTime $value + * @param mixed $context * - * @param mixed $timestamp * @return bool - */ - public static function isUnixTimestamp($timestamp) - { - return (is_int($timestamp) || ctype_digit($timestamp)) - && ($timestamp <= PHP_INT_MAX) - && ($timestamp >= ~PHP_INT_MAX); - } - - /** - * Returns a printable string containing all configured patterns * - * @return string - */ - private function getAllowedPatternList() - { - return '"' . join('","', $this->patterns) . '"'; - } - - /** - * Validate the input value and set the value of @see validPattern if the input machtes a pattern - * - * @param string $value The format string to validate - * @param null $context The form context (ignored) - * - * @return bool True when the input is valid, otherwise false - * - * @see Zend_Validate_Abstract::isValid() + * @see \Zend_Validate_Interface::isValid() */ public function isValid($value, $context = null) { - $this->validPattern = false; - if (!is_string($value) && !is_int($value)) { - $this->error('INVALID_TYPE'); + if (! $value instanceof DateTime && ! is_string($value)) { + $this->_error(t('Invalid type given. Instance of DateTime or date/time string expected')); return false; } - - if ($this->isUnixTimestamp($value)) { - $dt = DateTimeFactory::create(); - $dt->setTimestamp($value); - } else { - if (!isset($this->patterns)) { - throw new ProgrammingError('There are no allowed timeformats configured'); - } - - $match_found = false; - foreach ($this->patterns as $pattern) { - $dt = DateTimeFactory::parse($value, $pattern); - if ($dt !== false && $dt->format($pattern) === $value) { - $match_found = true; - $this->validPattern = $pattern; - break; - } - } - if (!$match_found) { - $this->_error('NO_MATCHING_PATTERN'); + if (is_string($value)) { + $format = $this->local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339; + $dateTime = DateTime::createFromFormat($format, $value); + if ($dateTime === false || $dateTime->format($format) !== $value) { + $this->_error(sprintf(t('Date/time string not in the expected format %s'), $format)); return false; } } - return true; } - - /** - * Return the matched pattern if any or false if input is a timestamp - * - * @return bool|string False if input was a timestamp otherwise string with the dateformat pattern - */ - public function getValidPattern() - { - return $this->validPattern; - } } diff --git a/library/Icinga/Web/Form/Validator/ReadablePathValidator.php b/library/Icinga/Web/Form/Validator/ReadablePathValidator.php new file mode 100644 index 000000000..4060f6ecd --- /dev/null +++ b/library/Icinga/Web/Form/Validator/ReadablePathValidator.php @@ -0,0 +1,58 @@ +_messageTemplates = array( + 'NOT_READABLE' => t('Path is not readable'), + 'DOES_NOT_EXIST' => t('Path does not exist') + ); + } + + /** + * Check whether the given value is a readable filepath + * + * @param string $value The value submitted in the form + * @param mixed $context The context of the form + * + * @return bool Whether the value was successfully validated + */ + public function isValid($value, $context = null) + { + if (false === file_exists($value)) { + $this->_error('DOES_NOT_EXIST'); + return false; + } + + if (false === is_readable($value)) { + $this->_error('NOT_READABLE'); + } + + return true; + } +} diff --git a/library/Icinga/Web/Hook.php b/library/Icinga/Web/Hook.php index 2e85f570e..36974fb5b 100644 --- a/library/Icinga/Web/Hook.php +++ b/library/Icinga/Web/Hook.php @@ -174,18 +174,6 @@ class Hook } } - /** - * Register your hook - * - * Alias for Hook::registerClass() - * - * @see Hook::registerClass() - */ - public static function register($name, $key, $class) - { - self::registerClass($name, $key, $class); - } - /** * Register a class * @@ -194,45 +182,12 @@ class Hook * @param string $class Your class name, must inherit one of the * classes in the Icinga/Web/Hook folder */ - public static function registerClass($name, $key, $class) + public static function register($name, $key, $class) { - if (!class_exists($class)) { - throw new ProgrammingError( - '"%s" is not an existing class', - $class - ); - } - if (!isset(self::$hooks[$name])) { self::$hooks[$name] = array(); } self::$hooks[$name][$key] = $class; } - - /** - * Register an object - * - * @param string $name One of the predefined hook names - * @param string $key The identifier of a specific subtype - * @param object $object The instantiated hook to register - * - * @throws ProgrammingError - */ - public static function registerObject($name, $key, $object) - { - if (!is_object($object)) { - throw new ProgrammingError( - '"%s" is not an instantiated class', - $object - ); - } - - if (!isset(self::$instances[$name])) { - self::$instances[$name] = array(); - } - - self::$instances[$name][$key] = $object; - self::registerClass($name, $key, get_class($object)); - } } diff --git a/library/Icinga/Web/JavaScript.php b/library/Icinga/Web/JavaScript.php index 01e6a55eb..6224b5b82 100644 --- a/library/Icinga/Web/JavaScript.php +++ b/library/Icinga/Web/JavaScript.php @@ -18,6 +18,7 @@ class JavaScript 'js/icinga/ui.js', 'js/icinga/timer.js', 'js/icinga/loader.js', + 'js/icinga/eventlistener.js', 'js/icinga/events.js', 'js/icinga/history.js', 'js/icinga/module.js', @@ -25,7 +26,8 @@ class JavaScript 'js/icinga/behavior/tooltip.js', 'js/icinga/behavior/sparkline.js', 'js/icinga/behavior/tristate.js', - 'js/icinga/behavior/navigation.js' + 'js/icinga/behavior/navigation.js', + 'js/icinga/behavior/form.js' ); protected static $vendorFiles = array( diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 33e0ea2a3..6ad4f6f87 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -17,7 +17,7 @@ class Response extends Zend_Controller_Response_Http $url->getParams()->setSeparator('&'); if (Icinga::app()->getFrontController()->getRequest()->isXmlHttpRequest()) { - $this->setHeader('X-Icinga-Redirect', rawurlencode($url)); + $this->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl())); } else { $this->setRedirect($url->getAbsoluteUrl()); } diff --git a/library/Icinga/Web/View.php b/library/Icinga/Web/View.php index 22e8e6fbd..7a954934a 100644 --- a/library/Icinga/Web/View.php +++ b/library/Icinga/Web/View.php @@ -127,9 +127,19 @@ class View extends Zend_View_Abstract ); } - public function translate($text) + public function translate($text, $context = null) { - return Translator::translate($text, $this->translationDomain); + return Translator::translate($text, $this->translationDomain, $context); + } + + /** + * Translate a plural string + * + * @see Translator::translatePlural() + */ + public function translatePlural($textSingular, $textPlural, $number, $context = null) + { + return Translator::translatePlural($textSingular, $textPlural, $number, $this->translationDomain, $context); } /** diff --git a/library/Icinga/Web/View/DateTimeRenderer.php b/library/Icinga/Web/View/DateTimeRenderer.php new file mode 100644 index 000000000..639befb7f --- /dev/null +++ b/library/Icinga/Web/View/DateTimeRenderer.php @@ -0,0 +1,205 @@ +future = $future; + $this->now = new DateTime(); + $this->dateTime = $this->normalize($dateTime); + $this->detectType(); + } + + /** + * Creates a new DateTimeRenderer + * + * @param DateTime|int $dateTime + * @param bool $future + * + * @return DateTimeRenderer + */ + public static function create($dateTime, $future = false) + { + return new static($dateTime, $future); + } + + /** + * Detects the DateTime context + */ + protected function detectType() + { + if ($this->now->format('Y-m-d') !== $this->dateTime->format('Y-m-d')) { + $this->type = self::TYPE_DATETIME; + return; + } + + if ( + $this->now->format('Y-m-d') === $this->dateTime->format('Y-m-d') && + (abs($this->now->getTimestamp() - $this->dateTime->getTimestamp()) >= self::HOUR) + ) { + $this->type = self::TYPE_TIME; + return; + } + + if ( + $this->now->format('Y-m-d') === $this->dateTime->format('Y-m-d') && + (abs($this->now->getTimestamp() - $this->dateTime->getTimestamp()) < self::HOUR) + ) { + $this->type = self::TYPE_TIMESPAN; + return; + } + } + + /** + * Normalizes the given DateTime + * + * @param DateTime|int $dateTime + * + * @return DateTime + */ + public static function normalize($dateTime) + { + if (! ($dt = $dateTime) instanceof DateTime) { + $dt = new DateTime(); + $dt->setTimestamp($dateTime); + } + return $dt; + } + + /** + * Checks whether DateTime is a date with time + * + * @return bool + */ + public function isDateTime() + { + return $this->type === self::TYPE_DATETIME; + } + + /** + * Checks whether DateTime is a time of the current day + * + * @return bool + */ + public function isTime() + { + return $this->type === self::TYPE_TIME; + } + + /** + * Checks whether DateTime is in a defined interval + * + * @return bool + */ + public function isTimespan() + { + return $this->type === self::TYPE_TIMESPAN; + } + + /** + * Returns the type of the DateTime + * + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * Renders the DateTime on the basis of the type and returns suited text + * + * @param string $dateTimeText + * @param string $timeText + * @param string $timespanText + * + * @return string + */ + public function render($dateTimeText, $timeText, $timespanText) + { + if ($this->isDateTime()) { + return sprintf($dateTimeText, $this); + } elseif ($this->isTime()) { + return sprintf($timeText, $this); + } elseif ($this->isTimespan()) { + return sprintf($timespanText, $this); + } + + return $dateTimeText; + } + + /** + * Returns a rendered html wrapped text + * + * @return string + */ + public function __toString() + { + switch ($this->type) { + case self::TYPE_DATETIME: + $format = $this->dateTime->format('d.m.Y - H:i:s'); + break; + case self::TYPE_TIME: + $format = $this->dateTime->format('H:i:s'); + break; + case self::TYPE_TIMESPAN: + $format = $this->dateTime->diff($this->now)->format(t('%im %ss', 'timespan')); + break; + default: + $format = $this->dateTime->format('d.m.Y - H:i:s'); + break; + } + + $css = ''; + if ($this->type === self::TYPE_TIMESPAN) { + $css = $this->future === true ? 'timeuntil' : 'timesince'; + } + + return sprintf( + '%s', + $css, + $this->dateTime->format('d.m.Y - H:i:s'), + $format + ); + } +} diff --git a/library/Icinga/Web/View/helpers/format.php b/library/Icinga/Web/View/helpers/format.php index 0d902acfd..a203f2fb3 100644 --- a/library/Icinga/Web/View/helpers/format.php +++ b/library/Icinga/Web/View/helpers/format.php @@ -44,3 +44,7 @@ $this->addHelperFunction('prefixedTimeUntil', function ($timestamp, $ucfirst = f Format::prefixedTimeUntil($timestamp, $ucfirst) ); }); + +$this->addHelperFunction('dateTimeRenderer', function ($dateTimeOrTimestamp, $future = false) { + return DateTimeRenderer::create($dateTimeOrTimestamp, $future); +}); diff --git a/library/Icinga/Web/Widget/AlertMessageBox.php b/library/Icinga/Web/Widget/AlertMessageBox.php deleted file mode 100644 index 09016f490..000000000 --- a/library/Icinga/Web/Widget/AlertMessageBox.php +++ /dev/null @@ -1,132 +0,0 @@ -user->getMessages(); - $this->user->clearMessages(); - Session::getSession()->write(); - return $messages; - } - - /** - * The displayed alert messages - * - * @var array - */ - private $messages = array(); - - /** - * The user to fetch the messages from - * - * @var User - */ - private $user; - - /** - * The available states. - * - * @var array - */ - private $states = array( - Zend_Log::INFO => array( - 'state' => 'alert-success', - 'icon' => 'success.png' - ), - Zend_Log::NOTICE => array( - 'state' => 'alert-info', - 'icon' => 'info.png' - ), - Zend_Log::WARN => array( - 'state' => 'alert-warning', - 'icon' => 'warning.png' - ), - Zend_Log::ERR => array( - 'state' => 'alert-danger', - 'icon' => 'error.png' - ) - ); - - /** - * Create a new AlertBox - * - * @param boolean showUserMessages If the current user messages should be displayed - * in this AlertMessageBox. Defaults to false - */ - public function __construct($showUserMessages = false) - { - if ($showUserMessages) { - $this->user = AuthenticationManager::getInstance()->getUser(); - } - } - - /** - * Add a new error - * - * @param $error - */ - public function addError($error) - { - $this->messages[] = new Message($error, Zend_Log::ERR); - } - - /** - * Add the error messages of the given Zend_Form - */ - public function addForm(Zend_Form $form) - { - foreach ($form->getErrorMessages() as $error) { - $this->addError($error); - } - } - - /** - * Output the HTML of the AlertBox - * - * @return string - */ - public function render(Zend_View_Abstract $view = null) - { - $html = ''; - if (isset($this->user)) { - $this->messages = array_merge($this->messages, $this->getAndClearMessages()); - } - foreach ($this->messages as $message) { - $level = $message->getLevel(); - if (!array_key_exists($level, $this->states)) { - continue; - } - $alert = $this->states[$level]; - $html .= '
' . - $this->view()->icon($alert['icon']) . - '' . htmlspecialchars($message->getMessage()) . '' . - '
'; - } - return $html; - } -} diff --git a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php index bb402009b..56de318e8 100644 --- a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php +++ b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php @@ -15,38 +15,45 @@ use DateInterval; */ class HistoryColorGrid extends AbstractWidget { - const ORIENTATION_VERTICAL = 'vertical'; + const CAL_GROW_INTO_PAST = 'past'; + const CAL_GROW_INTO_PRESENT = 'present'; + const ORIENTATION_VERTICAL = 'vertical'; const ORIENTATION_HORIZONTAL = 'horizontal'; + public $weekFlow = self::CAL_GROW_INTO_PAST; public $orientation = self::ORIENTATION_VERTICAL; + public $weekStartMonday = true; private $maxValue = 1; private $start = null; - private $end = null; - private $data = array(); - private $color; + public $opacity = 1.0; - public function __construct($color = '#51e551') { + public function __construct($color = '#51e551', $start = null, $end = null) { $this->setColor($color); + if (isset($start)) { + $this->start = $this->tsToDateStr($start); + } + if (isset($end)) { + $this->end = $this->tsToDateStr($end); + } } /** * Set the displayed data-set * - * @param $data array The values to display. - * properties for each entry: + * @param $events array The history events to display as an array of arrays: * value: The value to display * caption: The caption on mouse-over * url: The url to open on click. */ - public function setData(array $data) + public function setData(array $events) { - $this->data = $data; + $this->data = $events; $start = time(); $end = time(); foreach ($this->data as $entry) { @@ -64,8 +71,12 @@ class HistoryColorGrid extends AbstractWidget { $start = $time; } } - $this->start = $this->tsToDateStr($start); - $this->end = $this->tsToDateStr($end); + if (!isset($this->start)) { + $this->start = $this->tsToDateStr($start); + } + if (!isset($this->end)) { + $this->end = $this->tsToDateStr($end); + } } /** @@ -78,6 +89,16 @@ class HistoryColorGrid extends AbstractWidget { $this->color = $color; } + /** + * Set the used opacity + * + * @param $opacity + */ + public function setOpacity($opacity) + { + $this->opacity = $opacity; + } + /** * Calculate the color to display for the given value. * @@ -100,16 +121,17 @@ class HistoryColorGrid extends AbstractWidget { */ private function renderDay($day) { - if (array_key_exists($day, $this->data)) { + if (array_key_exists($day, $this->data) && $this->data[$day]['value'] > 0) { $entry = $this->data[$day]; return' '; } else { return ''; } @@ -126,13 +148,14 @@ class HistoryColorGrid extends AbstractWidget { { $weeks = $grid['weeks']; $months = $grid['months']; + $years = $grid['years']; $html = ''; $html .= ''; $old = -1; - foreach ($months as $month) { + foreach ($months as $week => $month) { if ($old !== $month) { $old = $month; - $txt = $this->monthName($month); + $txt = $this->monthName($month, $years[$week]); } else { $txt = ''; } @@ -153,12 +176,13 @@ class HistoryColorGrid extends AbstractWidget { */ private function renderVertical($grid) { + $years = $grid['years']; $weeks = $grid['weeks']; $months = $grid['months']; $html = '
'; $html .= ''; for ($i = 0; $i < 7; $i++) { - $html .= '"; + $html .= '"; } $html .= ''; $old = -1; @@ -172,7 +196,7 @@ class HistoryColorGrid extends AbstractWidget { } if ($old !== $months[$index]) { $old = $months[$index]; - $txt = $this->monthName($old); + $txt = $this->monthName($old, $years[$index]); } else { $txt = ''; } @@ -192,7 +216,9 @@ class HistoryColorGrid extends AbstractWidget { */ private function renderWeekdayHorizontal($weekday, &$weeks) { - $html = ''; + $html = ''; foreach ($weeks as $week) { if (array_key_exists($weekday, $week)) { $html .= ''; @@ -214,14 +240,20 @@ class HistoryColorGrid extends AbstractWidget { $weeks = array(array()); $week = 0; $months = array(); + $years = array(); $start = strtotime($this->start); $year = intval(date('Y', $start)); $month = intval(date('n', $start)); $day = intval(date('j', $start)); $weekday = intval(date('w', $start)); + if ($this->weekStartMonday) { + // 0 => monday, 6 => sunday + $weekday = $weekday === 0 ? 6 : $weekday - 1; + } $date = $this->toDateStr($day, $month, $year); $weeks[0][$weekday] = $date; + $years[0] = $year; $months[0] = $month; while ($date !== $this->end) { $day++; @@ -229,8 +261,12 @@ class HistoryColorGrid extends AbstractWidget { if ($weekday > 6) { $weekday = 0; $weeks[] = array(); + // PRESENT => The last day of week determines the month + if ($this->weekFlow === self::CAL_GROW_INTO_PRESENT) { + $months[$week] = $month; + $years[$week] = $year; + } $week++; - $months[$week] = $month; } if ($day > cal_days_in_month(CAL_GREGORIAN, $month, $year)) { $month++; @@ -240,13 +276,29 @@ class HistoryColorGrid extends AbstractWidget { } $day = 1; } + if ($weekday === 0) { + // PAST => The first day of each week determines the month + if ($this->weekFlow === self::CAL_GROW_INTO_PAST) { + $months[$week] = $month; + $years[$week] = $year; + } + } $date = $this->toDateStr($day, $month, $year); $weeks[$week][$weekday] = $date; }; + $years[$week] = $year; $months[$week] = $month; + if ($this->weekFlow == self::CAL_GROW_INTO_PAST) { + return array( + 'weeks' => array_reverse($weeks), + 'months' => array_reverse($months), + 'years' => array_reverse($years) + ); + } return array( 'weeks' => $weeks, - 'months' => $months + 'months' => $months, + 'years' => $years ); } @@ -257,9 +309,10 @@ class HistoryColorGrid extends AbstractWidget { * * @return string The */ - private function monthName($month) + private function monthName($month, $year) { - $dt = DateTimeFactory::create('2000-' . $month . '-01'); + // TODO: find a way to render years without messing up the layout + $dt = DateTimeFactory::create($year . '-' . $month . '-01'); return $dt->format('M'); } diff --git a/library/Icinga/Web/Widget/Chart/InlinePie.php b/library/Icinga/Web/Widget/Chart/InlinePie.php index da63ec403..4b4409e78 100644 --- a/library/Icinga/Web/Widget/Chart/InlinePie.php +++ b/library/Icinga/Web/Widget/Chart/InlinePie.php @@ -24,7 +24,7 @@ class InlinePie extends AbstractWidget const NUMBER_FORMAT_TIME = 'time'; const NUMBER_FORMAT_BYTES = 'bytes'; const NUMBER_FORMAT_RATIO = 'ratio'; - + /** * The template string used for rendering this widget * The template string used for rendering this widget @@ -137,7 +137,7 @@ EOD; * @var string */ private $tooltipFormat = '{{title}}
{{label}}: {{formatted}} ({{percent}}%)'; - + /** * The number format used to render numeric values in tooltips * @@ -148,30 +148,36 @@ EOD; /** * Set if the tooltip for the empty area should be hidden * - * @param bool $hide Whether to hide the empty area + * @param bool $hide Whether to hide the empty area + * + * @return $this */ public function setHideEmptyLabel($hide = true) { $this->hideEmptyLabel = $hide; + return $this; } /** * Set the data to be displayed. * - * @param $data array + * @param $data array + * + * @return $this */ public function setData(array $data) { $this->data = $data; $this->url->setParam('data', implode(',', $data)); + return $this; } /** * The labels to be displayed in the pie-chart * - * @param mixed $label The label of the displayed value, or null for no labels + * @param mixed $label The label of the displayed value, or null for no labels * - * @return $this Fluent interface + * @return $this */ public function setLabel($label) { @@ -191,7 +197,9 @@ EOD; /** * Set the colors used by the slices of the pie chart. * - * @param array $colors + * @param array $colors + * + * @return $this */ public function setColors(array $colors = null) { @@ -201,18 +209,22 @@ EOD; } else { $this->url->setParam('colors', null); } + return $this; } /** * Set the used number format * - * @param $format string 'bytes' or 'time' + * @param $format string 'bytes' or 'time' + * + * @return $this */ public function setNumberFormat($format) { $this->format = $format; + return $this; } - + /** * A format string used to render the content of the piechart tooltips * @@ -225,16 +237,23 @@ EOD; *
  • percent: The percentage of the current value
  • * * Note: Changes will only affect JavaScript sparklines and not the SVG charts used for fallback + * + * @param $format + * + * @return $this */ public function setTooltipFormat($format) { $this->tooltipFormat = $format; + return $this; } /** - * @param $height + * Set the height * - * @return $this + * @param $height + * + * @return $this */ public function setHeight($height) { @@ -245,17 +264,22 @@ EOD; /** * Set the border width of the pie chart * - * @param float $width Width in px + * @param float $width Width in px + * + * @return $this */ public function setBorderWidth($width) { $this->borderWidth = $width; + return $this; } /** * Set the color of the pie chart border * - * @param string $col The color string + * @param string $col The color string + * + * @return $this */ public function setBorderColor($col) { @@ -263,9 +287,11 @@ EOD; } /** - * @param $width + * Set the width * - * @return $this + * @param $width + * + * @return $this */ public function setWidth($width) { @@ -276,7 +302,9 @@ EOD; /** * Set the styling of the created HtmlElement * - * @param string $style + * @param string $style + * + * @return $this */ public function setStyle($style) { @@ -286,11 +314,14 @@ EOD; /** * Set the title of the displayed Data * - * @param string $title + * @param string $title + * + * @return $this */ public function setTitle($title) { $this->title = $title; + return $this; } /** @@ -346,7 +377,7 @@ EOD; { $template = $this->template; $template = str_replace('{url}', $this->url, $template); - + // style $template = str_replace('{width}', $this->width, $template); $template = str_replace('{height}', $this->height, $template); diff --git a/library/Icinga/Web/Widget/SortBox.php b/library/Icinga/Web/Widget/SortBox.php index cce268a48..42e7110cc 100644 --- a/library/Icinga/Web/Widget/SortBox.php +++ b/library/Icinga/Web/Widget/SortBox.php @@ -119,20 +119,19 @@ class SortBox extends AbstractWidget 'label' => 'Sort By', 'multiOptions' => $this->sortFields, 'style' => 'width: 12em', - 'class' => 'autosubmit', + 'autosubmit' => true )); $form->addElement('select', 'dir', array( 'multiOptions' => array( 'asc' => 'Asc', 'desc' => 'Desc', ), - 'style' => 'width: 5em', - 'class' => 'autosubmit' + 'style' => 'width: 5em', + 'autosubmit' => true )); $sort = $form->getElement('sort')->setDecorators(array('ViewHelper')); $dir = $form->getElement('dir')->setDecorators(array('ViewHelper')); if ($this->request) { - $form->setAction($this->request->getRequestUri()); $form->populate($this->request->getParams()); } return $form; diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index 716d5a959..b2a34e0ec 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -23,6 +23,7 @@ class Tabs extends AbstractWidget implements Countable EOT; @@ -40,6 +41,18 @@ EOT; EOT; + /** + * Template used for the close-button + * + * @var string + */ + private $closeTpl = <<< 'EOT' + +EOT; + + /** * This is where single tabs added to this container will be stored * @@ -61,6 +74,28 @@ EOT; */ private $dropdownTabs = array(); + /** + * Whether only the close-button should by rendered for this tab + * + * @var bool + */ + private $closeButtonOnly = false; + + /** + * Whether the tabs should contain a close-button + * + * @var bool + */ + private $closeTab = true; + + /** + * Set whether the current tab is closable + */ + public function hideCloseButton() + { + $this->closeTab = false; + } + /** * Activate the tab with the given name * @@ -235,6 +270,11 @@ EOT; return $tabs; } + private function renderCloseTab() + { + return $this->closeTpl; + } + /** * Render to HTML * @@ -242,13 +282,19 @@ EOT; */ public function render() { - if (empty($this->tabs)) { - return ''; + if (empty($this->tabs) || true === $this->closeButtonOnly) { + $tabs = ''; + $drop = ''; + } else { + $tabs = $this->renderTabs(); + $drop = $this->renderDropdownTabs(); } + $close = $this->closeTab ? $this->renderCloseTab() : ''; $html = $this->baseTpl; - $html = str_replace('{TABS}', $this->renderTabs(), $html); - $html = str_replace('{DROPDOWN}', $this->renderDropdownTabs(), $html); + $html = str_replace('{TABS}', $tabs, $html); + $html = str_replace('{DROPDOWN}', $drop, $html); + $html = str_replace('{CLOSE}', $close, $html); return $html; } @@ -284,6 +330,18 @@ EOT; return $this->tabs; } + /** + * Whether to hide all elements except of the close button + * + * @param bool $value + * @return Tabs fluent interface + */ + public function showOnlyCloseButton($value = true) + { + $this->closeButtonOnly = $value; + return $this; + } + /** * Apply a Tabextension on this tabs object * diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php new file mode 100644 index 000000000..02c8bb3db --- /dev/null +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -0,0 +1,626 @@ +getTabs(); + if (in_array($this->_request->getActionName(), array('alertsummary'))) { + $tabs->extend(new OutputFormat())->extend(new DashboardAction()); + } + + $this->url = Url::fromRequest(); + + $this->notificationData = $this->createNotificationData(); + $this->problemData = $this->createProblemData(); + } + + /** + * @param string $action + * @param bool $title + */ + protected function addTitleTab($action, $title = false) + { + $title = $title ?: ucfirst($action); + $this->getTabs()->add( + $action, + array( + 'title' => $title, + 'url' => $this->url + ) + )->activate($action); + $this->view->title = $title; + } + + /** + * Creat full report + */ + public function indexAction() + { + $this->addTitleTab('alertsummary'); + $this->view->intervalBox = $this->createIntervalBox(); + $this->view->recentAlerts = $this->createRecentAlerts(); + $this->view->interval = $this->getInterval(); + $this->view->defectChart = $this->createDefectImage(); + $this->view->healingChart = $this->createHealingChart(); + $this->view->perf = $this->createNotificationPerfdata(); + $this->view->trend = $this->createTrendInformation(); + + $this->setAutorefreshInterval(15); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); + + $this->view->notifications = $query->paginate(); + } + + /** + * Create data for charts + * + * @return array + */ + private function createNotificationData() + { + $interval = $this->getInterval(); + + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'asc'); + + $records = $query->getQuery()->fetchAll(); + $data = array(); + $period = $this->createPeriod($interval); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $data[$id] = array($id, 0); + } + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->notification_start_time); + if (empty($data[$id])) { + $data[$id] = array($id, 0); + } + + $data[$id][1]++; + } + + return $data; + } + + /** + * Trend information for notifications + * + * @return stdClass + */ + private function createTrendInformation() + { + $date = new DateTime(); + + $beginDate = $date->sub(new DateInterval('P3D')); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $beginDate->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'asc'); + + $records = $query->getQuery()->fetchAll(); + $slots = array(); + + $period = new DatePeriod($beginDate, new DateInterval('P1D'), 2, DatePeriod::EXCLUDE_START_DATE); + foreach ($period as $entry) { + $slots[$entry->format('Y-m-d')] = 0; + } + + foreach ($records as $item) { + $id = strftime('%Y-%m-%d', $item->notification_start_time); + if (isset($slots[$id])) { + $slots[$id]++; + } + } + + $yesterday = array_shift($slots); + $today = array_shift($slots); + + $out = new stdClass(); + if ($yesterday === $today) { + $out->trend = 'unchanged'; + } elseif ($yesterday > $today) { + $out->trend = 'down'; + } else { + $out->trend = 'up'; + } + + if ($yesterday <= 0) { + $out->percent = 100; + } elseif ($yesterday === $today) { + $out->percent = 0; + } else { + $out->percent = sprintf( + '%.2f', + 100 - ((100/($yesterday > $today ? $yesterday : $today)) * ($yesterday > $today ? $today : $yesterday)) + ); + } + + return $out; + } + + /** + * Perfdata for notifications + * + * @return stdClass + */ + private function createNotificationPerfdata() + { + $interval = $this->getInterval(); + + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'desc'); + + $records = $query->getQuery()->fetchAll(); + $slots = array(); + + foreach ($records as $item) { + $id = strftime('%Y-%m-%d %H:%I:00', $item->notification_start_time); + + if (empty($slots[$id])) { + $slots[$id] = 0; + } + + $slots[$id]++; + } + + $out = new stdClass(); + if (! empty($slots)) { + $out->avg = sprintf('%.2f', array_sum($slots) / count($slots)); + } else { + $out->avg = '0.0'; + } + $out->last = array_shift($slots); + + return $out; + } + + /** + * Problems for notifications + * + * @return array + */ + private function createProblemData() + { + $interval = $this->getInterval(); + + $query = $this->backend->select()->from( + 'EventHistory', + array( + 'host_name', + 'service_description', + 'object_type', + 'timestamp', + 'state', + 'attempt', + 'max_attempts', + 'output', + 'type', + 'host', + 'service', + 'service_host_name' + ) + ); + + $query->addFilter( + new Icinga\Data\Filter\FilterExpression( + 'timestamp', + '>=', + $this->getBeginDate($interval)->getTimestamp() + ) + ); + + $query->addFilter( + new Icinga\Data\Filter\FilterExpression( + 'state', + '>', + 0 + ) + ); + + $defects = array(); + $records = $query->getQuery()->fetchAll(); + $period = $this->createPeriod($interval); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $defects[$id] = array($id, 0); + } + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->timestamp); + if (empty($defects[$id])) { + $defects[$id] = array($id, 0); + } + $defects[$id][1]++; + } + + return $defects; + } + + /** + * Healing svg image + * + * @return GridChart + */ + public function createHealingChart() + { + $gridChart = new GridChart(); + + $gridChart->alignTopLeft(); + $gridChart->setAxisLabel('', t('Notifications')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0) + ->setYAxis(new LinearUnit(10)); + + $interval = $this->getInterval(); + + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_object_id', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state', + 'acknowledgement_entry_time' + ) + ); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'asc'); + + $records = $query->getQuery()->fetchAll(); + + $interval = $this->getInterval(); + $period = $this->createPeriod($interval); + $dAvg = array(); + $dMax = array(); + $notifications = array(); + $rData = array(); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $dMax[$id] = array($id, 0); + $dAvg[$id] = array($id, 0, 0); + $notifications[$id] = array($id, 0); + } + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->notification_start_time); + + if ($item->notification_state == '0' && isset($rData[$item->notification_object_id])) { + $rData[$item->notification_object_id]['recover'] = + $item->notification_start_time - $rData[$item->notification_object_id]['entry']; + } elseif ($item->notification_state !== '0') { + $recover = 0; + if ($item->acknowledgement_entry_time) { + $recover = $item->acknowledgement_entry_time - $item->notification_start_time; + } + $rData[$item->notification_object_id] = array( + 'id' => $id, + 'entry' => $item->notification_start_time, + 'recover' => $recover + ); + } + } + + foreach ($rData as $item) { + $notifications[$item['id']][1]++; + + if ($item['recover'] > $dMax[$item['id']][1]) { + $dMax[$item['id']][1] = (int) $item['recover']; + } + + $dAvg[$item['id']][1] += (int) $item['recover']; + $dAvg[$item['id']][2]++; + } + + foreach ($dAvg as &$item) { + if ($item[2] > 0) { + $item[1] = ($item[1]/$item[2])/60/60; + } + } + + foreach ($dMax as &$item) { + $item[1] = $item[1]/60/60; + } + + + $gridChart->drawBars( + array( + 'label' => $this->translate('Notifications'), + 'color' => 'blue', + 'data' => $notifications, + 'showPoints' => true + ) + ); + + $gridChart->drawLines( + array( + 'label' => $this->translate('Avg (min)'), + 'color' => 'orange', + 'data' => $dAvg, + 'showPoints' => true + ) + ); + + $gridChart->drawLines( + array( + 'label' => $this->translate('Max (min)'), + 'color' => 'red', + 'data' => $dMax, + 'showPoints' => true + ) + ); + + return $gridChart; + } + + /** + * Notifications and defects + * + * @return GridChart + */ + public function createDefectImage() + { + $gridChart = new GridChart(); + + $gridChart->alignTopLeft(); + $gridChart->setAxisLabel('', t('Notifications')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0) + ->setYAxis(new LinearUnit(10)); + + $gridChart->drawBars( + array( + 'label' => $this->translate('Notifications'), + 'color' => 'green', + 'data' => $this->notificationData, + 'showPoints' => true + ) + ); + + $gridChart->drawLines( + array( + 'label' => $this->translate('Defects'), + 'color' => 'red', + 'data' => $this->problemData, + 'showPoints' => true + ) + ); + + return $gridChart; + } + + /** + * Top recent alerts + * + * @return mixed + */ + private function createRecentAlerts() + { + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); + + $query->order('notification_start_time', 'desc'); + + return $query->paginate(5); + } + + /** + * Interval selector box + * + * @return SelectBox + */ + private function createIntervalBox() + { + $box = new SelectBox( + 'intervalBox', + array( + '1d' => t('One day'), + '1w' => t('One week'), + '1m' => t('One month'), + '1y' => t('One year') + ), + t('Report interval'), + 'interval' + ); + $box->applyRequest($this->getRequest()); + return $box; + } + + /** + * Return reasonable date time format for an interval + * + * @param string $interval + * @param string $timestamp + * + * @return string + */ + private function getPeriodFormat($interval, $timestamp) + { + $format = ''; + if ($interval === '1d') { + $format = '%H:00:00'; + } elseif ($interval === '1w') { + $format = '%Y-%m-%d'; + } elseif ($interval === '1m') { + $format = '%Y-%m-%d'; + } elseif ($interval === '1y') { + $format = '%Y-%m'; + } + + return strftime($format, $timestamp); + } + + /** + * Create a reasonable period based in interval strings + * + * @param $interval + * @return DatePeriod + */ + private function createPeriod($interval) + { + if ($interval === '1d') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('PT1H'), 24); + } elseif ($interval === '1w') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1D'), 7); + } elseif ($interval === '1m') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1D'), 30); + } elseif ($interval === '1y') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1M'), 12); + } + } + + /** + * Return start timestamps based on interval strings + * + * @param $interval + * @return DateTime|null + */ + private function getBeginDate($interval) + { + $new = new DateTime(); + if ($interval === '1d') { + return $new->sub(new DateInterval('P1D')); + } elseif ($interval === '1w') { + return $new->sub(new DateInterval('P1W')); + } elseif ($interval === '1m') { + return $new->sub(new DateInterval('P1M')); + } elseif ($interval === '1y') { + return $new->sub(new DateInterval('P1Y')); + } + + return null; + } + + /** + * Getter for interval + * + * @return string + * + * @throws Zend_Controller_Action_Exception + */ + private function getInterval() + { + $interval = $this->getParam('interval', '1d'); + if (false === in_array($interval, array('1d', '1w', '1m', '1y'))) { + throw new Zend_Controller_Action_Exception($this->translate('Value for interval not valid')); + } + + return $interval; + } +} diff --git a/modules/monitoring/application/controllers/ChartController.php b/modules/monitoring/application/controllers/ChartController.php index bc31a90f9..0fffcbab7 100644 --- a/modules/monitoring/application/controllers/ChartController.php +++ b/modules/monitoring/application/controllers/ChartController.php @@ -2,15 +2,8 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Application\Icinga; -use Icinga\Application\Config; -use Icinga\Logger\Logger; -use Icinga\Web\Form; use Icinga\Module\Monitoring\Controller; -use Icinga\Chart\SVGRenderer; use Icinga\Chart\GridChart; -use Icinga\Chart\Palette; -use Icinga\Chart\Axis; use Icinga\Chart\PieChart; use Icinga\Chart\Unit\StaticAxis; @@ -151,7 +144,7 @@ class Monitoring_ChartController extends Controller $this->view->chart = new GridChart(); $this->view->chart->alignTopLeft(); $this->view->chart->setAxisLabel('', t('Services')) - ->setXAxis(new \Icinga\Chart\Unit\StaticAxis()) + ->setXAxis(new StaticAxis()) ->setAxisMin(null, 0); $tooltip = t('{title}:
    {value} of {sum} services are {label}'); @@ -281,7 +274,7 @@ class Monitoring_ChartController extends Controller ), 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#E066FF', '#f099FF', '#fefefe'), 'labels'=> array( - (int) $query->hosts_up . ' Up Hosts', + (int) $query->hosts_up . t(' Up Hosts'), (int) $query->hosts_down_handled . t(' Down Hosts (Handled)'), (int) $query->hosts_down_unhandled . t(' Down Hosts (Unhandled)'), (int) $query->hosts_unreachable_handled . t(' Unreachable Hosts (Handled)'), @@ -301,7 +294,7 @@ class Monitoring_ChartController extends Controller ), 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#ffff00', '#ffff33', '#E066FF', '#f099FF', '#fefefe'), 'labels'=> array( - $query->services_ok . ' Up Services', + $query->services_ok . t(' Up Services'), $query->services_warning_handled . t(' Warning Services (Handled)'), $query->services_warning_unhandled . t(' Warning Services (Unhandled)'), $query->services_critical_handled . t(' Down Services (Handled)'), diff --git a/modules/monitoring/application/controllers/CommandController.php b/modules/monitoring/application/controllers/CommandController.php index 36d3d62ca..1aae9de20 100644 --- a/modules/monitoring/application/controllers/CommandController.php +++ b/modules/monitoring/application/controllers/CommandController.php @@ -115,14 +115,17 @@ class Monitoring_CommandController extends Controller if ($targetConfig->get($instance)) { $this->target = new CommandPipe($targetConfig->get($instance)); } else { - throw new ConfigurationError('Instance is not configured: %s', $instance); + throw new ConfigurationError( + $this->translate('Instance is not configured: %s'), + $instance + ); } } else { if ($targetConfig && $targetInfo = $targetConfig->current()) { // Take the very first section $this->target = new CommandPipe($targetInfo); } else { - throw new ConfigurationError('No instances are configured yet'); + throw new ConfigurationError($this->translate('No instances are configured yet')); } } } diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php index e5ba316d0..de9c65b04 100644 --- a/modules/monitoring/application/controllers/ConfigController.php +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -2,42 +2,27 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Config\PreservingIniWriter; -use Icinga\Web\Controller\ModuleActionController; use Icinga\Web\Notification; -use Icinga\Module\Monitoring\Form\Config\ConfirmRemovalForm; -use Icinga\Module\Monitoring\Form\Config\Backend\EditBackendForm; -use Icinga\Module\Monitoring\Form\Config\Backend\CreateBackendForm; -use Icinga\Module\Monitoring\Form\Config\Instance\EditInstanceForm; -use Icinga\Module\Monitoring\Form\Config\Instance\CreateInstanceForm; -use Icinga\Module\Monitoring\Form\Config\SecurityForm; - -use Icinga\Exception\NotReadableError; +use Icinga\Data\ResourceFactory; +use Icinga\Form\ConfirmRemovalForm; +use Icinga\Web\Controller\ModuleActionController; +use Icinga\Module\Monitoring\Form\Config\BackendConfigForm; +use Icinga\Module\Monitoring\Form\Config\InstanceConfigForm; +use Icinga\Module\Monitoring\Form\Config\SecurityConfigForm; /** * Configuration controller for editing monitoring resources */ class Monitoring_ConfigController extends ModuleActionController { - /** * Display a list of available backends and instances */ public function indexAction() { + $this->view->backendsConfig = $this->Config('backends'); + $this->view->instancesConfig = $this->Config('instances'); $this->view->tabs = $this->Module()->getConfigTabs()->activate('backends'); - foreach (array('backends', 'instances') as $element) { - try { - $elementConfig = $this->Config($element); - if ($elementConfig === null) { - $this->view->{$element} = array(); - } else { - $this->view->{$element} = $elementConfig->toArray(); - } - } catch (NotReadableError $e) { - $this->view->{$element} = $e; - } - } } /** @@ -45,52 +30,27 @@ class Monitoring_ConfigController extends ModuleActionController */ public function editbackendAction() { - $backend = $this->getParam('backend'); - if (!$this->isExistingBackend($backend)) { - $this->view->error = 'Unknown backend ' . $backend; - return; - } - $backendForm = new EditBackendForm(); - $backendForm->setRequest($this->getRequest()); - $backendForm->setBackendConfiguration($this->Config('backends')->get($backend)); + $form = new BackendConfigForm(); + $form->setIniConfig($this->Config('backends')); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); - if ($backendForm->isSubmittedAndValid()) { - $newConfig = $backendForm->getConfig(); - $config = $this->Config('backends'); - $config->$backend = $newConfig; - if ($this->writeConfiguration($config, 'backends')) { - Notification::success('Backend ' . $backend . ' Modified.'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); - return; - } - } - $this->view->name = $backend; - $this->view->form = $backendForm; + $this->view->form = $form; } /** - * Display a form to create a new backends + * Display a form to create a new backend */ public function createbackendAction() { - $form = new CreateBackendForm(); - $form->setRequest($this->getRequest()); - if ($form->isSubmittedAndValid()) { - $configArray = $this->Config('backends')->toArray(); - $configArray[$form->getBackendName()] = $form->getConfig(); + $form = new BackendConfigForm(); + $form->setIniConfig($this->Config('backends')); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); - if ($this->writeConfiguration(new Zend_Config($configArray), 'backends')) { - Notification::success('Backend Creation Succeeded'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); - } - return; - } $this->view->form = $form; - $this->render('editbackend'); } /** @@ -98,62 +58,63 @@ class Monitoring_ConfigController extends ModuleActionController */ public function removebackendAction() { - $backend = $this->getParam('backend'); - if (!$this->isExistingBackend($backend)) { - $this->view->error = 'Unknown backend ' . $backend; - return; - } - $form = new ConfirmRemovalForm(); - $form->setRequest($this->getRequest()); - $form->setRemoveTarget('backend', $backend); + $config = $this->Config('backends'); + $form = new ConfirmRemovalForm(array( + 'onSuccess' => function ($request) use ($config) { + $backendName = $request->getQuery('backend'); + $configForm = new BackendConfigForm(); + $configForm->setIniConfig($config); - if ($form->isSubmittedAndValid()) { - $configArray = $this->Config('backends')->toArray(); - unset($configArray[$backend]); + try { + $configForm->remove($backendName); + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } - if ($this->writeConfiguration(new Zend_Config($configArray), 'backends')) { - Notification::success('Backend "' . $backend . '" Removed'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); + if ($configForm->save()) { + Notification::success(sprintf(t('Backend "%s" successfully removed.'), $backendName)); + } else { + return false; + } } - return; - } + )); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); $this->view->form = $form; - $this->view->name = $backend; } /** - * Display a form to remove the instance identified by the 'instance' parameter + * Display a confirmation form to remove the instance identified by the 'instance' parameter */ public function removeinstanceAction() { - $instance = $this->getParam('instance'); - if (!$this->isExistingInstance($instance)) { - $this->view->error = 'Unknown instance ' . $instance; - return; - } + $config = $this->Config('instances'); + $form = new ConfirmRemovalForm(array( + 'onSuccess' => function ($request) use ($config) { + $instanceName = $request->getQuery('instance'); + $configForm = new InstanceConfigForm(); + $configForm->setIniConfig($config); - $form = new ConfirmRemovalForm(); - $form->setRequest($this->getRequest()); - $form->setRemoveTarget('instance', $instance); + try { + $configForm->remove($instanceName); + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } - if ($form->isSubmittedAndValid()) { - $configArray = $this->Config('instances')->toArray(); - unset($configArray[$instance]); - - if ($this->writeConfiguration(new Zend_Config($configArray), 'instances')) { - Notification::success('Instance "' . $instance . '" Removed'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); + if ($configForm->save()) { + Notification::success(sprintf(t('Instance "%s" successfully removed.'), $instanceName)); + } else { + return false; + } } - return; - } + )); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); $this->view->form = $form; - $this->view->name = $instance; } /** @@ -161,25 +122,11 @@ class Monitoring_ConfigController extends ModuleActionController */ public function editinstanceAction() { - $instance = $this->getParam('instance'); - if (!$this->isExistingInstance($instance)) { - $this->view->error = 'Unknown instance ' . htmlentities($instance); - return; - } - $form = new EditInstanceForm(); - $form->setInstanceConfiguration($this->Config('instances')->get($instance)); - $form->setRequest($this->getRequest()); - if ($form->isSubmittedAndValid()) { - $instanceConfig = $this->Config('instances')->toArray(); - $instanceConfig[$instance] = $form->getConfig(); - if ($this->writeConfiguration(new Zend_Config($instanceConfig), 'instances')) { - Notification::success('Instance Modified'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); - return; - } - } + $form = new InstanceConfigForm(); + $form->setIniConfig($this->Config('instances')); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); + $this->view->form = $form; } @@ -188,92 +135,24 @@ class Monitoring_ConfigController extends ModuleActionController */ public function createinstanceAction() { - $form = new CreateInstanceForm(); - $form->setRequest($this->getRequest()); - if ($form->isSubmittedAndValid()) { - $instanceConfig = $this->Config('instances'); - if ($instanceConfig === null) { - $instanceConfig = array(); - } else { - $instanceConfig = $instanceConfig->toArray(); - } - $instanceConfig[$form->getInstanceName()] = $form->getConfig()->toArray(); - if ($this->writeConfiguration(new Zend_Config($instanceConfig), 'instances')) { - Notification::success('Instance Creation Succeeded'); - $this->redirectNow('monitoring/config'); - } else { - $this->render('show-configuration'); - } - return; - } + $form = new InstanceConfigForm(); + $form->setIniConfig($this->Config('instances')); + $form->setRedirectUrl('monitoring/config'); + $form->handleRequest(); + $this->view->form = $form; - $this->render('editinstance'); } /** - * Display a form to remove the instance identified by the 'instance' parameter + * Display a form to adjust security relevant settings */ - private function writeConfiguration($config, $file = null) - { - $target = $this->Config($file)->getConfigFile(); - $writer = new PreservingIniWriter(array('filename' => $target, 'config' => $config)); - - try { - $writer->write(); - } catch (Exception $exc) { - $this->view->exceptionMessage = $exc->getMessage(); - $this->view->iniConfigurationString = $writer->render(); - $this->view->file = $target; - return false; - } - - return true; - } - - /** - * Return true if the backend exists in the current configuration - * - * @param string $backend The name of the backend to check for existence - * - * @return bool True if the backend name exists, otherwise false - */ - private function isExistingBackend($backend) - { - $backendCfg = $this->Config('backends'); - return $backend && $backendCfg->get($backend); - } - - /** - * Return true if the instance exists in the current configuration - * - * @param string $instance The name of the instance to check for existence - * - * @return bool True if the instance name exists, otherwise false - */ - private function isExistingInstance($instance) - { - $instanceCfg = $this->Config('instances'); - return $instanceCfg && $instanceCfg->get($instance); - } - public function securityAction() { - $this->view->tabs = $this->Module()->getConfigTabs()->activate('security'); + $form = new SecurityConfigForm(); + $form->setIniConfig($this->Config()); + $form->handleRequest(); - $form = new SecurityForm(); - $form->setConfiguration($this->Config()->get('security')); - $form->setRequest($this->getRequest()); - if ($form->isSubmittedAndValid()) { - $config = $this->Config()->toArray(); - $config['security'] = $form->getConfig(); - if ($this->writeConfiguration(new Zend_Config($config))) { - Notification::success('Configuration modified successfully'); - $this->redirectNow('monitoring/config/security'); - } else { - $this->render('show-configuration'); - return; - } - } $this->view->form = $form; + $this->view->tabs = $this->Module()->getConfigTabs()->activate('security'); } } diff --git a/modules/monitoring/application/controllers/HostController.php b/modules/monitoring/application/controllers/HostController.php new file mode 100644 index 000000000..f77d11deb --- /dev/null +++ b/modules/monitoring/application/controllers/HostController.php @@ -0,0 +1,79 @@ +backend, $this->params->get('host')); + if ($host->fetch() === false) { + throw new Zend_Controller_Action_Exception($this->translate('Host not found')); + } + $this->object = $host; + $this->createTabs(); + } + + /** + * Show a host + */ + public function showAction() + { + $this->getTabs()->activate('host'); + parent::showAction(); + } + + /** + * Acknowledge a host problem + */ + public function acknowledgeProblemAction() + { + $this->view->title = $this->translate('Acknowledge Host Problem'); + $this->handleCommandForm(new AcknowledgeProblemCommandForm()); + } + + /** + * Add a host comment + */ + public function addCommentAction() + { + $this->view->title = $this->translate('Add Host Comment'); + $this->handleCommandForm(new AddCommentCommandForm()); + } + + /** + * Reschedule a host check + */ + public function rescheduleCheckAction() + { + $this->view->title = $this->translate('Reschedule Host Check'); + $this->handleCommandForm(new ScheduleHostCheckCommandForm()); + } + + /** + * Schedule a host downtime + */ + public function scheduleDowntimeAction() + { + $this->view->title = $this->translate('Schedule Host Downtime'); + $this->handleCommandForm(new ScheduleHostDowntimeCommandForm()); + } +} diff --git a/modules/monitoring/application/controllers/HostsController.php b/modules/monitoring/application/controllers/HostsController.php new file mode 100644 index 000000000..48b068705 --- /dev/null +++ b/modules/monitoring/application/controllers/HostsController.php @@ -0,0 +1,164 @@ +backend); + $hostList->setFilter(Filter::fromQueryString((string) $this->params)); + $this->hostList = $hostList; + } + + protected function handleCommandForm(ObjectsCommandForm $form) + { + $form + ->setObjects($this->hostList) + ->setRedirectUrl(Url::fromPath('monitoring/hosts/show')->setParams($this->params)) + ->handleRequest(); + $this->view->form = $form; + $this->_helper->viewRenderer('partials/command-form', null, true); + return $form; + } + + public function showAction() + { + $this->getTabs()->add( + 'show', + array( + 'title' => t('Hosts'), + 'url' => Url::fromRequest() + ) + )->activate('show'); + $this->setAutorefreshInterval(15); + $checkNowForm = new CheckNowCommandForm(); + $checkNowForm + ->setObjects($this->hostList) + ->handleRequest(); + $this->view->checkNowForm = $checkNowForm; + $this->hostList->setColumns(array( + 'host_name', + 'host_state', + 'host_problem', + 'host_handled', + 'host_acknowledged', + 'host_in_downtime'/*, + 'host_passive_checks_enabled', + 'host_notifications_enabled', + 'host_event_handler_enabled', + 'host_flap_detection_enabled', + 'host_active_checks_enabled', + 'host_obsessing'*/ + )); + $unhandledObjects = array(); + $acknowledgedObjects = array(); + $objectsInDowntime = array(); + $hostStates = array( + Host::getStateText(Host::STATE_UP) => 0, + Host::getStateText(Host::STATE_DOWN) => 0, + Host::getStateText(Host::STATE_UNREACHABLE) => 0, + Host::getStateText(Host::STATE_PENDING) => 0, + ); + foreach ($this->hostList as $host) { + /** @var Service $host */ + if ((bool) $host->problem === true && (bool) $host->handled === false) { + $unhandledObjects[] = $host; + } + if ((bool) $host->acknowledged === true) { + $acknowledgedObjects[] = $host; + } + if ((bool) $host->in_downtime === true) { + $objectsInDowntime[] = $host; + } + ++$hostStates[$host::getStateText($host->state)]; + } + if (! empty($acknowledgedObjects)) { + $removeAckForm = new RemoveAcknowledgementCommandForm(); + $removeAckForm + ->setObjects($acknowledgedObjects) + ->handleRequest(); + $this->view->removeAckForm = $removeAckForm; + } + $this->setAutorefreshInterval(15); + $this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/hosts'); + $this->view->rescheduleAllLink = Url::fromRequest()->setPath('monitoring/hosts/reschedule-check'); + $this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/hosts/schedule-downtime'); + $this->view->hostStates = $hostStates; + $this->view->objects = $this->hostList; + $this->view->unhandledObjects = $unhandledObjects; + $this->view->acknowledgeUnhandledLink = Url::fromRequest() + ->setPath('monitoring/hosts/acknowledge-problem') + ->addParams(array('host_problem' => 1, 'host_handled' => 0)); + $this->view->downtimeUnhandledLink = Url::fromRequest() + ->setPath('monitoring/hosts/schedule-downtime') + ->addParams(array('host_problem' => 1, 'host_handled' => 0)); + $this->view->acknowledgedObjects = $acknowledgedObjects; + $this->view->objectsInDowntime = $objectsInDowntime; + $this->view->inDowntimeLink = Url::fromRequest() + ->setPath('monitoring/list/downtimes'); + $this->view->havingCommentsLink = Url::fromRequest() + ->setPath('monitoring/list/comments'); + $this->view->hostStatesPieChart = $this->createPieChart( + $hostStates, + $this->translate('Host State'), + array('#44bb77', '#FF5566', '#E066FF', '#77AAFF') + ); + } + + protected function createPieChart(array $states, $title, array $colors) + { + $chart = new InlinePie(array_values($states), $title, $colors); + return $chart + ->setLabel(array_map('strtoupper', array_keys($states))) + ->setHeight(100) + ->setWidth(100) + ->setTitle($title); + } + + /** + * Acknowledge host problems + */ + public function acknowledgeProblemAction() + { + $this->view->title = $this->translate('Acknowledge Host Problems'); + $this->handleCommandForm(new AcknowledgeProblemCommandForm()); + } + + /** + * Reschedule host checks + */ + public function rescheduleCheckAction() + { + $this->view->title = $this->translate('Reschedule Host Checks'); + $this->handleCommandForm(new ScheduleHostCheckCommandForm()); + } + + /** + * Schedule host downtimes + */ + public function scheduleDowntimeAction() + { + $this->view->title = $this->translate('Schedule Host Downtimes'); + $this->handleCommandForm(new ScheduleHostDowntimeCommandForm()); + } +} diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index bd0bb047f..1c8f671a0 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -4,6 +4,8 @@ use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Form\Command\Object\DeleteCommentCommandForm; +use Icinga\Module\Monitoring\Form\Command\Object\DeleteDowntimeCommandForm; use Icinga\Web\Url; use Icinga\Web\Hook; use Icinga\Web\Widget\Tabextension\DashboardAction; @@ -14,6 +16,8 @@ use Icinga\Web\Widget\FilterBox; use Icinga\Web\Widget\Chart\HistoryColorGrid; use Icinga\Data\Filter\Filter; use Icinga\Web\Widget; +use Icinga\Module\Monitoring\Web\Widget\SelectBox; +use Icinga\Module\Monitoring\Form\StatehistoryForm; class Monitoring_ListController extends Controller { @@ -28,6 +32,10 @@ class Monitoring_ListController extends Controller { $this->createTabs(); $this->view->compact = $this->_request->getParam('view') === 'compact'; + if ($this->_request->getParam('view') === 'inline') { + $this->view->compact = true; + $this->view->inline = true; + } $this->url = Url::fromRequest(); } @@ -139,12 +147,12 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->setupSortControl(array( - 'host_last_check' => 'Last Check', - 'host_severity' => 'Severity', - 'host_name' => 'Hostname', - 'host_address' => 'Address', - 'host_state' => 'Current State', - 'host_state' => 'Hard State' + 'host_last_check' => $this->translate('Last Check'), + 'host_severity' => $this->translate('Severity'), + 'host_name' => $this->translate('Hostname'), + 'host_address' => $this->translate('Address'), + 'host_state' => $this->translate('Current State'), + 'host_state' => $this->translate('Hard State') )); $this->view->hosts = $query->paginate(); } @@ -216,16 +224,16 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->setupSortControl(array( - 'service_last_check' => 'Last Service Check', - 'service_severity' => 'Severity', - 'service_state' => 'Current Service State', - 'service_description' => 'Service Name', - 'service_state_type' => 'Hard State', - 'host_severity' => 'Host Severity', - 'host_state' => 'Current Host State', - 'host_name' => 'Host Name', - 'host_address' => 'Host Address', - 'host_last_check' => 'Last Host Check' + 'service_last_check' => $this->translate('Last Service Check'), + 'service_severity' => $this->translate('Severity'), + 'service_state' => $this->translate('Current Service State'), + 'service_description' => $this->translate('Service Name'), + 'service_state_type' => $this->translate('Hard State'), + 'host_severity' => $this->translate('Host Severity'), + 'host_state' => $this->translate('Current Host State'), + 'host_name' => $this->translate('Host Name'), + 'host_address' => $this->translate('Host Address'), + 'host_last_check' => $this->translate('Last Host Check') )); $limit = $this->params->get('limit'); $this->view->limit = $limit; @@ -261,6 +269,9 @@ class Monitoring_ListController extends Controller */ public function downtimesAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('downtimes'); $this->setAutorefreshInterval(12); $query = $this->backend->select()->from('downtime', array( @@ -287,16 +298,17 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->view->downtimes = $query->paginate(); $this->setupSortControl(array( - 'downtime_is_in_effect' => 'Is In Effect', - 'downtime_host' => 'Host / Service', - 'downtime_entry_time' => 'Entry Time', - 'downtime_author' => 'Author', - 'downtime_start' => 'Start Time', - 'downtime_start' => 'End Time', - 'downtime_scheduled_start' => 'Scheduled Start', - 'downtime_scheduled_end' => 'Scheduled End', - 'downtime_duration' => 'Duration', + 'downtime_is_in_effect' => $this->translate('Is In Effect'), + 'downtime_host' => $this->translate('Host / Service'), + 'downtime_entry_time' => $this->translate('Entry Time'), + 'downtime_author' => $this->translate('Author'), + 'downtime_start' => $this->translate('Start Time'), + 'downtime_start' => $this->translate('End Time'), + 'downtime_scheduled_start' => $this->translate('Scheduled Start'), + 'downtime_scheduled_end' => $this->translate('Scheduled End'), + 'downtime_duration' => $this->translate('Duration'), )); + $this->view->delDowntimeForm = new DeleteDowntimeCommandForm(); } /** @@ -304,6 +316,9 @@ class Monitoring_ListController extends Controller */ public function notificationsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('notifications'); $this->setAutorefreshInterval(15); $query = $this->backend->select()->from('notification', array( @@ -317,12 +332,15 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->view->notifications = $query->paginate(); $this->setupSortControl(array( - 'notification_start_time' => 'Notification Start' + 'notification_start_time' => $this->translate('Notification Start') )); } public function contactsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('contacts'); $query = $this->backend->select()->from('contact', array( 'contact_name', @@ -348,30 +366,60 @@ class Monitoring_ListController extends Controller $this->view->contacts = $query->paginate(); $this->setupSortControl(array( - 'contact_name' => 'Name', - 'contact_alias' => 'Alias', - 'contact_email' => 'Email', - 'contact_pager' => 'Pager Address / Number', - 'contact_notify_service_timeperiod' => 'Service Notification Timeperiod', - 'contact_notify_host_timeperiod' => 'Host Notification Timeperiod' + 'contact_name' => $this->translate('Name'), + 'contact_alias' => $this->translate('Alias'), + 'contact_email' => $this->translate('Email'), + 'contact_pager' => $this->translate('Pager Address / Number'), + 'contact_notify_service_timeperiod' => $this->translate('Service Notification Timeperiod'), + 'contact_notify_host_timeperiod' => $this->translate('Host Notification Timeperiod') )); } public function statehistorysummaryAction() { - $this->addTitleTab('statehistorysummary', 'Critical Events'); + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } + $this->addTitleTab('statehistorysummary', 'State Summary'); + + $form = new StatehistoryForm(); + $form->setEnctype(Zend_Form::ENCTYPE_URLENCODED); + $form->setMethod('get'); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->render(); + $this->view->form = $form; + + $orientation = $this->params->shift('horizontal', 0) ? 'horizontal' : 'vertical'; + + $orientationBox = new SelectBox( + 'orientation', + array( + '0' => t('Vertical'), + '1' => t('Horizontal') + ), + t('Orientation'), + 'horizontal' + ); + $orientationBox->applyRequest($this->getRequest()); + $query = $this->backend->select()->from( 'stateHistorySummary', - array('day', 'cnt_critical') - )->getQuery()->order('day'); - $query->limit(365); - $this->view->summary = $query->fetchAll(); - $this->view->grid = new HistoryColorGrid(); - $this->handleFormatRequest($query); + array('day', $form->getValue('state')) + ); + $this->params->remove(array('objecttype', 'from', 'to', 'state', 'btn_submit')); + $this->applyFilters($query); + $this->view->summary = $query->getQuery()->fetchAll(); + $this->view->column = $form->getValue('state'); + $this->view->orientationBox = $orientationBox; + $this->view->orientation = $orientation; } public function contactgroupsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('contactgroups'); $query = $this->backend->select()->from('contactgroup', array( 'contactgroup_name', @@ -401,6 +449,9 @@ class Monitoring_ListController extends Controller public function commentsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('comments'); $this->setAutorefreshInterval(12); $query = $this->backend->select()->from('comment', array( @@ -420,12 +471,13 @@ class Monitoring_ListController extends Controller $this->setupSortControl( array( - 'comment_timestamp' => 'Comment Timestamp', - 'comment_host' => 'Host / Service', - 'comment_type' => 'Comment Type', - 'comment_expiration' => 'Expiration', + 'comment_timestamp' => $this->translate('Comment Timestamp'), + 'comment_host' => $this->translate('Host / Service'), + 'comment_type' => $this->translate('Comment Type'), + 'comment_expiration' => $this->translate('Expiration'), ) ); + $this->view->delCommentForm = new DeleteCommentCommandForm(); } public function servicegroupsAction() @@ -455,7 +507,7 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->view->servicegroups = $query->paginate(); $this->setupSortControl(array( - 'servicegroup_name' => 'Servicegroup Name' + 'servicegroup_name' => $this->translate('Servicegroup Name') )); } @@ -486,12 +538,16 @@ class Monitoring_ListController extends Controller $this->applyFilters($query); $this->view->hostgroups = $query->paginate(); $this->setupSortControl(array( - 'hostgroup_name' => 'Hostgroup Name' + 'hostgroup_name' => $this->translate('Hostgroup Name') )); } public function eventhistoryAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } + $this->addTitleTab('eventhistory'); $query = $this->backend->select()->from('eventHistory', array( 'host_name', @@ -516,6 +572,9 @@ class Monitoring_ListController extends Controller public function servicematrixAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('servicematrix'); $this->setAutorefreshInterval(15); $query = $this->backend->select()->from('serviceStatus', array( @@ -527,8 +586,8 @@ class Monitoring_ListController extends Controller )); $this->applyFilters($query); $this->setupSortControl(array( - 'host_name' => 'Hostname', - 'service_description' => 'Service description' + 'host_name' => $this->translate('Hostname'), + 'service_description' => $this->translate('Service description') )); $pivot = $query->pivot('service_description', 'host_name'); $this->view->pivot = $pivot; @@ -649,6 +708,7 @@ class Monitoring_ListController extends Controller 'hosts', 'services', 'eventhistory', + 'statehistorysummary', 'notifications' ))) { $tabs->extend(new OutputFormat())->extend(new DashboardAction()); diff --git a/modules/monitoring/application/controllers/MonitoringCommands.php b/modules/monitoring/application/controllers/MonitoringCommands.php deleted file mode 100644 index 16b713d4c..000000000 --- a/modules/monitoring/application/controllers/MonitoringCommands.php +++ /dev/null @@ -1,75 +0,0 @@ -getCommandForObject($object, $type); - $out = '
    '; - $i = 0; - - foreach ($definitions as $definition) { - - if ($i % 5 === 0) { - $out .= '
    '; - } - - if ($type === Meta::TYPE_FULL) { - $out .= '
    '; - } - - $out .= sprintf( - '', - $definition->id, - $definition->shortDescription, - $definition->longDescription, - $definition->iconCls, - ($definition->btnCls) ? $definition->btnCls : 'btn-default' - ); - - if ($type === Meta::TYPE_FULL) { - $out .= '
    '; - } - - $i++; - } - - $out .= '
    '; - - $out .= '
    '; - - if ($type === Meta::TYPE_FULL) { - return '
    '. $out. '
    '; - } - - return $out; - } -} diff --git a/modules/monitoring/application/controllers/MultiController.php b/modules/monitoring/application/controllers/MultiController.php index 6459edd59..3cb15082a 100644 --- a/modules/monitoring/application/controllers/MultiController.php +++ b/modules/monitoring/application/controllers/MultiController.php @@ -2,10 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Web\Form; use Icinga\Module\Monitoring\Controller; -use Icinga\Module\Monitoring\Backend; -use Icinga\Data\SimpleQuery; use Icinga\Web\Widget\Chart\InlinePie; use Icinga\Module\Monitoring\Form\Command\MultiCommandFlagForm; use Icinga\Web\Widget; @@ -62,12 +59,12 @@ class Monitoring_MultiController extends Controller // Handle configuration changes $this->handleConfigurationForm(array( - 'host_passive_checks_enabled' => 'Passive Checks', - 'host_active_checks_enabled' => 'Active Checks', - 'host_notifications_enabled' => 'Notifications', - 'host_event_handler_enabled' => 'Event Handler', - 'host_flap_detection_enabled' => 'Flap Detection', - 'host_obsessing' => 'Obsessing' + 'host_passive_checks_enabled' => $this->translate('Passive Checks'), + 'host_active_checks_enabled' => $this->translate('Active Checks'), + 'host_notifications_enabled' => $this->translate('Notifications'), + 'host_event_handler_enabled' => $this->translate('Event Handler'), + 'host_flap_detection_enabled' => $this->translate('Flap Detection'), + 'host_obsessing' => $this->translate('Obsessing') )); } @@ -130,12 +127,12 @@ class Monitoring_MultiController extends Controller $this->view->errors = $errors; $this->handleConfigurationForm(array( - 'service_passive_checks_enabled' => 'Passive Checks', - 'service_active_checks_enabled' => 'Active Checks', - 'service_notifications_enabled' => 'Notifications', - 'service_event_handler_enabled' => 'Event Handler', - 'service_flap_detection_enabled' => 'Flap Detection', - 'service_obsessing' => 'Obsessing', + 'service_passive_checks_enabled' => $this->translate('Passive Checks'), + 'service_active_checks_enabled' => $this->translate('Active Checks'), + 'service_notifications_enabled' => $this->translate('Notifications'), + 'service_event_handler_enabled' => $this->translate('Event Handler'), + 'service_flap_detection_enabled' => $this->translate('Flap Detection'), + 'service_obsessing' => $this->translate('Obsessing'), )); } diff --git a/modules/monitoring/application/controllers/ProcessController.php b/modules/monitoring/application/controllers/ProcessController.php index 49f5715b1..98cba000f 100644 --- a/modules/monitoring/application/controllers/ProcessController.php +++ b/modules/monitoring/application/controllers/ProcessController.php @@ -2,66 +2,113 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Module\Monitoring\Controller as MonitoringController; +use Icinga\Module\Monitoring\Controller; +use Icinga\Module\Monitoring\Form\Command\Instance\DisableNotificationsExpireCommandForm; +use Icinga\Module\Monitoring\Form\Command\Instance\ToggleInstanceFeaturesCommandForm; /** - * Display process information and global commands + * Display process and performance information of the monitoring host and program-wide commands */ -class Monitoring_ProcessController extends MonitoringController +class Monitoring_ProcessController extends Controller { /** - * Retrieve backend and hooks for this controller + * Add tabs * - * @see ActionController::init + * @see \Icinga\Web\Controller\ActionController::init() */ public function init() { - $this->getTabs()->add('info', array( - 'title' => 'Process Info', - 'url' =>'monitoring/process/info' - ))->add('performance', array( - 'title' => 'Performance Info', - 'url' =>'monitoring/process/performance' - )); + $this + ->getTabs() + ->add( + 'info', + array( + 'title' => $this->translate('Process Info'), + 'url' =>'monitoring/process/info' + ) + ) + ->add( + 'performance', + array( + 'title' => $this->translate('Performance Info'), + 'url' => 'monitoring/process/performance' + ) + ); } + /** + * Display process information and program-wide commands + */ public function infoAction() { + $this->view->title = $this->translate('Process Info'); $this->getTabs()->activate('info'); $this->setAutorefreshInterval(10); - - // TODO: This one is broken right now, doublecheck default columns - $this->view->programstatus = $this->backend->select() - ->from('programstatus', array( - 'id', - 'status_update_time', - 'program_start_time', - 'program_end_time', - 'is_currently_running', - 'process_id', - 'daemon_mode', - 'last_command_check', - 'last_log_rotation', - 'notifications_enabled', - 'disable_notif_expire_time', - 'active_service_checks_enabled', - 'passive_service_checks_enabled', - 'active_host_checks_enabled', - 'passive_host_checks_enabled', - 'event_handlers_enabled', - 'flap_detection_enabled', - 'failure_prediction_enabled', - 'process_performance_data', - 'obsess_over_hosts', - 'obsess_over_services', - 'modified_host_attributes', - 'modified_service_attributes', - 'global_host_event_handler', - 'global_service_event_handler' - )) - ->getQuery()->fetchRow(); - $this->view->backendName = $this->backend->getName(); + $programStatus = $this->backend + ->select() + ->from( + 'programstatus', + array( + 'is_currently_running', + 'process_id', + 'program_start_time', + 'status_update_time', + 'last_command_check', + 'last_log_rotation', + 'global_service_event_handler', + 'global_host_event_handler', + 'notifications_enabled', + 'disable_notif_expire_time', + 'active_service_checks_enabled', + 'passive_service_checks_enabled', + 'active_host_checks_enabled', + 'passive_host_checks_enabled', + 'event_handlers_enabled', + 'obsess_over_services', + 'obsess_over_hosts', + 'flap_detection_enabled', + 'process_performance_data' + ) + ) + ->getQuery() + ->fetchRow(); + $this->view->programStatus = $programStatus; + $toggleFeaturesForm = new ToggleInstanceFeaturesCommandForm(); + $toggleFeaturesForm + ->setStatus($programStatus) + ->load($programStatus) + ->handleRequest(); + $this->view->toggleFeaturesForm = $toggleFeaturesForm; + } + + /** + * Disable notifications w/ an optional expire time + */ + public function disableNotificationsAction() + { + $this->view->title = $this->translate('Disable Notifications'); + $programStatus = $this->backend + ->select() + ->from( + 'programstatus', + array( + 'notifications_enabled', + 'disable_notif_expire_time' + ) + ) + ->getQuery() + ->fetchRow(); + $this->view->programStatus = $programStatus; + if ((bool) $programStatus->notifications_enabled === false) { + return; + } else { + $form = new DisableNotificationsExpireCommandForm(); + $form + ->setRedirectUrl('monitoring/process/info') + ->handleRequest(); + $this->view->form = $form; + } } public function performanceAction() diff --git a/modules/monitoring/application/controllers/ServiceController.php b/modules/monitoring/application/controllers/ServiceController.php new file mode 100644 index 000000000..ea29aa54a --- /dev/null +++ b/modules/monitoring/application/controllers/ServiceController.php @@ -0,0 +1,79 @@ +backend, $this->params->get('host'), $this->params->get('service')); + if ($service->fetch() === false) { + throw new Zend_Controller_Action_Exception($this->translate('Service not found')); + } + $this->object = $service; + $this->createTabs(); + } + + /** + * Show a service + */ + public function showAction() + { + $this->getTabs()->activate('service'); + parent::showAction(); + } + + /** + * Acknowledge a service problem + */ + public function acknowledgeProblemAction() + { + $this->view->title = $this->translate('Acknowledge Service Problem'); + $this->handleCommandForm(new AcknowledgeProblemCommandForm()); + } + + /** + * Add a service comment + */ + public function addCommentAction() + { + $this->view->title = $this->translate('Add Service Comment'); + $this->handleCommandForm(new AddCommentCommandForm()); + } + + /** + * Reschedule a service check + */ + public function rescheduleCheckAction() + { + $this->view->title = $this->translate('Reschedule Service Check'); + $this->handleCommandForm(new ScheduleServiceCheckCommandForm()); + } + + /** + * Schedule a service downtime + */ + public function scheduleDowntimeAction() + { + $this->view->title = $this->translate('Schedule Service Downtime'); + $this->handleCommandForm(new ScheduleServiceDowntimeCommandForm()); + } +} diff --git a/modules/monitoring/application/controllers/ServicesController.php b/modules/monitoring/application/controllers/ServicesController.php new file mode 100644 index 000000000..c421ed881 --- /dev/null +++ b/modules/monitoring/application/controllers/ServicesController.php @@ -0,0 +1,184 @@ +backend); + $serviceList->setFilter(Filter::fromQueryString((string) $this->params)); + $this->serviceList = $serviceList; + } + + protected function handleCommandForm(ObjectsCommandForm $form) + { + $form + ->setObjects($this->serviceList) + ->setRedirectUrl(Url::fromPath('monitoring/services/show')->setParams($this->params)) + ->handleRequest(); + $this->view->form = $form; + $this->_helper->viewRenderer('partials/command-form', null, true); + return $form; + } + + public function showAction() + { + $this->getTabs()->add( + 'show', + array( + 'title' => t('Services'), + 'url' => Url::fromRequest() + ) + )->activate('show'); + $this->setAutorefreshInterval(15); + $checkNowForm = new CheckNowCommandForm(); + $checkNowForm + ->setObjects($this->serviceList) + ->handleRequest(); + $this->view->checkNowForm = $checkNowForm; + $this->serviceList->setColumns(array( + 'host_name', + 'host_state', + 'service_description', + 'service_state', + 'service_problem', + 'service_handled', + 'service_acknowledged', + 'service_in_downtime'/*, + 'service_passive_checks_enabled', + 'service_notifications_enabled', + 'service_event_handler_enabled', + 'service_flap_detection_enabled', + 'service_active_checks_enabled', + 'service_obsessing'*/ + )); + $unhandledObjects = array(); + $acknowledgedObjects = array(); + $objectsInDowntime = array(); + $serviceStates = array( + Service::getStateText(Service::STATE_OK) => 0, + Service::getStateText(Service::STATE_WARNING) => 0, + Service::getStateText(Service::STATE_CRITICAL) => 0, + Service::getStateText(Service::STATE_UNKNOWN) => 0, + Service::getStateText(Service::STATE_PENDING) => 0 + ); + $knownHostStates = array(); + $hostStates = array( + Host::getStateText(Host::STATE_UP) => 0, + Host::getStateText(Host::STATE_DOWN) => 0, + Host::getStateText(Host::STATE_UNREACHABLE) => 0, + Host::getStateText(Host::STATE_PENDING) => 0, + ); + foreach ($this->serviceList as $service) { + /** @var Service $service */ + if ((bool) $service->problem === true && (bool) $service->handled === false) { + $unhandledObjects[] = $service; + } + if ((bool) $service->acknowledged === true) { + $acknowledgedObjects[] = $service; + } + if ((bool) $service->in_downtime === true) { + $objectsInDowntime[] = $service; + } + ++$serviceStates[$service::getStateText($service->state)]; + if (! isset($knownHostStates[$service->getHost()->getName()])) { + $knownHostStates[$service->getHost()->getName()] = true; + ++$hostStates[$service->getHost()->getStateText($service->host_state)]; + } + } + if (! empty($acknowledgedObjects)) { + $removeAckForm = new RemoveAcknowledgementCommandForm(); + $removeAckForm + ->setObjects($acknowledgedObjects) + ->handleRequest(); + $this->view->removeAckForm = $removeAckForm; + } + $this->setAutorefreshInterval(15); + $this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/services'); + $this->view->rescheduleAllLink = Url::fromRequest()->setPath('monitoring/services/reschedule-check'); + $this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/services/schedule-downtime'); + $this->view->hostStates = $hostStates; + $this->view->serviceStates = $serviceStates; + $this->view->objects = $this->serviceList; + $this->view->unhandledObjects = $unhandledObjects; + $this->view->acknowledgeUnhandledLink = Url::fromRequest() + ->setPath('monitoring/services/acknowledge-problem') + ->addParams(array('service_problem' => 1, 'service_handled' => 0)); + $this->view->downtimeUnhandledLink = Url::fromRequest() + ->setPath('monitoring/services/schedule-downtime') + ->addParams(array('service_problem' => 1, 'service_handled' => 0)); + $this->view->acknowledgedObjects = $acknowledgedObjects; + $this->view->objectsInDowntime = $objectsInDowntime; + $this->view->inDowntimeLink = Url::fromRequest() + ->setPath('monitoring/list/downtimes'); + $this->view->havingCommentsLink = Url::fromRequest() + ->setPath('monitoring/list/comments'); + $this->view->serviceStatesPieChart = $this->createPieChart( + $serviceStates, + $this->translate('Service State'), + array('#44bb77', '#FFCC66', '#FF5566', '#E066FF', '#77AAFF') + ); + $this->view->hostStatesPieChart = $this->createPieChart( + $hostStates, + $this->translate('Host State'), + array('#44bb77', '#FF5566', '#E066FF', '#77AAFF') + ); + } + + protected function createPieChart(array $states, $title, array $colors) + { + $chart = new InlinePie(array_values($states), $title, $colors); + return $chart + ->setLabel(array_map('strtoupper', array_keys($states))) + ->setHeight(100) + ->setWidth(100) + ->setTitle($title); + } + + /** + * Acknowledge service problems + */ + public function acknowledgeProblemAction() + { + $this->view->title = $this->translate('Acknowledge Service Problems'); + $this->handleCommandForm(new AcknowledgeProblemCommandForm()); + } + + /** + * Reschedule service checks + */ + public function rescheduleCheckAction() + { + $this->view->title = $this->translate('Reschedule Service Checks'); + $this->handleCommandForm(new ScheduleServiceCheckCommandForm()); + } + + /** + * Schedule service downtimes + */ + public function scheduleDowntimeAction() + { + $this->view->title = $this->translate('Schedule Service Downtimes'); + $this->handleCommandForm(new ScheduleServiceDowntimeCommandForm()); + } +} diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index 3630d6424..129903ce0 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -5,6 +5,7 @@ use Icinga\Application\Benchmark; use Icinga\Module\Monitoring\Object\MonitoredObject; use Icinga\Web\Hook; +use Icinga\Web\Url; use Icinga\Web\Widget\Tabs; use Icinga\Web\Widget\Tabextension\OutputFormat; use Icinga\Web\Widget\Tabextension\DashboardAction; @@ -35,14 +36,11 @@ class Monitoring_ShowController extends Controller */ public function init() { - if ($this->getRequest()->getActionName() === 'host') { - $this->view->object = new Host($this->params); - } elseif ($this->getRequest()->getActionName() === 'service') { - $this->view->object = new Service($this->params); - } else { - // TODO: Well... this could be done better - $this->view->object = MonitoredObject::fromParams($this->params); + $this->view->object = MonitoredObject::fromParams($this->params); + if ($this->view->object && $this->view->object->fetch() === false) { + throw new Zend_Controller_Action_Exception($this->translate('Host or service not found')); } + if (Hook::has('ticket')) { $this->view->tickets = Hook::first('ticket'); } @@ -57,36 +55,19 @@ class Monitoring_ShowController extends Controller } /** - * Service overview + * @deprecated */ public function serviceAction() { - $o = $this->view->object; - $this->setAutorefreshInterval(10); - $this->view->title = $o->service_description - . ' on ' . $o->host_name; - $this->getTabs()->activate('service'); - $o->populate(); - if ($this->grapher) { - $this->view->grapherHtml = $this->grapher->getPreviewHtml($o); - } - $this->fetchHostStats(); + $this->redirectNow(Url::fromRequest()->setPath('monitoring/service/show')); } /** - * Host overview + * @deprecated */ public function hostAction() { - $o = $this->view->object; - $this->setAutorefreshInterval(10); - $this->getTabs()->activate('host'); - $this->view->title = $o->host_name; - $o->populate(); - if ($this->grapher) { - $this->view->grapherHtml = $this->grapher->getPreviewHtml($o); - } - $this->fetchHostStats(); + $this->redirectNow(Url::fromRequest()->setPath('monitoring/host/show')); } public function historyAction() @@ -206,16 +187,17 @@ class Monitoring_ShowController extends Controller if (($object = $this->view->object) === null) { return; } - - $tabs = $this->getTabs(); - $params = array( - 'host' => $object->host_name, - ); - if ($object instanceof Service) { - $params['service'] = $object->service_description; - } elseif ($service = $this->_getParam('service')) { - $params['service'] = $service; + if ($object->getType() === $object::TYPE_HOST) { + $params = array( + 'host' => $object->getName() + ); + } else { + $params = array( + 'host' => $object->getHost()->getName(), + 'service' => $object->getName() + ); } + $tabs = $this->getTabs(); $tabs->add( 'host', array( diff --git a/modules/monitoring/application/controllers/TimelineController.php b/modules/monitoring/application/controllers/TimelineController.php index 2da29a0b9..f78caafa6 100644 --- a/modules/monitoring/application/controllers/TimelineController.php +++ b/modules/monitoring/application/controllers/TimelineController.php @@ -12,7 +12,7 @@ use Icinga\Util\DateTimeFactory; use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Timeline\TimeLine; use Icinga\Module\Monitoring\Timeline\TimeRange; -use Icinga\Module\Monitoring\Web\Widget\TimelineIntervalBox; +use Icinga\Module\Monitoring\Web\Widget\SelectBox; use Icinga\Module\Monitoring\DataView\EventHistory as EventHistoryView; class Monitoring_TimelineController extends Controller @@ -26,7 +26,7 @@ class Monitoring_TimelineController extends Controller $detailUrl = Url::fromPath('monitoring/list/eventhistory'); $timeline = new TimeLine( - $this->backend->select()->from('eventHistory', + $this->backend->select()->from('eventHistory', array( 'name' => 'type', 'time' => 'timestamp' @@ -85,7 +85,7 @@ class Monitoring_TimelineController extends Controller */ private function setupIntervalBox() { - $box = new TimelineIntervalBox( + $box = new SelectBox( 'intervalBox', array( '4h' => t('4 Hours'), @@ -93,7 +93,9 @@ class Monitoring_TimelineController extends Controller '1w' => t('One week'), '1m' => t('One month'), '1y' => t('One year') - ) + ), + t('TimeLine interval'), + 'interval' ); $box->applyRequest($this->getRequest()); $this->view->intervalBox = $box; diff --git a/modules/monitoring/application/forms/Command/AcknowledgeForm.php b/modules/monitoring/application/forms/Command/AcknowledgeForm.php deleted file mode 100644 index 4ac4901b0..000000000 --- a/modules/monitoring/application/forms/Command/AcknowledgeForm.php +++ /dev/null @@ -1,154 +0,0 @@ -addNote( - t( - 'This command is used to acknowledge host or service problems. When a problem is ' - . 'acknowledged, future notifications about problems are temporarily disabled until the ' - . 'host/service changes from its current state.' - ) - ); - - $this->addElement($this->createAuthorField()); - - $this->addElement( - 'textarea', - 'comment', - array( - 'label' => t('Comment'), - 'rows' => 4, - 'cols' => 72, - 'required' => true, - 'helptext' => t( - ' If you work with other administrators you may find it useful to share information ' - . 'about a host or service that is having problems if more than one of you may be working on ' - . 'it. Make sure you enter a brief description of what you are doing.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'persistent', - array( - 'label' => t('Persistent Comment'), - 'value' => false, - 'helptext' => t( - 'If you would like the comment to remain even when the acknowledgement is removed, ' - . 'check this option.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'expire', - array( - 'label' => t('Use Expire Time'), - 'helptext' => t('If the acknowledgement should expire, check this option.') - ) - ); - $this->enableAutoSubmit(array('expire')); - - if ($this->getRequest()->getPost('expire', '0') === '1') { - $now = DateTimeFactory::create(); - $this->addElement( - new DateTimePicker( - array( - 'name' => 'expiretime', - 'label' => t('Expire Time'), - 'value' => $now->getTimestamp() + 3600, - 'patterns' => $this->getValidDateTimeFormats(), - 'helptext' => t( - 'Enter the expire date/time for this acknowledgement here. Icinga will ' - . ' delete the acknowledgement after this date expired.' - ), - 'jspicker' => true - ) - ) - ); - } - - $this->addElement( - 'checkbox', - 'sticky', - array( - 'label' => t('Sticky Acknowledgement'), - 'value' => true, - 'helptext' => t( - 'If you want the acknowledgement to disable notifications until the host/service ' - . 'recovers, check this option.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'notify', - array( - 'label' => t('Send Notification'), - 'value' => true, - 'helptext' => t( - 'If you do not want an acknowledgement notification to be sent out to the appropriate ' - . 'contacts, uncheck this option.' - ) - ) - ); - - $this->setSubmitLabel(t('Acknowledge Problem')); - - parent::create(); - } - - /** - * Add validator for dependent fields - * - * @param array $data - * - * @see \Icinga\Web\Form::preValidation() - */ - protected function preValidation(array $data) - { - if (isset($data['expire']) && intval($data['expire']) === 1) { - $expireTime = $this->getElement('expiretime'); - $expireTime->setRequired(true); - } - } - - /** - * Create the acknowledgement command object - * - * @return AcknowledgeCommand - */ - public function createCommand() - { - return new AcknowledgeCommand( - new Comment( - $this->getAuthorName(), - $this->getValue('comment'), - $this->getValue('persistent') - ), - $this->getValue('expire') ? $this->getValue('expire') : -1, - $this->getValue('notify'), - $this->getValue('sticky') - ); - } -} diff --git a/modules/monitoring/application/forms/Command/CommandForm.php b/modules/monitoring/application/forms/Command/CommandForm.php index 9ccdd0f9b..0a7cd19f9 100644 --- a/modules/monitoring/application/forms/Command/CommandForm.php +++ b/modules/monitoring/application/forms/Command/CommandForm.php @@ -4,140 +4,61 @@ namespace Icinga\Module\Monitoring\Form\Command; -use Zend_Config; -use Zend_Controller_Request_Abstract; -use Zend_Form_Element_Hidden; -use Icinga\Module\Monitoring\Command\AcknowledgeCommand; +use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Command\Transport\CommandTransport; use Icinga\Web\Form; +use Icinga\Web\Request; /** - * Simple confirmation command + * Base class for command forms */ abstract class CommandForm extends Form { /** - * If the form is for a global command + * Monitoring backend * - * @var bool + * @var Backend */ - protected $globalCommand = false; + protected $backend; /** - * Set command program wide + * Set the monitoring backend * - * @param bool $flag + * @param Backend $backend + * + * @return $this */ - public function setProvideGlobalCommand($flag = true) + public function setBackend(Backend $backend) { - $this->globalCommand = (boolean) $flag; + $this->backend = $backend; + return $this; } /** - * Getter for globalCommand + * Get the monitoring backend * - * @return bool + * @return Backend */ - public function provideGlobalCommand() + public function getBackend() { - return (boolean) $this->globalCommand; + return $this->backend; } /** - * Create an instance name containing hidden field + * Get the transport used to send commands * - * @return Zend_Form_Element_Hidden - */ - private function createInstanceHiddenField() - { - $field = new Zend_Form_Element_Hidden('instance'); - $value = $this->getRequest()->getParam($field->getName()); - $field->setValue($value); - return $field; - } - - /** - * Add elements to this form (used by extending classes) + * @param Request $request * - * @see Form::create + * @return \Icinga\Module\Monitoring\Command\Transport\CommandTransportInterface */ - protected function create() + public function getTransport(Request $request) { - $this->addElement($this->createInstanceHiddenField()); - } - - /** - * Get the author name - * - * @return string - */ - protected function getAuthorName() - { - if (is_a($this->getRequest(), "Zend_Controller_Request_HttpTestCase")) { - return "Test user"; + $instance = $request->getParam('instance'); + if ($instance !== null) { + $transport = CommandTransport::create($instance); + } else { + $transport = CommandTransport::first(); } - return $this->getRequest()->getUser()->getUsername(); + return $transport; } - - /** - * Creator for author field - * - * @return Zend_Form_Element_Hidden - */ - protected function createAuthorField() - { - $authorName = $this->getAuthorName(); - - $authorField = new Zend_Form_Element_Hidden( - array( - 'name' => 'author', - 'label' => t('Author (Your Name)'), - 'value' => $authorName, - 'required' => true - ) - ); - - $authorField->addDecorator( - 'Callback', - array( - 'callback' => function () use ($authorName) { - return sprintf('%s', $authorName); - } - ) - ); - - return $authorField; - } - - /** - * Get a list of valid datetime formats - * - * @return array - */ - public function getValidDateTimeFormats() - { - // TODO(mh): Missing localized format (#6077) - return array('d/m/Y g:i A'); - } - - /** - * Sets the form to global if we have data in the request - * - * @param Zend_Controller_Request_Abstract $request - */ - public function setRequest(Zend_Controller_Request_Abstract $request) - { - parent::setRequest($request); - - if ($request->getParam('global')) { - $this->setProvideGlobalCommand(true); - } - } - - - /** - * Create command object for CommandPipe protocol - * - * @return AcknowledgeCommand - */ - abstract public function createCommand(); } diff --git a/modules/monitoring/application/forms/Command/CommentForm.php b/modules/monitoring/application/forms/Command/CommentForm.php deleted file mode 100644 index 497c276f1..000000000 --- a/modules/monitoring/application/forms/Command/CommentForm.php +++ /dev/null @@ -1,75 +0,0 @@ -setName('form_CommentForm'); - - $this->addNote(t('This command is used to add a comment to hosts or services.')); - - $this->addElement($this->createAuthorField()); - - $this->addElement( - 'textarea', - 'comment', - array( - 'label' => t('Comment'), - 'rows' => 4, - 'cols' => 72, - 'required' => true, - 'helptext' => t( - 'If you work with other administrators, you may find it useful to share information ' - . 'about a host or service that is having problems if more than one of you may be working on ' - . 'it. Make sure you enter a brief description of what you are doing.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'persistent', - array( - 'label' => t('Persistent'), - 'value' => true, - 'helptext' => t( - 'If you uncheck this option, the comment will automatically be deleted the next time ' - . 'Icinga is restarted.' - ) - ) - ); - - $this->setSubmitLabel(t('Post Comment')); - - parent::create(); - } - - /** - * Create the command object to add comments - * - * @return AddCommentCommand - */ - public function createCommand() - { - return new AddCommentCommand( - new Comment( - $this->getAuthorName(), - $this->getValue('comment'), - $this->getValue('persistent') - ) - ); - } -} diff --git a/modules/monitoring/application/forms/Command/CustomNotificationForm.php b/modules/monitoring/application/forms/Command/CustomNotificationForm.php deleted file mode 100644 index 9eb75a67b..000000000 --- a/modules/monitoring/application/forms/Command/CustomNotificationForm.php +++ /dev/null @@ -1,93 +0,0 @@ -addNote( - t( - 'This command is used to send a custom notification about hosts or services. Useful in ' - . 'emergencies when you need to notify admins of an issue regarding a monitored system or ' - . 'service.' - ) - ); - - $this->addElement($this->createAuthorField()); - - $this->addElement( - 'textarea', - 'comment', - array( - 'label' => t('Comment'), - 'rows' => 4, - 'cols' => 72, - 'required' => true, - 'helptext' => t( - 'If you work with other administrators, you may find it useful to share information ' - . 'about a host or service that is having problems if more than one of you may be working on ' - . 'it. Make sure you enter a brief description of what you are doing.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'forced', - array( - 'label' => t('Forced'), - 'helptext' => t( - 'Custom notifications normally follow the regular notification logic in Icinga. Selecting this ' - . 'option will force the notification to be sent out, regardless of time restrictions, ' - . 'whether or not notifications are enabled, etc.' - ) - ) - ); - - $this->addElement( - 'checkbox', - 'broadcast', - array( - 'label' => t('Broadcast'), - 'helptext' => t( - 'Selecting this option causes the notification to be sent out to all normal (non-escalated) ' - . ' and escalated contacts. These options allow you to override the normal notification logic ' - . 'if you need to get an important message out.' - ) - ) - ); - - $this->setSubmitLabel(t('Send Custom Notification')); - - parent::create(); - } - - /** - * Create the command object to send custom notifications - * - * @return CustomNotificationCommand - */ - public function createCommand() - { - return new CustomNotificationCommand( - new Comment( - $this->getAuthorName(), - $this->getValue('comment') - ), - $this->getValue('forced'), - $this->getValue('broadcast') - ); - } -} diff --git a/modules/monitoring/application/forms/Command/DelayNotificationForm.php b/modules/monitoring/application/forms/Command/DelayNotificationForm.php deleted file mode 100644 index efb6eae0f..000000000 --- a/modules/monitoring/application/forms/Command/DelayNotificationForm.php +++ /dev/null @@ -1,65 +0,0 @@ -addNote(t('This command is used to delay the next problem notification that is sent out.')); - - $this->addElement( - 'text', - 'minutes', - array( - 'label' => t('Notification Delay (Minutes From Now)'), - 'style' => 'width: 80px;', - 'value' => 0, - 'required' => true, - 'validators' => array( - array( - 'between', - true, - array( - 'min' => 1, - 'max' => self::MAX_DELAY - ) - ) - ), - 'helptext' => t( - 'The notification delay will be disregarded if the host/service changes state before the next ' - . 'notification is scheduled to be sent out.' - ) - ) - ); - - $this->setSubmitLabel(t('Delay Notification')); - - parent::create(); - } - - /** - * Create the command object to delay notifications - * - * @return DelayNotificationCommand - */ - public function createCommand() - { - return new DelayNotificationCommand($this->getValue('minutes') * 60); - } -} diff --git a/modules/monitoring/application/forms/Command/DisableNotificationWithExpireForm.php b/modules/monitoring/application/forms/Command/DisableNotificationWithExpireForm.php deleted file mode 100644 index d6657dd37..000000000 --- a/modules/monitoring/application/forms/Command/DisableNotificationWithExpireForm.php +++ /dev/null @@ -1,56 +0,0 @@ -addNote('Disable notifications for a specific time on a program-wide basis'); - - $now = DateTimeFactory::create(); - $this->addElement( - new DateTimePicker( - array( - 'name' => 'expiretime', - 'label' => t('Expire Time'), - 'value' => $now->getTimestamp() + 3600, - 'patterns' => $this->getValidDateTimeFormats(), - 'helptext' => t( - 'Enter the expire date/time for this acknowledgement here. Icinga will ' - . ' delete the acknowledgement after this date expired.' - ) - ) - ) - ); - - $this->setSubmitLabel('Disable notifications'); - - parent::create(); - } - - - /** - * Create command object for CommandPipe protocol - * - * @return AcknowledgeCommand - */ - public function createCommand() - { - $command = new DisableNotificationWithExpireCommand(); - $command->setExpirationTimestamp($this->getValue('expiretime')); - return $command; - } -} diff --git a/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php b/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php new file mode 100644 index 000000000..272fb6660 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php @@ -0,0 +1,76 @@ +setSubmitLabel(mt('monitoring', 'Disable Notifications')); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElement( + new Note( + 'command-info', + array( + 'value' => mt( + 'monitoring', + 'This command is used to disable host and service notifications for a specific time.' + ) + ) + ) + ); + $expireTime = new DateTime(); + $expireTime->add(new DateInterval('PT1H')); + $this->addElement( + new DateTimePicker( + 'expire_time', + array( + 'required' => true, + 'label' => mt('monitoring', 'Expire Time'), + 'description' => mt('monitoring', 'Set the expire time.'), + 'value' => $expireTime + ) + ) + ); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + $disableNotifications = new DisableNotificationsExpireCommand(); + $disableNotifications + ->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp()); + $this->getTransport($request)->send($disableNotifications); + Notification::success(mt('monitoring', 'Disabling host and service notifications..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php new file mode 100644 index 000000000..883a11a35 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php @@ -0,0 +1,205 @@ +setAttrib('class', 'inline instance-features'); + } + + /** + * Set the instance status + * + * @param object $status + * + * @return $this + */ + public function setStatus($status) + { + $this->status = (object) $status; + return $this; + } + + /** + * Get the instance status + * + * @return object + */ + public function getStatus() + { + return $this->status; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + if ((bool) $this->status->notifications_enabled) { + $notificationDescription = sprintf( + '%s', + mt('monitoring', 'Disable notifications for a specific time on a program-wide basis'), + $this->getView()->href('monitoring/process/disable-notifications'), + mt('monitoring', 'Disable temporarily') + ); + } elseif ($this->status->disable_notif_expire_time) { + $notificationDescription = sprintf( + mt('monitoring', 'Notifications will be re-enabled in %s'), + $this->getView()->timeUntil($this->status->disable_notif_expire_time) + ); + } else { + $notificationDescription = null; + } + $this->addElements(array( + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS, + array( + 'label' => mt('monitoring', 'Active Host Checks Being Executed'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS, + array( + 'label' => mt('monitoring', 'Active Service Checks Being Executed'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS, + array( + 'label' => mt('monitoring', 'Event Handlers Enabled'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION, + array( + 'label' => mt('monitoring', 'Flap Detection Enabled'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS, + array( + 'label' => mt('monitoring', 'Notifications Enabled'), + 'autosubmit' => true, + 'description' => $notificationDescription, + 'decorators' => array( + 'ViewHelper', + 'Errors', + array( + 'Description', + array('tag' => 'span', 'class' => 'description', 'escape' => false) + ), + 'Label', + array('HtmlTag', array('tag' => 'div')) + ) + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING, + array( + 'label' => mt('monitoring', 'Obsessing Over Hosts'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING, + array( + 'label' => mt('monitoring', 'Obsessing Over Services'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS, + array( + 'label' => mt('monitoring', 'Passive Host Checks Being Accepted'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS, + array( + 'label' => mt('monitoring', 'Passive Service Checks Being Accepted'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA, + array( + 'label' => mt('monitoring', 'Performance Data Being Processed'), + 'autosubmit' => true + ) + ) + )); + return $this; + } + + /** + * Load feature status + * + * @param object $instanceStatus + * + * @return $this + */ + public function load($instanceStatus) + { + $this->create(); + foreach ($this->getValues() as $feature => $enabled) { + $this->getElement($feature)->setChecked($instanceStatus->{$feature}); + } + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->getValues() as $feature => $enabled) { + $toggleFeature = new ToggleInstanceFeatureCommand(); + $toggleFeature + ->setFeature($feature) + ->setEnabled($enabled); + $this->getTransport($request)->send($toggleFeature); + } + Notification::success(mt('monitoring', 'Toggling feature..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/MultiCommandFlagForm.php b/modules/monitoring/application/forms/Command/MultiCommandFlagForm.php deleted file mode 100644 index e413848c8..000000000 --- a/modules/monitoring/application/forms/Command/MultiCommandFlagForm.php +++ /dev/null @@ -1,147 +0,0 @@ -flags = $flags; - parent::__construct(); - $this->setEnctype(Zend_Form::ENCTYPE_MULTIPART); - } - - /** - * Initialise the form values with the array of items to configure. - * - * @param mixed $items The items that will be edited with this form. - */ - public function initFromItems($items) - { - $this->values = $this->valuesFromObjects($items); - $this->buildForm(); - $this->populate($this->values); - } - - /** - * Return only the values that have been updated. - */ - public function getChangedValues() - { - $values = $this->getValues(); - $changed = array(); - foreach ($values as $key => $value) { - $oldKey = $key . self::OLD_VALUE_MARKER; - if (array_key_exists($oldKey, $values)) { - if ($values[$oldKey] !== $value) { - $changed[$key] = $value; - } - } - } - return $changed; - } - - private function valuesFromObjects($items) - { - $values = array(); - foreach ($items as $item) { - foreach ($this->flags as $key => $unused) { - if (isset($item->{$key})) { - $value = $item->{$key}; - // convert strings - if ($value === '1' || $value === '0') { - $value = intval($value); - } - // init key with first value - if (!array_key_exists($key, $values)) { - $values[$key] = $value; - continue; - } - // already a mixed state ? - if ($values[$key] === 'unchanged') { - continue; - } - // values differ? - if ($values[$key] ^ $value) { - $values[$key] = 'unchanged'; - } - } - } - } - $old = array(); - foreach ($values as $key => $value) { - $old[$key . self::OLD_VALUE_MARKER] = $value; - } - return array_merge($values, $old); - } - - public function buildCheckboxes() - { - $checkboxes = array(); - foreach ($this->flags as $flag => $description) { - $checkboxes[] = new TriStateCheckbox( - $flag, - array( - 'label' => $description, - 'required' => true - ) - ); - } - return $checkboxes; - } - - /** - * Create the multi flag form - * - * @see Form::create() - */ - public function create() - { - $this->setName('form_flag_configuration'); - foreach ($this->buildCheckboxes() as $checkbox) { - $this->addElement($checkbox); - $old = new Zend_Form_Element_Hidden($checkbox->getName() . self::OLD_VALUE_MARKER); - $this->addElement($old); - } - $this->setSubmitLabel('Save Configuration'); - } -} diff --git a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php new file mode 100644 index 000000000..c5b78e25c --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php @@ -0,0 +1,173 @@ +objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + new Note( + 'command-info', + array( + 'value' => mt( + 'monitoring', + 'This command is used to acknowledge host or service problems. When a problem is acknowledged,' + . ' future notifications about problems are temporarily disabled until the host or service' + . ' recovers.' + ) + ) + ), + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => mt('monitoring', 'Comment'), + 'description' => mt( + 'monitoring', + 'If you work with other administrators, you may find it useful to share information about the' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ) + ), + array( + 'checkbox', + 'persistent', + array( + 'label' => mt('monitoring', 'Persistent Comment'), + 'description' => mt( + 'monitoring', + 'If you would like the comment to remain even when the acknowledgement is removed, check this' + . ' option.' + ) + ) + ), + array( + 'checkbox', + 'expire', + array( + 'label' => mt('monitoring', 'Use Expire Time'), + 'description' => mt('monitoring', 'If the acknowledgement should expire, check this option.'), + 'autosubmit' => true + ) + ) + )); + if (isset($formData['expire']) && (bool) $formData['expire'] === true) { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval('PT1H')); + $this->addElement( + new DateTimePicker( + 'expire_time', + array( + 'label' => mt('monitoring', 'Expire Time'), + 'value' => $expireTime, + 'description' => mt( + 'monitoring', + 'Enter the expire date and time for this acknowledgement here. Icinga will delete the' + . ' acknowledgement after this time expired.' + ) + ) + ) + ); + $this->addDisplayGroup( + array('expire', 'expire_time'), + 'expire-expire_time', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')) + ) + ) + ); + } + $this->addElements(array( + array( + 'checkbox', + 'sticky', + array( + 'label' => mt('monitoring', 'Sticky Acknowledgement'), + 'value' => true, + 'description' => mt( + 'monitoring', + 'If you want the acknowledgement to disable notifications until the host or service recovers,' + . ' check this option.' + ) + ) + ), + array( + 'checkbox', + 'notify', + array( + 'label' => mt('monitoring', 'Send Notification'), + 'value' => true, + 'description' => mt( + 'monitoring', + 'If you do not want an acknowledgement notification to be sent out to the appropriate contacts,' + . ' uncheck this option.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $ack = new AcknowledgeProblemCommand(); + $ack + ->setObject($object) + ->setComment($this->getElement('comment')->getValue()) + ->setAuthor($request->getUser()->getUsername()) + ->setPersistent($this->getElement('persistent')->isChecked()) + ->setSticky($this->getElement('sticky')->isChecked()) + ->setNotify($this->getElement('notify')->isChecked()); + if ($this->getElement('expire')->isChecked()) { + $ack->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp()); + } + $this->getTransport($request)->send($ack); + } + Notification::success(mtp( + 'monitoring', + 'Acknowledging problem..', + 'Acknowledging problems..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php new file mode 100644 index 000000000..1f6f9573e --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php @@ -0,0 +1,98 @@ +objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + new Note( + 'command-info', + array( + 'value' => mt( + 'monitoring', + 'This command is used to add host or service comments.' + ) + ) + ), + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => mt('monitoring', 'Comment'), + 'description' => mt( + 'monitoring', + 'If you work with other administrators, you may find it useful to share information about the' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ) + ), + array( + 'checkbox', + 'persistent', + array( + 'label' => mt('monitoring', 'Persistent'), + 'value' => true, + 'description' => mt( + 'monitoring', + 'If you uncheck this option, the comment will automatically be deleted the next time Icinga is' + . ' restarted.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $comment = new AddCommentCommand(); + $comment->setObject($object); + $comment->setComment($this->getElement('comment')->getValue()); + $comment->setAuthor($request->getUser()->getUsername()); + $comment->setPersistent($this->getElement('persistent')->isChecked()); + $this->getTransport($request)->send($comment); + } + Notification::success(mtp( + 'monitoring', + 'Adding comment..', + 'Adding comments..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php new file mode 100644 index 000000000..14e7be011 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php @@ -0,0 +1,83 @@ +setAttrib('class', 'inline link-like'); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::addSubmitButton() For the method documentation. + */ + public function addSubmitButton() + { + $this->addElements(array( + new Note( + 'icon', // Bogus + array( + 'decorators' => array(array( + 'HtmlTag', + array('tag' => 'img', 'src' => $this->getView()->href('img/icons/refresh_petrol.png')) + )) + ) + ), + array( + 'submit', + 'btn_submit', + array( + 'ignore' => true, + 'label' => mt('monitoring', 'Check now'), + 'decorators' => array('ViewHelper') + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if ($object->getType() === $object::TYPE_HOST) { + $check = new ScheduleHostCheckCommand(); + } else { + $check = new ScheduleServiceCheckCommand(); + } + $check + ->setObject($object) + ->setForced() + ->setCheckTime(time()); + $this->getTransport($request)->send($check); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling check..', + 'Scheduling checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php new file mode 100644 index 000000000..4c7df0141 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php @@ -0,0 +1,87 @@ +setAttrib('class', 'inline link-like'); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'hidden', + 'comment_id', + array( + 'required' => true + ) + ), + array( + 'hidden', + 'redirect' + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::addSubmitButton() For the method documentation. + */ + public function addSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + array( + 'ignore' => true, + 'label' => 'X', + 'title' => mt('monitoring', 'Delete comment'), + 'decorators' => array('ViewHelper') + ) + ); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $delComment = new DeleteCommentCommand(); + $delComment + ->setObject($object) + ->setCommentId($this->getElement('comment_id')->getValue()); + $this->getTransport($request)->send($delComment); + } + $redirect = $this->getElement('redirect')->getValue(); + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + } + Notification::success(mt('monitoring', 'Deleting comment..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php new file mode 100644 index 000000000..ae712eddb --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php @@ -0,0 +1,87 @@ +setAttrib('class', 'inline link-like'); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'hidden', + 'downtime_id', + array( + 'required' => true + ) + ), + array( + 'hidden', + 'redirect' + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::addSubmitButton() For the method documentation. + */ + public function addSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + array( + 'ignore' => true, + 'label' => 'X', + 'title' => mt('monitoring', 'Delete downtime'), + 'decorators' => array('ViewHelper') + ) + ); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $delDowntime = new DeleteDowntimeCommand(); + $delDowntime + ->setObject($object) + ->setDowntimeId($this->getElement('downtime_id')->getValue()); + $this->getTransport($request)->send($delDowntime); + } + $redirect = $this->getElement('redirect')->getValue(); + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + } + Notification::success(mt('monitoring', 'Deleting downtime..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php b/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php new file mode 100644 index 000000000..bf603a7b5 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php @@ -0,0 +1,48 @@ +objects = array($objects); + } else { + $this->objects = $objects; + } + return $this; + } + + /** + * Get the involved Icinga objects + * + * @return array|\ArrayAccess|\Traversable + */ + public function getObjects() + { + return $this->objects; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php new file mode 100644 index 000000000..6ccd0633d --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php @@ -0,0 +1,56 @@ +setAttrib('class', 'inline link-like'); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return mtp( + 'monitoring', 'Remove problem acknowledgement', 'Remove problem acknowledgements', count($this->objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $removeAck = new RemoveAcknowledgementCommand(); + $removeAck->setObject($object); + $this->getTransport($request)->send($removeAck); + } + Notification::success(mtp( + 'monitoring', + 'Removing problem acknowledgement..', + 'Removing problem acknowledgements..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php new file mode 100644 index 000000000..89d39716c --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php @@ -0,0 +1,61 @@ +addElements(array( + array( + 'checkbox', + 'all_services', + array( + 'label' => mt('monitoring', 'All Services'), + 'description' => mt( + 'monitoring', + 'Schedule check for all services on the hosts and the hosts themselves.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $check = new ScheduleHostCheckCommand(); + $check + ->setObject($object) + ->setOfAllServices($this->getElement('all_services')->isChecked()); + $this->scheduleCheck($check, $request); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling host check..', + 'Scheduling host checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php new file mode 100644 index 000000000..25caa0f20 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php @@ -0,0 +1,97 @@ +addElements(array( + array( + 'checkbox', + 'all_services', + array( + 'label' => mt('monitoring', 'All Services'), + 'description' => mt( + 'monitoring', + 'Schedule downtime for all services on the hosts and the hosts themselves.' + ) + ) + ), + array( + 'select', + 'child_hosts', + array( + 'label' => mt('monitoring', 'Child Hosts'), + 'required' => true, + 'multiOptions' => array( + 0 => mt('monitoring', 'Do nothing with child hosts'), + 1 => mt('monitoring', 'Schedule triggered downtime for all child hosts'), + 2 => mt('monitoring', 'Schedule non-triggered downtime for all child hosts') + ), + 'description' => mt( + 'monitoring', + 'Define what should be done with the child hosts of the hosts.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $childHosts = (int) $this->getElement('child_hosts')->getValue(); + $allServices = $this->getElement('all_services')->isChecked(); + if ($childHosts === 0) { + $hostDowntime = new ScheduleHostDowntimeCommand(); + if ($allServices === true) { + $hostDowntime->setForAllServices(); + }; + } else { + $hostDowntime = new PropagateHostDowntimeCommand(); + if ($childHosts === 1) { + $hostDowntime->setTriggered(); + } + if ($allServices === true) { + foreach ($object->services as $service) { + $serviceDowntime = new ScheduleServiceDowntimeCommand(); + $serviceDowntime->setObject($service); + $this->scheduleDowntime($serviceDowntime, $request); + } + } + } + $hostDowntime->setObject($object); + $this->scheduleDowntime($hostDowntime, $request); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling host downtime..', + 'Scheduling host downtimes..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php new file mode 100644 index 000000000..304ccb435 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php @@ -0,0 +1,109 @@ +objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $checkTime = new DateTime(); + $checkTime->add(new DateInterval('PT1H')); + $this->addElements(array( + new Note( + 'command-info', + array( + 'value' => mt( + 'monitoring', + 'This command is used to schedule the next check of hosts or services. Icinga will re-queue the' + . ' hosts or services to be checked at the time you specify.' + ) + ) + ), + new DateTimePicker( + 'check_time', + array( + 'required' => true, + 'label' => mt('monitoring', 'Check Time'), + 'description' => mt('monitoring', 'Set the date and time when the check should be scheduled.'), + 'value' => $checkTime + ) + ), + array( + 'checkbox', + 'force_check', + array( + 'label' => mt('monitoring', 'Force Check'), + 'description' => mt( + 'monitoring', + 'If you select this option, Icinga will force a check regardless of both what time the' + . ' scheduled check occurs and whether or not checks are enabled.' + ) + ) + ) + )); + return $this; + } + + /** + * Schedule a check + * + * @param ScheduleServiceCheckCommand $check + * @param Request $request + */ + public function scheduleCheck(ScheduleServiceCheckCommand $check, Request $request) + { + $check + ->setForced($this->getElement('force_check')->isChecked()) + ->setCheckTime($this->getElement('check_time')->getValue()->getTimestamp()); + $this->getTransport($request)->send($check); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $check = new ScheduleServiceCheckCommand(); + $check->setObject($object); + $this->scheduleCheck($check, $request); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling service check..', + 'Scheduling service checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php new file mode 100644 index 000000000..58b391d14 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php @@ -0,0 +1,218 @@ +objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $start = new DateTime; + $end = clone $start; + $end->add(new DateInterval('PT1H')); + $this->addElements(array( + new Note( + 'command-info', + array( + 'value' => mt( + 'monitoring', + 'This command is used to schedule host and service downtimes. During the specified downtime,' + . ' Icinga will not send notifications out about the hosts and services. When the scheduled' + . ' downtime expires, Icinga will send out notifications for the hosts and services as it' + . ' normally would. Scheduled downtimes are preserved across program shutdowns and' + . ' restarts.' + ) + ) + ), + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => mt('monitoring', 'Comment'), + 'description' => mt( + 'monitoring', + 'If you work with other administrators, you may find it useful to share information about the' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ) + ), + new DateTimePicker( + 'start', + array( + 'required' => true, + 'label' => mt('monitoring', 'Start Time'), + 'description' => mt('monitoring', 'Set the start date and time for the downtime.'), + 'value' => $start + ) + ), + new DateTimePicker( + 'end', + array( + 'required' => true, + 'label' => mt('monitoring', 'End Time'), + 'description' => mt('monitoring', 'Set the end date and time for the downtime.'), + 'value' => $end + ) + ), + array( + 'select', + 'type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => mt('monitoring', 'Type'), + 'description' => mt( + 'monitoring', + 'If you select the fixed option, the downtime will be in effect between the start and end' + . ' times you specify whereas a flexible downtime starts when the host or service enters a' + . ' problem state sometime between the start and end times you specified and lasts as long' + . ' as the duration time you enter. The duration fields do not apply for fixed downtimes.' + ), + 'multiOptions' => array( + self::FIXED => mt('monitoring', 'Fixed'), + self::FLEXIBLE => mt('monitoring', 'Flexible') + ), + 'validators' => array( + array( + 'InArray', + true, + array(array(self::FIXED, self::FLEXIBLE)) + ) + ) + ) + ) + )); + $this->addDisplayGroup( + array('start', 'end'), + 'start-end', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')) + ) + ) + ); + if (isset($formData['type']) && $formData['type'] === self::FLEXIBLE) { + $this->addElements(array( + new Number( + 'hours', + array( + 'required' => true, + 'label' => mt('monitoring', 'Hours'), + 'value' => 2, + 'min' => -1 + ) + ), + new Number( + 'minutes', + array( + 'required' => true, + 'label' => mt('monitoring', 'Minutes'), + 'value' => 0, + 'min' => -1 + ) + ) + )); + $this->addDisplayGroup( + array('hours', 'minutes'), + 'duration', + array( + 'legend' => mt('monitoring', 'Flexible Duration'), + 'description' => mt( + 'monitoring', + 'Enter here the duration of the downtime. The downtime will be automatically deleted after this' + . ' time expired.' + ), + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')), + array( + 'Description', + array('tag' => 'span', 'class' => 'description', 'placement' => 'prepend') + ), + 'Fieldset' + ) + ) + ); + } + return $this; + } + + public function scheduleDowntime(ScheduleServiceDowntimeCommand $downtime, Request $request) + { + $downtime + ->setComment($this->getElement('comment')->getValue()) + ->setAuthor($request->getUser()->getUsername()) + ->setStart($this->getElement('start')->getValue()->getTimestamp()) + ->setEnd($this->getElement('end')->getValue()->getTimestamp()); + if ($this->getElement('type')->getValue() === self::FLEXIBLE) { + $downtime->setFixed(false); + $downtime->setDuration( + (float) $this->getElement('hours')->getValue() * 3600 + + (float) $this->getElement('minutes')->getValue() * 60 + ); + } + $this->getTransport($request)->send($downtime); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $downtime = new ScheduleServiceDowntimeCommand(); + $downtime->setObject($object); + $this->scheduleDowntime($downtime, $request); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling service downtime..', + 'Scheduling service downtimes..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php new file mode 100644 index 000000000..8e5e5408c --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php @@ -0,0 +1,127 @@ +setAttrib('class', 'inline object-features'); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS, + array( + 'label' => mt('monitoring', 'Active Checks'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS, + array( + 'label' => mt('monitoring', 'Passive Checks'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_OBSESSING, + array( + 'label' => mt('monitoring', 'Obsessing'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS, + array( + 'label' => mt('monitoring', 'Notifications'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER, + array( + 'label' => mt('monitoring', 'Event Handler'), + 'autosubmit' => true + ) + ), + array( + 'checkbox', + ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION, + array( + 'label' => mt('monitoring', 'Flap Detection'), + 'autosubmit' => true + ) + ) + )); + return $this; + } + + /** + * Load feature status + * + * @param MonitoredObject $object + * + * @return $this + */ + public function load(MonitoredObject $object) + { + $this->create(); + foreach ($this->getValues() as $feature => $enabled) { + $element = $this->getElement($feature); + $element->setChecked($object->{$feature}); + if ((bool) $object->{$feature . '_changed'} === true) { + $element->setDescription(mt('monitoring', 'changed')); + } + } + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess(Request $request) + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + foreach ($this->getValues() as $feature => $enabled) { + if ((bool) $object->{$feature} !== (bool) $enabled) { + $toggleFeature = new ToggleObjectFeatureCommand(); + $toggleFeature + ->setFeature($feature) + ->setObject($object) + ->setEnabled($enabled); + $this->getTransport($request)->send($toggleFeature); + } + } + } + Notification::success(mt('monitoring', 'Toggling feature..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php b/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php deleted file mode 100644 index cb7ab90f1..000000000 --- a/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php +++ /dev/null @@ -1,82 +0,0 @@ -addNote( - t( - 'This command is used to schedule the next check of hosts/services. Icinga will re-queue the ' - . 'check at the time you specify.' - ) - ); - - $this->addElement( - new DateTimePicker( - array( - 'name' => 'checktime', - 'label' => t('Check Time'), - 'patterns' => $this->getValidDateTimeFormats(), - 'value' => DateTimeFactory::create()->getTimestamp(), - 'required' => !$this->getRequest()->getPost('forcecheck'), - 'helptext' => t('Set the date/time when this check should be executed.') - ) - ) - ); - - $this->addElement( - new Zend_Form_Element_Checkbox( - array( - 'name' => 'forcecheck', - 'label' => t('Force Check'), - 'value' => true, - 'helptext' => t( - 'If you select this option, Icinga will force a check regardless of both what time the ' - . 'scheduled check occurs and whether or not checks are enabled.' - ) - ) - ) - ); - - // TODO: As of the time of writing it's possible to set hosts AND services as affected by this command but - // with children only makes sense on hosts - if ($this->getWithChildren() === true) { - $this->addNote(t('TODO: Help message when with children is enabled')); - } else { - $this->addNote(t('TODO: Help message when with children is disabled')); - } - - $this->setSubmitLabel(t('Reschedule Check')); - - parent::create(); - } - - /** - * Create the command object to schedule checks - * - * @return ScheduleCheckCommand - */ - public function createCommand() - { - $command = new ScheduleCheckCommand( - $this->getValue('checktime'), - $this->getValue('forcecheck') - ); - return $command->excludeHost($this->getWithChildren()); - } -} diff --git a/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php b/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php deleted file mode 100644 index 73ce2666c..000000000 --- a/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php +++ /dev/null @@ -1,336 +0,0 @@ -setName('ScheduleDowntimeForm'); - } - - /** - * Generate translated multi options based on type constants - * - * @return array - */ - private function getDowntimeTypeOptions() - { - return array( - self::TYPE_FIXED => t('Fixed'), - self::TYPE_FLEXIBLE => t('Flexible') - ); - } - - /** - * Fetch all available downtimes from the database - * - * @return array - */ - private function getCurrentDowntimes() - { - if (isset($this->downtimes)) { - return $this->downtimes; - } - - $cfg = $this->getConfiguration(); - $preferences = $this->getUserPreferences(); - $object = MonitoredObject::fromParams(Url::fromRequest()->getParams()); - $object->fetchDowntimes(); - $downtimes = $object->downtimes; -/* - - $downtimes = Backend::createBackend($this->getRequest()->getParam('backend'))->select() - ->from( - 'downtime', - array( - 'host', - 'service', - 'downtime_start', - 'downtime_scheduled_start_time', - 'downtime_internal_downtime_id' - ) - )->fetchAll(); -*/ - $options = array( - '0' => 'No Triggered Downtime' - ); - $dateFormat = $this->getView()->dateFormat(); - foreach ($downtimes as $downtime) { - $label = sprintf( - 'ID %s: %s%s Starting @ %s', - $downtime->id, - $object->host_name, - $object instanceof Service ? ' (' . $object->service_description . ')' : '', - $dateFormat->formatDateTime($downtime->scheduled_start) - ); - $options[$downtime->id] = $label; - } - return $options; - } - - /** - * Set the downtimes displayed by this form (used for testing) - * - * @param array $downtimes list of strings - */ - public function setCurrentDowntimes($downtimes) - { - $this->downtimes = $downtimes; - } - - /** - * Create the form's elements - */ - protected function create() - { - $this->addNote( - t( - 'This command is used to schedule downtime for hosts/services. During the specified downtime, ' - . 'Icinga will not send notifications out about the affected objects. When the scheduled ' - . 'downtime expires, Icinga will send out notifications as it normally would. Scheduled ' - . 'downtimes are preserved across program shutdowns and restarts.' - ) - ); - - $this->addElement($this->createAuthorField()); - - $this->addElement( - 'textarea', - 'comment', - array( - 'label' => t('Comment'), - 'rows' => 4, - 'cols' => 72, - 'required' => true, - 'helptext' => t( - 'If you work with other administrators, you may find it useful to share information ' - . 'about a host or service that is having problems if more than one of you may be working on ' - . 'it. Make sure you enter a brief description of what you are doing.' - ) - ) - ); - - $this->addElement( - 'select', - 'triggered', - array( - 'label' => t('Triggered by'), - 'required' => true, - 'multiOptions' => $this->getCurrentDowntimes() - ) - ); - - $now = DateTimeFactory::create(); - - $this->addElement( - new DateTimePicker( - array( - 'name' => 'starttime', - 'label' => t('Start Time'), - 'value' => $now->getTimestamp(), - 'patterns' => $this->getValidDateTimeFormats(), - 'helptext' => t('Set the start date/time for the downtime.') - ) - ) - ); - $this->addElement( - new DateTimePicker( - array( - 'name' => 'endtime', - 'label' => t('End Time'), - 'value' => $now->getTimestamp() + 3600, - 'patterns' => $this->getValidDateTimeFormats(), - 'helptext' => t('Set the end date/time for the downtime.') - ) - ) - ); - - $this->addElement( - 'select', - 'type', - array( - 'label' => t('Downtime Type'), - 'multiOptions' => $this->getDowntimeTypeOptions(), - 'required' => true, - 'validators' => array( - array( - 'InArray', - true, - array( - array_keys($this->getDowntimeTypeOptions()) - ) - ) - ), - 'helptext' => t( - 'If you select the fixed option, the downtime will be in effect between the start and end ' - . 'times you specify whereas a flexible downtime starts when the service enters a non-OK state ' - . '(sometime between the start and end times you specified) and lasts as long as the duration ' - . 'of time you enter. The duration fields do not apply for fixed downtime.' - ) - ) - ); - $this->enableAutoSubmit(array('type')); - - if ($this->getRequest()->getPost('type') === self::TYPE_FLEXIBLE) { - $hoursText = new Zend_Form_Element_Text('hours'); - $hoursText->setLabel(t('Flexible Duration')); - $hoursText->setAttrib('style', 'width: 40px;'); - $hoursText->setValue(1); - $hoursText->addDecorator('HtmlTag', array('tag' => 'dd', 'openOnly' => true)); - $hoursText->addDecorator( - 'Callback', - array( - 'callback' => function () { - return t('Hours'); - } - ) - ); - $minutesText = new Zend_Form_Element_Text('minutes'); - $minutesText->setLabel(t('Minutes')); - $minutesText->setAttrib('style', 'width: 40px;'); - $minutesText->setValue(0); - $minutesText->removeDecorator('HtmlTag'); - $minutesText->removeDecorator('Label'); - $minutesText->addDecorator( - 'Callback', - array( - 'callback' => function () { - return t('Minutes'); - } - ) - ); - $this->addElements(array($hoursText, $minutesText)); - $this->addNote( - t( - 'Enter here the duration of the downtime. Icinga will automatically delete the downtime ' - . 'after this time expired.' - ) - ); - } - - // TODO: As of the time of writing it's possible to set hosts AND services as affected by this command but - // with children only makes sense on hosts - if ($this->getWithChildren() === true) { - $this->addNote(t('TODO: Help message when with children is enabled')); - } else { - $this->addElement( - 'select', - 'childobjects', - array( - 'label' => t('Child Objects'), - 'required' => true, - 'multiOptions' => array( - 0 => t('Do nothing with child objects'), - 1 => t('Schedule triggered downtime for all child objects'), - 2 => t('Schedule non-triggered downtime for all child objects') - ), - 'validators' => array( - array( - 'Digits', - true - ), - array( - 'InArray', - true, - array( - array(0, 1, 2) - ) - ) - ) - ) - ); - $this->addNote(t('TODO: Help message when with children is disabled')); - } - - $this->setSubmitLabel(t('Schedule Downtime')); - - parent::create(); - } - - /** - * Change validators at runtime - * - * @param array $data The user provided data that will go into validation - * - * @see Form::preValidation - */ - protected function preValidation(array $data) - { - /* - * Other values needed when downtime type change to flexible - */ - if (isset($data['type']) && $data['type'] === self::TYPE_FLEXIBLE) { - $greaterThanValidator = new Zend_Validate_GreaterThan(-1); - $digitsValidator = new Zend_Validate_Digits(); - - $hours = $this->getElement('hours'); - $hours->setRequired(true); - $hours->addValidator($digitsValidator, true); - $hours->addValidator($greaterThanValidator, true); - - $minutes = $this->getElement('minutes'); - $minutes->setRequired(true); - $minutes->addValidator($digitsValidator, true); - $minutes->addValidator($greaterThanValidator, true); - } - } - - /** - * Create ScheduleDowntimeCommand object - * - * @return ScheduleDowntimeCommand - */ - public function createCommand() - { - // TODO: Add support for host-/servicegroups and services only (#4588) - $command = new ScheduleDowntimeCommand( - $this->getValue('starttime'), - $this->getValue('endtime'), - new Comment( - $this->getRequest()->getUser()->getUsername(), - $this->getValue('comment') - ), - $this->getValue('type') === self::TYPE_FIXED, - $this->getValue('hours') * 3600 + $this->getValue('minutes') * 60, - $this->getValue('triggered') - ); - - return $command->includeChildren( - $this->getWithChildren(), - $this->getValue('childobjects') === 1 - ); - } -} diff --git a/modules/monitoring/application/forms/Command/SingleArgumentCommandForm.php b/modules/monitoring/application/forms/Command/SingleArgumentCommandForm.php deleted file mode 100644 index d7228ebdf..000000000 --- a/modules/monitoring/application/forms/Command/SingleArgumentCommandForm.php +++ /dev/null @@ -1,169 +0,0 @@ -hostCommand = $hostCommand; - - if ($serviceCommand !== null) { - $this->serviceCommand = $serviceCommand; - } - } - - /** - * Setter for global commands - * - * @param string $hostOrGenericGlobalCommand Generic command or one for host - * @param string $serviceGlobalCommand If any (leave blank if you need a global global) - */ - public function setGlobalCommands($hostOrGenericGlobalCommand, $serviceGlobalCommand = null) - { - $this->globalCommands[] = $hostOrGenericGlobalCommand; - - if ($serviceGlobalCommand !== null) { - $this->globalCommands[] = $serviceGlobalCommand; - } - } - - /** - * Use an explicit value to send with command - * - * @param mixed $parameterValue - */ - public function setParameterValue($parameterValue) - { - $this->parameterValue = $parameterValue; - } - - /** - * Use a form field to take the value from - * - * @param string $parameterName - */ - public function setParameterName($parameterName) - { - $this->parameterName = $parameterName; - } - - /** - * Flag to ignore every objects - * - * @param bool $flag - */ - public function setObjectIgnoreFlag($flag = true) - { - $this->ignoreObject = (bool) $flag; - } - - /** - * - */ - protected function create() - { - if ($this->parameterName) { - $field = new Zend_Form_Element_Hidden($this->parameterName); - $value = $this->getRequest()->getParam($field->getName()); - $field->setValue($value); - $this->addElement($field); - } - parent::create(); - } - - public function setRequest(Zend_Controller_Request_Abstract $request) - { - parent::setRequest($request); - - if ($this->globalCommand === true) { - $this->setParameterName('global'); - } - } - - - /** - * Create command object for CommandPipe protocol - * - * @return SingleArgumentCommand - */ - public function createCommand() - { - $command = new SingleArgumentCommand(); - - if ($this->parameterValue !== null) { - $command->setValue($this->parameterValue); - } else { - $command->setValue($this->getValue($this->parameterName)); - } - - if ($this->provideGlobalCommand() == true) { - $command->setGlobalCommands($this->globalCommands); - $this->ignoreObject = true; - } else { - $command->setCommand($this->hostCommand, $this->serviceCommand); - } - - $command->setObjectIgnoreFlag($this->ignoreObject); - - return $command; - } -} diff --git a/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php b/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php deleted file mode 100644 index c2c9151f3..000000000 --- a/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php +++ /dev/null @@ -1,177 +0,0 @@ - array( - 0 => t('UP'), - 1 => t('DOWN'), - 2 => t('UNREACHABLE') - ), - self::TYPE_SERVICE => array( - 0 => t('OK'), - 1 => t('WARNING'), - 2 => t('CRITICAL'), - 3 => t('UNKNOWN') - ) - ); - } - - parent::init(); - } - - /** - * Setter for type - * - * @param string $type - */ - public function setType($type) - { - $this->type = $type; - } - - /** - * Getter for type - * - * @return string - */ - public function getType() - { - return $this->type; - } - - /** - * Return array of options - * - * @return array - * @throws \Icinga\Exception\ProgrammingError - */ - public function getOptions() - { - if (in_array($this->getType(), array(self::TYPE_HOST, self::TYPE_SERVICE)) === false) { - throw new ProgrammingError('Type is not valid'); - } - - return self::$options[$this->getType()]; - } - - /** - * Create the form's elements - */ - protected function create() - { - $this->setName('form_submit_passive_checkresult'); - - $this->addNote( - t( - 'This command is used to submit a passive check result for particular hosts/services. It is ' - . 'particularly useful for resetting security-related objects to OK states once they have been ' - . 'dealt with.' - ) - ); - - $this->addElement( - 'select', - 'pluginstate', - array( - 'label' => t('Check Result'), - 'multiOptions' => $this->getOptions(), - 'required' => true, - 'validators' => array( - array( - 'Digits', - true - ), - array( - 'InArray', - true, - array( - array_keys($this->getOptions()) - ) - ) - ), - 'helptext' => t('Set the state which should be send to Icinga for this objects.') - ) - ); - - $this->addElement( - 'textarea', - 'checkoutput', - array( - 'label' => t('Check Output'), - 'rows' => 2, - 'cols' => 72, - 'required' => true, - 'helptext' => t('Fill in the check output string which should be send to Icinga.') - ) - ); - - $this->addElement( - 'textarea', - 'performancedata', - array( - 'label' => t('Performance Data'), - 'rows' => 2, - 'cols' => 72, - 'helptext' => t('Fill in the performance data string which should be send to Icinga.') - ) - ); - - $this->setSubmitLabel(t('Submit Passive Check Result')); - - parent::create(); - } - - /** - * Create the submit passive checkresult command object - * - * @return SubmitPassiveCheckresultCommand - */ - public function createCommand() - { - return new SubmitPassiveCheckresultCommand( - $this->getValue('pluginstate'), - $this->getValue('checkoutput'), - $this->getValue('performancedata') - ); - } -} diff --git a/modules/monitoring/application/forms/Command/WithChildrenCommandForm.php b/modules/monitoring/application/forms/Command/WithChildrenCommandForm.php deleted file mode 100644 index 5ae69173e..000000000 --- a/modules/monitoring/application/forms/Command/WithChildrenCommandForm.php +++ /dev/null @@ -1,37 +0,0 @@ -withChildren = $flag; - } - - /** - * Getter for withChildren - * - * @return bool - */ - public function getWithChildren() - { - return $this->withChildren; - } -} diff --git a/modules/monitoring/application/forms/Config/Backend/CreateBackendForm.php b/modules/monitoring/application/forms/Config/Backend/CreateBackendForm.php deleted file mode 100644 index 6acc210ae..000000000 --- a/modules/monitoring/application/forms/Config/Backend/CreateBackendForm.php +++ /dev/null @@ -1,45 +0,0 @@ -setBackendConfiguration(new Zend_Config(array('type' => 'ido'))); - $this->addElement( - 'text', - 'backend_name', - array( - 'label' => 'Backend Name', - 'required' => true, - 'helptext' => 'This will be the identifier of this backend' - ) - ); - parent::create(); - } - - /** - * Return the name of the backend that is to be created - * - * @return string The name of the backend as entered in the form - */ - public function getBackendName() - { - return $this->getValue('backend_name'); - } -} diff --git a/modules/monitoring/application/forms/Config/Backend/EditBackendForm.php b/modules/monitoring/application/forms/Config/Backend/EditBackendForm.php deleted file mode 100644 index 52e420a76..000000000 --- a/modules/monitoring/application/forms/Config/Backend/EditBackendForm.php +++ /dev/null @@ -1,148 +0,0 @@ -backend = $config; - } - - /** - * Set a custom array of resources to be used in this form instead of the ones from ResourceFactory - * (used for testing) - */ - public function setResources($resources) - { - $this->resources = $resources; - } - - /** - * Return content of the resources.ini or previously set resources for displaying in the database selection field - * - * @return array - */ - public function getResources() - { - if ($this->resources === null) { - return ResourceFactory::getResourceConfigs()->toArray(); - } else { - return $this->resources; - } - } - - /** - * Return a list of all resources of the given type ready to be used as content for a select input - * - * @param string $type The type of resources to return - * - * @return array - */ - protected function getResourcesByType($type) - { - $backends = array(); - foreach ($this->getResources() as $name => $resource) { - if ($resource['type'] === $type) { - $backends[$name] = $name; - } - } - - return $backends; - } - - /** - * Create this form - * - * @see Icinga\Web\Form::create() - */ - public function create() - { - $backendType = $this->getRequest()->getParam('backend_type', $this->backend->type); - - $this->addElement( - 'select', - 'backend_type', - array( - 'label' => 'Backend Type', - 'value' => $this->backend->type, - 'required' => true, - 'helptext' => 'The data source used for retrieving monitoring information', - 'multiOptions' => array( - 'ido' => 'IDO Backend', - 'statusdat' => 'Status.dat', - 'livestatus' => 'Livestatus' - ) - ) - ); - $this->addElement( - 'select', - 'backend_resource', - array( - 'label' => 'Resource', - 'value' => $this->backend->resource, - 'required' => true, - 'multiOptions' => $this->getResourcesByType($backendType === 'ido' ? 'db' : $backendType), - 'helptext' => 'The resource to use' - ) - ); - $this->addElement( - 'checkbox', - 'backend_disable', - array( - 'label' => 'Disable This Backend', - 'required' => true, - 'value' => $this->backend->disabled - ) - ); - - $this->enableAutoSubmit(array('backend_type')); - $this->setSubmitLabel('{{SAVE_ICON}} Save Changes'); - } - - /** - * Return a configuration containing the backend settings entered in this form - * - * @return Zend_Config The updated configuration for this backend - */ - public function getConfig() - { - $values = $this->getValues(); - return new Zend_Config( - array( - 'type' => $values['backend_type'], - 'disabled' => $values['backend_disable'], - 'resource' => $values['backend_resource'] - ) - ); - } -} diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php new file mode 100644 index 000000000..079ea538a --- /dev/null +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -0,0 +1,239 @@ +setName('form_config_monitoring_backends'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * Set the resource configuration to use + * + * @param Config $resources The resource configuration + * + * @return self + * + * @throws ConfigurationError In case there are no valid monitoring backend resources + */ + public function setResourceConfig(Config $resourceConfig) + { + $resources = array(); + foreach ($resourceConfig as $name => $resource) { + if ($resource->type === 'db' || $resource->type === 'livestatus') { + $resources[$resource->type === 'db' ? 'ido' : 'livestatus'][$name] = $name; + } + } + + if (empty($resources)) { + throw new ConfigurationError(t('Could not find any valid monitoring backend resources')); + } + + $this->resources = $resources; + return $this; + } + + /** + * Add a particular monitoring backend + * + * The backend to add is identified by the array-key `name'. + * + * @param array $values The values to extend the configuration with + * + * @return self + * + * @throws InvalidArgumentException In case the backend does already exist + */ + public function add(array $values) + { + $name = isset($values['name']) ? $values['name'] : ''; + if (! $name) { + throw new InvalidArgumentException(t('Monitoring backend name missing')); + } elseif ($this->config->get($name) !== null) { + throw new InvalidArgumentException(t('Monitoring backend already exists')); + } + + unset($values['name']); + $this->config->{$name} = $values; + return $this; + } + + /** + * Edit a particular monitoring backend + * + * @param string $name The name of the backend to edit + * @param array $values The values to edit the configuration with + * + * @return array The edited backend configuration + * + * @throws InvalidArgumentException In case the backend does not exist + */ + public function edit($name, array $values) + { + if (! $name) { + throw new InvalidArgumentException(t('Old monitoring backend name missing')); + } elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) { + throw new InvalidArgumentException(t('New monitoring backend name missing')); + } elseif (($backendConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown monitoring backend provided')); + } + + unset($values['name']); + unset($this->config->{$name}); + $this->config->{$newName} = $values; + return $this->config->{$newName}; + } + + /** + * Remove the given monitoring backend + * + * @param string $name The name of the backend to remove + * + * @return array The removed backend configuration + * + * @throws InvalidArgumentException In case the backend does not exist + */ + public function remove($name) + { + if (! $name) { + throw new InvalidArgumentException(t('Monitoring backend name missing')); + } elseif (($backendConfig = $this->config->get($name)) === null) { + throw new InvalidArgumentException(t('Unknown monitoring backend provided')); + } + + unset($this->config->{$name}); + return $backendConfig; + } + + /** + * Add or edit a monitoring backend and save the configuration + * + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $monitoringBackend = $request->getQuery('backend'); + try { + if ($monitoringBackend === null) { // create new backend + $this->add($this->getValues()); + $message = t('Monitoring backend "%s" has been successfully created'); + } else { // edit existing backend + $this->edit($monitoringBackend, $this->getValues()); + $message = t('Monitoring backend "%s" has been successfully changed'); + } + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } + + if ($this->save()) { + Notification::success(sprintf($message, $this->getElement('name')->getValue())); + } else { + return false; + } + } + + /** + * Populate the form in case a monitoring backend is being edited + * + * @see Form::onRequest() + * + * @throws ConfigurationError In case the backend name is missing in the request or is invalid + */ + public function onRequest(Request $request) + { + $monitoringBackend = $request->getQuery('backend'); + if ($monitoringBackend !== null) { + if ($monitoringBackend === '') { + throw new ConfigurationError(t('Monitoring backend name missing')); + } elseif (false === isset($this->config->{$monitoringBackend})) { + throw new ConfigurationError(t('Unknown monitoring backend provided')); + } + + $backendConfig = $this->config->{$monitoringBackend}->toArray(); + $backendConfig['name'] = $monitoringBackend; + $this->populate($backendConfig); + } + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $resourceType = isset($formData['type']) ? $formData['type'] : key($this->resources); + + $resourceTypes = array(); + if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) { + $resourceTypes['ido'] = 'IDO Backend'; + } + if ($resourceType === 'livestatus' || array_key_exists('livestatus', $this->resources)) { + $resourceTypes['livestatus'] = 'Livestatus'; + } + + $this->addElement( + 'checkbox', + 'disabled', + array( + 'required' => true, + 'label' => t('Disable This Backend') + ) + ); + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => t('Backend Name'), + 'description' => t('The identifier of this backend') + ) + ); + $this->addElement( + 'select', + 'type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => t('Backend Type'), + 'description' => t('The data source used for retrieving monitoring information'), + 'multiOptions' => $resourceTypes, + 'value' => $resourceType + ) + ); + $this->addElement( + 'select', + 'resource', + array( + 'required' => true, + 'label' => t('Resource'), + 'description' => t('The resource to use'), + 'multiOptions' => $this->resources[$resourceType] + ) + ); + } +} diff --git a/modules/monitoring/application/forms/Config/ConfirmRemovalForm.php b/modules/monitoring/application/forms/Config/ConfirmRemovalForm.php deleted file mode 100644 index 6a9e17bd5..000000000 --- a/modules/monitoring/application/forms/Config/ConfirmRemovalForm.php +++ /dev/null @@ -1,68 +0,0 @@ -targetName = $name; - $this->removeTarget = $target; - } - - /** - * Create the confirmation form - * - * @see Form::create() - */ - public function create() - { - $this->addElement( - 'hidden', - $this->targetName, - array( - 'value' => $this->removeTarget, - 'required' => true - ) - ); - - $this->addElement( - 'button', - 'btn_submit', - array( - 'type' => 'submit', - 'escape' => false, - 'value' => '1', - 'class' => 'btn btn-cta btn-common', - 'label' => $this->getView()->icon('remove.png') . ' Confirm Removal' - ) - ); - } -} diff --git a/modules/monitoring/application/forms/Config/Instance/CreateInstanceForm.php b/modules/monitoring/application/forms/Config/Instance/CreateInstanceForm.php deleted file mode 100644 index 077e02183..000000000 --- a/modules/monitoring/application/forms/Config/Instance/CreateInstanceForm.php +++ /dev/null @@ -1,46 +0,0 @@ -setInstanceConfiguration(new Zend_Config(array())); - $this->addElement( - 'text', - 'instance_name', - array( - 'label' => 'Instance Name', - 'helptext' => 'Please enter the name for the instance to create' - ) - ); - parent::create(); - } - - /** - * Return the name of the instance to be created - * - * @return string The name of the instance as entered in the form - */ - public function getInstanceName() - { - return $this->getValue('instance_name'); - } -} diff --git a/modules/monitoring/application/forms/Config/Instance/EditInstanceForm.php b/modules/monitoring/application/forms/Config/Instance/EditInstanceForm.php deleted file mode 100644 index f74b091df..000000000 --- a/modules/monitoring/application/forms/Config/Instance/EditInstanceForm.php +++ /dev/null @@ -1,148 +0,0 @@ -instance = $config; - if (isset($this->instance->host)) { - $this->instanceType = 'remote'; - } - } - - /** - * Add a form field for selecting the command pipe type (local or remote) - */ - private function addTypeSelection() - { - $this->addElement( - 'select', - 'instance_type', - array( - 'value' => $this->instanceType, - 'multiOptions' => array( - 'local' => 'Local Command Pipe', - 'remote' => 'Remote Command Pipe' - ) - ) - ); - $this->enableAutoSubmit(array('instance_type')); - } - - /** - * Add form elements for remote instance - */ - private function addRemoteInstanceForm() - { - $this->addNote('When configuring a remote host, you need to setup passwordless key authentication'); - - $this->addElement( - 'text', - 'instance_remote_host', - array( - 'label' => 'Remote Host', - 'required' => true, - 'value' => $this->instance->host, - 'helptext' => 'Enter the hostname or address of the machine on which the icinga instance is running' - ) - ); - - $this->addElement( - 'text', - 'instance_remote_port', - array( - 'label' => 'Remote SSH Port', - 'required' => true, - 'value' => $this->instance->get('port', 22), - 'helptext' => 'Enter the ssh port to use for connecting to the remote icigna instance' - ) - ); - - $this->addElement( - 'text', - 'instance_remote_user', - array( - 'label' => 'Remote SSH User', - 'value' => $this->instance->user, - 'helptext' => 'Enter the username to use for connecting ' - . 'to the remote machine or leave blank for default' - ) - ); - } - - /** - * Create this form - * - * @see Icinga\Web\Form::create - */ - public function create() - { - $this->addTypeSelection(); - if ($this->getRequest()->getParam('instance_type', $this->instanceType) === 'remote') { - $this->addRemoteInstanceForm(); - } - $this->addElement( - 'text', - 'instance_path', - array( - 'label' => 'Remote Pipe Filepath', - 'required' => true, - 'value' => $this->instance->get('path', '/usr/local/icinga/var/rw/icinga.cmd'), - 'helptext' => 'The file path where the icinga commandpipe can be found' - ) - ); - $this->setSubmitLabel('{{SAVE_ICON}} Save'); - } - - /** - * Return the configuration set by this form - * - * @return Zend_Config The configuration set in this form - */ - public function getConfig() - { - $values = $this->getValues(); - $config = array( - 'path' => $values['instance_path'] - ); - if ($values['instance_type'] === 'remote') { - $config['host'] = $values['instance_remote_host']; - $config['port'] = $values['instance_remote_port']; - if (isset($values['instance_remote_user']) && $values['instance_remote_user'] != '') { - $config['user'] = $values['instance_remote_user']; - } - } - return new Zend_Config($config); - } -} diff --git a/modules/monitoring/application/forms/Config/Instance/LocalInstanceForm.php b/modules/monitoring/application/forms/Config/Instance/LocalInstanceForm.php new file mode 100644 index 000000000..513ba2433 --- /dev/null +++ b/modules/monitoring/application/forms/Config/Instance/LocalInstanceForm.php @@ -0,0 +1,37 @@ +setName('form_config_monitoring_instance_local'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'path', + array( + 'required' => true, + 'label' => t('Local Filepath'), + 'value' => '/usr/local/icinga/var/rw/icinga.cmd', + 'description' => t('The file path where the icinga commandpipe can be found') + ) + ); + + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/Instance/RemoteInstanceForm.php b/modules/monitoring/application/forms/Config/Instance/RemoteInstanceForm.php new file mode 100644 index 000000000..0e78a0aec --- /dev/null +++ b/modules/monitoring/application/forms/Config/Instance/RemoteInstanceForm.php @@ -0,0 +1,68 @@ +setName('form_config_monitoring_instance_remote'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'host', + array( + 'required' => true, + 'label' => t('Remote Host'), + 'description' => t( + 'Enter the hostname or address of the machine on which the icinga instance is running' + ) + ) + ); + $this->addElement( + new Number( + array( + 'required' => true, + 'name' => 'port', + 'label' => t('Remote SSH Port'), + 'description' => t('Enter the ssh port to use for connecting to the remote icinga instance'), + 'value' => 22 + ) + ) + ); + $this->addElement( + 'text', + 'user', + array( + 'required' => true, + 'label' => t('Remote SSH User'), + 'description' => t( + 'Enter the username to use for connecting to the remote machine or leave blank for default' + ) + ) + ); + $this->addElement( + 'text', + 'path', + array( + 'required' => true, + 'label' => t('Remote Filepath'), + 'value' => '/usr/local/icinga/var/rw/icinga.cmd', + 'description' => t('The file path where the icinga commandpipe can be found') + ) + ); + + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/InstanceConfigForm.php b/modules/monitoring/application/forms/Config/InstanceConfigForm.php new file mode 100644 index 000000000..6f8d04764 --- /dev/null +++ b/modules/monitoring/application/forms/Config/InstanceConfigForm.php @@ -0,0 +1,209 @@ +setName('form_config_monitoring_instance'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * Return a form object for the given instance type + * + * @param string $type The instance type for which to return a form + * + * @return Form + * + * @throws InvalidArgumentException In case the given instance type is invalid + */ + public function getInstanceForm($type) + { + if ($type === 'local') { + return new LocalInstanceForm(); + } elseif ($type === 'remote') { + return new RemoteInstanceForm(); + } else { + throw new InvalidArgumentException(sprintf(t('Invalid instance type "%s" provided'), $type)); + } + } + + /** + * Add a new instance + * + * The resource to add is identified by the array-key `name'. + * + * @param array $values The values to extend the configuration with + * + * @return self + * + * @throws InvalidArgumentException In case the resource already exists + */ + public function add(array $values) + { + $name = isset($values['name']) ? $values['name'] : ''; + if (! $name) { + throw new InvalidArgumentException(t('Instance name missing')); + } elseif ($this->config->get($name) !== null) { + throw new InvalidArgumentException(t('Instance already exists')); + } + + unset($values['name']); + $this->config->{$name} = $values; + return $this; + } + + /** + * Edit an existing instance + * + * @param string $name The name of the resource to edit + * @param array $values The values to edit the configuration with + * + * @return array The edited resource configuration + * + * @throws InvalidArgumentException In case the resource name is missing or invalid + */ + public function edit($name, array $values) + { + if (! $name) { + throw new InvalidArgumentException(t('Old instance name missing')); + } elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) { + throw new InvalidArgumentException(t('New instance name missing')); + } elseif (! ($instanceConfig = $this->config->get($name))) { + throw new InvalidArgumentException(t('Unknown instance name provided')); + } + + unset($values['name']); + unset($this->config->{$name}); + $this->config->{$newName} = array_merge($instanceConfig->toArray(), $values); + return $this->config->{$newName}; + } + + /** + * Remove a instance + * + * @param string $name The name of the resource to remove + * + * @return array The removed resource confguration + * + * @throws InvalidArgumentException In case the resource name is missing or invalid + */ + public function remove($name) + { + if (! $name) { + throw new InvalidArgumentException(t('Instance name missing')); + } elseif (! ($instanceConfig = $this->config->get($name))) { + throw new InvalidArgumentException(t('Unknown instance name provided')); + } + + unset($this->config->{$name}); + return $instanceConfig; + } + + /** + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $instanceName = $request->getQuery('instance'); + + try { + if ($instanceName === null) { // create new instance + $this->add($this->getValues()); + $message = t('Instance "%s" created successfully.'); + } else { // edit existing instance + $this->edit($instanceName, $this->getValues()); + $message = t('Instance "%s" edited successfully.'); + } + } catch (InvalidArgumentException $e) { + Notification::error($e->getMessage()); + return; + } + + if ($this->save()) { + Notification::success(sprintf($message, $this->getElement('name')->getValue())); + } else { + return false; + } + } + + /** + * @see Form::onRequest() + * + * @throws ConfigurationError In case the instance name is missing or invalid + */ + public function onRequest(Request $request) + { + $instanceName = $request->getQuery('instance'); + if ($instanceName !== null) { + if (! $instanceName) { + throw new ConfigurationError(t('Instance name missing')); + } elseif (false === isset($this->config->{$instanceName})) { + throw new ConfigurationError(t('Unknown instance name provided')); + } + + $instanceConfig = $this->config->{$instanceName}->toArray(); + $instanceConfig['name'] = $instanceName; + if (isset($instanceConfig['host'])) { + // Necessary as we have no config directive for setting the instance's type + $instanceConfig['type'] = 'remote'; + } + $this->populate($instanceConfig); + } + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $instanceType = isset($formData['type']) ? $formData['type'] : 'local'; + + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => t('Instance Name') + ) + ); + $this->addElement( + 'select', + 'type', + array( + 'required' => true, + 'ignore' => true, + 'autosubmit' => true, + 'label' => t('Instance Type'), + 'description' => t( + 'When configuring a remote host, you need to setup passwordless key authentication' + ), + 'multiOptions' => array( + 'local' => t('Local Command Pipe'), + 'remote' => t('Remote Command Pipe') + ), + 'value' => $instanceType + ) + ); + + $this->addElements($this->getInstanceForm($instanceType)->createElements($formData)->getElements()); + } +} diff --git a/modules/monitoring/application/forms/Config/SecurityConfigForm.php b/modules/monitoring/application/forms/Config/SecurityConfigForm.php new file mode 100644 index 000000000..e0cc68680 --- /dev/null +++ b/modules/monitoring/application/forms/Config/SecurityConfigForm.php @@ -0,0 +1,68 @@ +setName('form_config_monitoring_security'); + $this->setSubmitLabel(t('Save Changes')); + } + + /** + * @see Form::onSuccess() + */ + public function onSuccess(Request $request) + { + $this->config->security = $this->getValues(); + + if ($this->save()) { + Notification::success(t('New security configuration has successfully been stored')); + } else { + return false; + } + } + + /** + * @see Form::onRequest() + */ + public function onRequest(Request $request) + { + if (isset($this->config->security)) { + $this->populate($this->config->security->toArray()); + } + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'protected_customvars', + array( + 'required' => true, + 'label' => t('Protected Custom Variables'), + 'description' => t( + 'Comma separated case insensitive list of protected custom variables.' + . ' Use * as a placeholder for zero or more wildcard characters.' + . ' Existance of those custom variables will be shown, but their values will be masked.' + ) + ) + ); + } +} diff --git a/modules/monitoring/application/forms/Config/SecurityForm.php b/modules/monitoring/application/forms/Config/SecurityForm.php deleted file mode 100644 index b0749868d..000000000 --- a/modules/monitoring/application/forms/Config/SecurityForm.php +++ /dev/null @@ -1,60 +0,0 @@ -addElement( - 'text', - 'protected_customvars', - array( - 'label' => 'Protected Custom Variables', - 'required' => true, - 'value' => $this->config ? $this->config->get('protected_customvars', $default) : $default, - 'helptext' => 'Comma separated case insensitive list of protected custom variables.' - . ' Use * as a placeholder for zero or more wildcard characters.' - . ' Existance of those custom variables will be shown, but their values will be masked.' - ) - ); - $this->setSubmitLabel('Save'); - } - - /** - * Set the configuration to be used for initial population of the form - */ - public function setConfiguration($config) - { - $this->config = $config; - } - - /** - * Return the configuration set by this form - * - * @return Zend_Config The configuration set in this form - */ - public function getConfig() - { - $values = $this->getValues(); - return new Zend_Config(array( - 'protected_customvars' => $values['protected_customvars'] - )); - } -} diff --git a/modules/monitoring/application/forms/StatehistoryForm.php b/modules/monitoring/application/forms/StatehistoryForm.php new file mode 100644 index 000000000..ed5710454 --- /dev/null +++ b/modules/monitoring/application/forms/StatehistoryForm.php @@ -0,0 +1,150 @@ +getValue('objecttype', 'hosts') === 'hosts') { + $objectTypeFilter = Filter::expression('object_type', '=', 'host'); + } else { + $objectTypeFilter = Filter::expression('object_type', '=', 'service'); + } + + $states = array( + 'cnt_down_hard' => Filter::expression('state', '=', '1'), + 'cnt_unreachable_hard' => Filter::expression('state', '=', '2'), + 'cnt_up' => Filter::expression('state', '=', '0'), + 'cnt_critical_hard' => Filter::expression('state', '=', '2'), + 'cnt_warning_hard' => Filter::expression('state', '=', '1'), + 'cnt_unknown_hard' => Filter::expression('state', '=', '3'), + 'cnt_ok' => Filter::expression('state', '=', '0') + ); + $state = $this->getValue('state', 'cnt_critical_hard'); + $stateFilter = $states[$state]; + if (in_array($state, array('cnt_ok', 'cnt_up'))) { + return Filter::matchAll($objectTypeFilter, $stateFilter); + } + return Filter::matchAll($baseFilter, $objectTypeFilter, $stateFilter); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'select', + 'from', + array( + 'label' => t('From'), + 'value' => $this->getRequest()->getParam('from', strtotime('3 months ago')), + 'multiOptions' => array( + strtotime('midnight 3 months ago') => t('3 Months'), + strtotime('midnight 4 months ago') => t('4 Months'), + strtotime('midnight 8 months ago') => t('8 Months'), + strtotime('midnight 12 months ago') => t('1 Year'), + strtotime('midnight 24 months ago') => t('2 Years') + ), + 'class' => 'autosubmit' + ) + ); + $this->addElement( + 'select', + 'to', + array( + 'label' => t('To'), + 'value' => $this->getRequest()->getParam('to', time()), + 'multiOptions' => array( + time() => t('Today') + ), + 'class' => 'autosubmit' + ) + ); + + $objectType = $this->getRequest()->getParam('objecttype', 'services'); + $this->addElement( + 'select', + 'objecttype', + array( + 'label' => t('Object type'), + 'value' => $objectType, + 'multiOptions' => array( + 'services' => t('Services'), + 'hosts' => t('Hosts') + ), + 'class' => 'autosubmit' + ) + ); + if ($objectType === 'services') { + $serviceState = $this->getRequest()->getParam('state', 'cnt_critical_hard'); + if (in_array($serviceState, array('cnt_down_hard', 'cnt_unreachable_hard', 'cnt_up'))) { + $serviceState = 'cnt_critical_hard'; + } + $this->addElement( + 'select', + 'state', + array( + 'label' => t('State'), + 'value' => $serviceState, + 'multiOptions' => array( + 'cnt_critical_hard' => t('Critical'), + 'cnt_warning_hard' => t('Warning'), + 'cnt_unknown_hard' => t('Unknown'), + 'cnt_ok' => t('Ok') + ), + 'class' => 'autosubmit' + ) + ); + } else { + $hostState = $this->getRequest()->getParam('state', 'cnt_down_hard'); + if (in_array($hostState, array('cnt_ok', 'cnt_critical_hard', 'cnt_warning', 'cnt_unknown'))) { + $hostState = 'cnt_down_hard'; + } + $this->addElement( + 'select', + 'state', + array( + 'label' => t('State'), + 'value' => $hostState, + 'multiOptions' => array( + 'cnt_up' => t('Up'), + 'cnt_down_hard' => t('Down'), + 'cnt_unreachable_hard' => t('Unreachable') + ), + 'class' => 'autosubmit' + ) + ); + } + $this->addElement( + 'button', + 'btn_submit', + array( + 'type' => 'submit', + 'escape' => false, + 'value' => '1', + 'class' => 'btn btn-cta btn-common', + 'label' => t('Apply') + ) + ); + } +} diff --git a/modules/monitoring/application/views/helpers/CommandForm.php b/modules/monitoring/application/views/helpers/CommandForm.php deleted file mode 100644 index fd0ffd581..000000000 --- a/modules/monitoring/application/views/helpers/CommandForm.php +++ /dev/null @@ -1,179 +0,0 @@ -setAttrib('class', 'inline'); - $form->setRequest(Zend_Controller_Front::getInstance()->getRequest()); - - // Filter work only from get parts. Put important - // fields in the action URL - $getParts = array(); - foreach (self::$getArguments as $argumentName) { - if (array_key_exists($argumentName, $arguments) === true) { - if ($arguments[$argumentName]) { - $getParts[$argumentName] = $arguments[$argumentName]; - } - - unset($arguments[$argumentName]); - } - } - - $form->setAction($this->view->href('monitoring/command/' . $commandName, $getParts)); - - foreach ($arguments as $elementName => $elementValue) { - $hiddenField = new Zend_Form_Element_Hidden($elementName); - $hiddenField->setValue($elementValue); - $form->addElement($hiddenField); - - $hiddenField = $form->getElement($elementName); - } - - return $form; - } - - /** - * Creates an iconized submit form - * - * @param string $iconCls Css class of icon - * @param string $submitTitle Title of submit button - * @param string $cls Css class names - * @param string $commandName Name of command - * @param array $arguments Additional arguments - * - * @return Form - */ - public function iconSubmitForm($icon, $submitTitle, $cls, $commandName, array $arguments = array()) - { - $form = $this->labelSubmitForm('', $submitTitle, $cls, $commandName, $arguments); - $submit = $form->getElement('btn_submit'); - $submit->setLabel($this->view->img($icon)); - - return $form; - } - - /** - * Renders a simple for with a labeled submit button - * - * @param string $submitLabel Label of submit button - * @param string $submitTitle Title of submit button - * @param string $cls Css class names - * @param string $commandName Name of command - * @param array $arguments Additional arguments - * - * @return Form - */ - public function labelSubmitForm($submitLabel, $submitTitle, $cls = '', $commandName, array $arguments = array()) - { - $form = $this->simpleForm($commandName, $arguments); - - $button = new Zend_Form_Element_Submit( - array( - 'name' => 'btn_submit', - 'class' => $cls, - 'escape' => false, - 'value' => '1', - 'type' => 'submit', - 'label' => $submitLabel, - 'title' => $submitTitle - ) - ); - - $form->addElement($button); - - // Because of implicit added decorators - $form->getElement('btn_submit')->setDecorators(array('ViewHelper')); - - return $form; - } - - /** - * Create a toggle form for switch between commands - * - * @param string $label - * @param string $checkValue - * @param string $enabledCommand - * @param string $disabledCommand - * @param bool $changed - * @param array $arguments - * - * @return string - */ - public function toggleSubmitForm($label, $checkValue, $enabledCommand, $disabledCommand, $changed = false, array $arguments = array()) - { - if ($checkValue === '1') { - $commandName = $disabledCommand; - } else { - $commandName = $enabledCommand; - } - - $form = $this->simpleForm($commandName, $arguments); - - $uniqueName = uniqid('check'); - - $checkBox = new Zend_Form_Element_Checkbox($uniqueName); - - if ($checkValue === '1') { - $checkBox->setChecked(true); - } - - $form->addElement($checkBox); - - $checkBox->setDecorators(array('ViewHelper')); - $checkBox->setAttrib('id', $uniqueName); - $form->enableAutoSubmit(array($uniqueName)); - - $submit_identifier = new Zend_Form_Element_Hidden('btn_submit'); - $submit_identifier->setValue('1'); - $form->addElement($submit_identifier); - $form->getElement('btn_submit')->setDecorators(array('ViewHelper')); - - $out = ''; - if ($label) { - $out .= ''; - } - - if ($changed === true) { - $out .= ' (modified)'; - } - - $formCode = (string) $form; - - $jsLessSubmit = ''; - - $formCode = str_replace('', $jsLessSubmit, $formCode); - - $out .= $formCode; - - return $out; - } -} diff --git a/modules/monitoring/application/views/helpers/MonitoringState.php b/modules/monitoring/application/views/helpers/MonitoringState.php index 935578ca5..871e8ad08 100644 --- a/modules/monitoring/application/views/helpers/MonitoringState.php +++ b/modules/monitoring/application/views/helpers/MonitoringState.php @@ -2,11 +2,17 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} +/** + * @deprecated Crap. + */ class Zend_View_Helper_MonitoringState extends Zend_View_Helper_Abstract { private $servicestates = array('ok', 'warning', 'critical', 'unknown', 99 => 'pending', null => 'pending'); private $hoststates = array('up', 'down', 'unreachable', 99 => 'pending', null => 'pending'); + /** + * @deprecated The host or service object must know it's possible states. + */ public function monitoringState($object, $type = 'service') { if ($type === 'service') { @@ -16,26 +22,50 @@ class Zend_View_Helper_MonitoringState extends Zend_View_Helper_Abstract } } + public function monitoringStateById($id, $type = 'service') + { + if ($type === 'service') { + return $this->servicestates[$id]; + } elseif ($type === 'host') { + return $this->hoststates[$id]; + } + } + + /** + * @deprecated Monitoring colors are clustered. + */ public function getServiceStateColors() { return array('#44bb77', '#FFCC66', '#FF5566', '#E066FF', '#77AAFF'); } + /** + * @deprecated Monitoring colors are clustered. + */ public function getHostStateColors() { return array('#44bb77', '#FF5566', '#E066FF', '#77AAFF'); } + /** + * @deprecated The service object must know about it's possible states. + */ public function getServiceStateNames() { return array_values($this->servicestates); } + /** + * @deprecated The host object must know about it's possible states. + */ public function getHostStateNames() { return array_values($this->hoststates); } + /** + * @deprecated Not used anywhere. + */ public function getStateFlags($object, $type = 'service') { $state_classes = array(); @@ -60,6 +90,9 @@ class Zend_View_Helper_MonitoringState extends Zend_View_Helper_Abstract return $state_classes; } + /** + * @deprecated Not translated. + */ public function getStateTitle($object, $type) { return strtoupper($this->monitoringState($object, $type)) diff --git a/modules/monitoring/application/views/helpers/ResolveComments.php b/modules/monitoring/application/views/helpers/ResolveComments.php deleted file mode 100644 index 6f62ee1d2..000000000 --- a/modules/monitoring/application/views/helpers/ResolveComments.php +++ /dev/null @@ -1,33 +0,0 @@ -getHelper('MonitoringState'); +?> +
    + tabs ?> +
    + +
    + widget('limiter') ?> + paginationControl($notifications, null, null, array('preserve' => $this->preserve)) ?> +
    + +
    +

    translate('Alert summary'); ?>

    + +
    +
    +

    translate('Notifications and problems'); ?>

    +
    + render(); ?> +
    +
    + +
    +

    translate('Time to reaction (Ack, Recover)'); ?>

    +
    + render(); ?> +
    +
    +
    + +

    translate('Trend'); ?>

    + +
    +
    + translate('Average') ?> + perf->avg; ?> + translate('notifications per hour'); ?>, + perf->last; ?> + translate('in the last hour'); ?>. + + translate('Trend for the last 24h'); ?> + (trend->percent; ?>% + translate($this->trend->trend); ?>) + + + trend->trend === 'up'): ?> + icon('up.png'); ?> + trend->trend === 'unchanged'): ?> + icon('next.png'); ?> + + icon('down.png'); ?> + + +
    +
    + + recentAlerts): ?> +

    translate('Top 5 recent alerts'); ?>

    + +
    +
    + partial('list/notifications.phtml', array( + 'notifications' => $this->recentAlerts, + 'compact' => true, + 'inline' => true + )); ?> +
    +
    + + +

    translate('History'); ?>

    +
    +
    + partial('list/notifications.phtml', array( + 'notifications' => $this->notifications, + 'compact' => true, + 'inline' => true + )); ?> +
    +
    +
    \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/createbackend.phtml b/modules/monitoring/application/views/scripts/config/createbackend.phtml new file mode 100644 index 000000000..0ef51cd6a --- /dev/null +++ b/modules/monitoring/application/views/scripts/config/createbackend.phtml @@ -0,0 +1,2 @@ +

    translate('Add New Backend'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/createinstance.phtml b/modules/monitoring/application/views/scripts/config/createinstance.phtml new file mode 100644 index 000000000..49c8b5ec5 --- /dev/null +++ b/modules/monitoring/application/views/scripts/config/createinstance.phtml @@ -0,0 +1,2 @@ +

    translate('Add New Instance'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/editbackend.phtml b/modules/monitoring/application/views/scripts/config/editbackend.phtml index 6f2f19cbb..58b6c18f1 100644 --- a/modules/monitoring/application/views/scripts/config/editbackend.phtml +++ b/modules/monitoring/application/views/scripts/config/editbackend.phtml @@ -1,13 +1,2 @@ -name): ?> -

    Edit Backend "escape($this->name) ?>"

    - -

    Create New Backend

    - - -error): ?> -
    - escape($this->error); ?> -
    - - -form; ?> \ No newline at end of file +

    translate('Edit Existing Backend'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/editinstance.phtml b/modules/monitoring/application/views/scripts/config/editinstance.phtml index 1a2f5fe97..b12f262c3 100644 --- a/modules/monitoring/application/views/scripts/config/editinstance.phtml +++ b/modules/monitoring/application/views/scripts/config/editinstance.phtml @@ -1,14 +1,2 @@ - -name)): ?> -

    } Edit Instance Configuration for "escape($this->name) ?>"

    - -

    Configure New Icinga Instance

    - - -error): ?> -
    - escape($this->error); ?> -
    - - -form; ?> \ No newline at end of file +

    translate('Edit Existing Instance'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml index 41d2748d5..34f47a0b4 100644 --- a/modules/monitoring/application/views/scripts/config/index.phtml +++ b/modules/monitoring/application/views/scripts/config/index.phtml @@ -1,99 +1,70 @@ -{$element}) && - get_class($this->{$element}) === 'Icinga\Exception\NotReadableError') { - $fileNotReadable[$element] = $this->{$element}->getMessage(); - } else { - $fileNotReadable[$element] = false; - } -} -?> -
    - tabs->render($this); ?> +
    - -

    Monitoring Backends

    - - messageBox)): ?> - messageBox->render() ?> - - -

    - - icon('create.png'); ?> Create New Monitoring Backend - -

    - -
    ' . $this->weekdayName($i) . "' . $this->weekdayName($this->weekStartMonday ? $i + 1 : $i) . "
    ' . $this->weekdayName($weekday) . '
    ' + . $this->weekdayName($this->weekStartMonday ? $weekday + 1 : $weekday) + . '' . $this->renderDay($week[$weekday]) . '
    - - - - - - backends as $backendName => $config): ?> - $backendName)); - $editUrl = Url::fromPath('/monitoring/config/editbackend', array('backend' => $backendName)); - ?> - - - - - - -
    Monitoring BackendRemove
    - icon('edit.png'); ?> escape($backendName); ?> - (Type: escape($config['type'] === 'ido' ? 'IDO' : ucfirst($config['type'])); ?>) - - icon('remove.png'); ?> -
    - -

    - escape($fileNotReadable['backends']) ?> -

    - - - -

    Monitoring Instances

    - -

    - - icon('create.png'); ?> Create New Instance - -

    - - - - - - - - instances as $instanceName => $config): ?> - $instanceName)); - $editUrl = Url::fromPath('/monitoring/config/editinstance', array('instance' => $instanceName)); - ?> - - - - - - -
    InstanceRemove
    - icon('edit.png'); ?> escape($instanceName); ?> - (Type: ) - - icon('remove.png'); ?> -
    - -

    - escape($fileNotReadable['instances']) ?> -

    - - +

    Monitoring Backends

    +

    + + icon('create.png'); ?> translate('Create New Monitoring Backend'); ?> + +

    + + + + + + +backendsConfig as $backendName => $config): ?> + + + + + + +
    translate('Monitoring Backend'); ?>translate('Remove'); ?>
    + + icon('edit.png'); ?> escape($backendName); ?> + + (translate('Type: %s'), + $this->escape($config->type === 'ido' ? 'IDO' : ucfirst($config->type)) + ); ?>) + + + icon('remove.png'); ?> + +
    +

    Monitoring Instances

    +

    + + icon('create.png'); ?> translate('Create New Instance'); ?> + +

    + + + + + + +instancesConfig as $instanceName => $config): ?> + + + + + + +
    translate('Instance'); ?>translate('Remove'); ?>
    + + icon('edit.png'); ?> escape($instanceName); ?> + + (translate('Type: %s'), + $config->host !== null ? $this->translate('Remote') : $this->translate('Local') + ); ?>) + + + icon('remove.png'); ?> + +
    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/removebackend.phtml b/modules/monitoring/application/views/scripts/config/removebackend.phtml index bd4a95f3b..fc7da17e3 100644 --- a/modules/monitoring/application/views/scripts/config/removebackend.phtml +++ b/modules/monitoring/application/views/scripts/config/removebackend.phtml @@ -1,8 +1,2 @@ - -

    Remove Backend "escape($this->name) ?>"

    - -

    - Are you sure you want to remove the backend escape($this->name) ?>? -

    - -form; ?> +

    translate('Remove Existing Backend'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/removeinstance.phtml b/modules/monitoring/application/views/scripts/config/removeinstance.phtml index a278cf9b1..306d41815 100644 --- a/modules/monitoring/application/views/scripts/config/removeinstance.phtml +++ b/modules/monitoring/application/views/scripts/config/removeinstance.phtml @@ -1,13 +1,4 @@ - -

    Remove Instance "escape($this->name) ?>"

    - -

    - Are you sure you want to remove the instance escape($this->name) ?>? -

    - -

    - If you have still any environments or views refering to this instance, you won't be able to send commands anymore - after deletion. -

    - -form; ?> +

    translate('Remove Existing Instance'); ?>

    +

    translate('Are you sure you want to remove this instance?'); ?>

    +

    translate('If you have still any environments or views referring to this instance, you won\'t be able to send commands anymore after deletion.'); ?>

    + \ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/host/show.phtml b/modules/monitoring/application/views/scripts/host/show.phtml new file mode 100644 index 000000000..8023c1821 --- /dev/null +++ b/modules/monitoring/application/views/scripts/host/show.phtml @@ -0,0 +1,27 @@ +
    + render('show/components/header.phtml') ?> + render('show/components/hostservicesummary.phtml') ?> +
    +
    + render('show/components/output.phtml') ?> + render('show/components/grapher.phtml') ?> + + + + render('show/components/acknowledgement.phtml') ?> + render('show/components/comments.phtml') ?> + render('show/components/notifications.phtml') ?> + render('show/components/downtime.phtml') ?> + render('show/components/flapping.phtml') ?> + render('show/components/perfdata.phtml') ?> + render('show/components/checksource.phtml') ?> + render('show/components/actions.phtml') ?> + render('show/components/command.phtml') ?> + render('show/components/hostgroups.phtml') ?> + render('show/components/contacts.phtml') ?> + render('show/components/checkstatistics.phtml') ?> + render('show/components/customvars.phtml') ?> + render('show/components/flags.phtml') ?> + +
    +
    diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml new file mode 100644 index 000000000..e43198780 --- /dev/null +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -0,0 +1,113 @@ +
    + tabs ?> +
    +
    + + translate('No hosts matching the filter') ?> + +
    + translate('Hosts (%u)'), array_sum(array_values($hostStates))) ?> +
    +
    + hostStatesPieChart ?> +
    +
    + $count) { + echo sprintf('%s: %u
    ', strtoupper($text), $count); + } ?> +
    + +

    + translate('%u Hosts'), + count($objects)) + ?> +

    + +
    + + translate('List all') ?> + +
    + +
    + +
    + +
    + + icon('reschedule_petrol.png') ?> + translate('Reschedule host checks') ?> + +
    + +
    + + icon('in_downtime_petrol.png') ?> + translate('Schedule host downtimes') ?> + +
    + + +

    + translatePlural( + '%u Unhandled Host Problem', + '%u Unhandled Host Problems', + count($unhandledObjects) + ), + count($unhandledObjects) + ) ?> +

    +
    + + icon('in_downtime_petrol.png') ?> + translate('Schedule downtimes for unhandled problem hosts') ?> + +
    +
    + + icon('acknowledgement_petrol.png') ?> + translate('Acknowledge unhandled problem hosts') ?> + +
    + + + +

    + translatePlural( + '%u Acknowledged Host Problem', + '%u Acknowledged Host Problems', + count($acknowledgedObjects) + ), + count($acknowledgedObjects) + ) ?> +

    +
    + +
    + + + +

    + + icon('in_downtime_petrol.png') ?> + translate(sprintf('%u hosts are in downtime', count($objectsInDowntime))) ?> + +

    + + + getComments())): ?> +

    + + icon('comment.png') ?> + translate(sprintf('%u comments', count($objects->getComments()))) ?> + +

    + + +
    diff --git a/modules/monitoring/application/views/scripts/layout/topbar.phtml b/modules/monitoring/application/views/scripts/layout/topbar.phtml index 149bfeabd..f7c58db85 100644 --- a/modules/monitoring/application/views/scripts/layout/topbar.phtml +++ b/modules/monitoring/application/views/scripts/layout/topbar.phtml @@ -1,27 +1,27 @@ @@ -135,8 +135,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services CRITICAL Unhandled"> - services_critical_unhandled; ?> CRITICAL + ); ?>" title="translate('Services CRITICAL Unhandled') ?>"> + services_critical_unhandled; ?> translate('CRITICAL', 'icinga.state') ?> services_critical_handled): ?> @@ -148,8 +148,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services CRITICAL Handled"> - services_critical_handled . ' ' . ($h->services_critical_unhandled ? $this->translate('Acknowledged') : 'CRITICAL'); ?> + ); ?>" title="translate('Services CRITICAL Handled') ?>"> + services_critical_handled . ' ' . ($h->services_critical_unhandled ? $this->translate('Acknowledged') : $this->translate('CRITICAL', 'icinga.state')); ?> @@ -169,8 +169,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services WARNING Unhandled"> - services_warning_unhandled; ?> WARNING + ); ?>" title="translate('Services WARNING Unhandled') ?>"> + services_warning_unhandled; ?> translate('WARNING', 'icinga.state') ?> services_warning_handled): ?> @@ -182,8 +182,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services WARNING Handled"> - services_warning_handled . ' ' . ($h->services_warning_unhandled ? $this->translate('Acknowledged') : 'WARNING'); ?> + ); ?>" title="translate('Services WARNING Handled') ?>"> + services_warning_handled . ' ' . ($h->services_warning_unhandled ? $this->translate('Acknowledged') : $this->translate('WARNING', 'icinga.state')); ?> @@ -203,8 +203,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services UNKNOWN Unhandled"> - services_unknown_unhandled; ?> UNKNOWN + ); ?>" title="translate('Services UNKNOWN Unhandled') ?>"> + services_unknown_unhandled; ?> translate('UNKNOWN', 'icinga.state') ?> services_unknown_handled): ?> @@ -216,8 +216,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services UNKNOWN Handled"> - services_unknown_handled . ' ' . ($h->services_unknown_unhandled ? $this->translate('Acknowledged') : 'UNKNOWN'); ?> + ); ?>" title="translate('Services UNKNOWN Handled') ?>"> + services_unknown_handled . ' ' . ($h->services_unknown_unhandled ? $this->translate('Acknowledged') : $this->translate('UNKNOWN', 'icinga.state')); ?> @@ -233,8 +233,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services OK"> - services_ok; ?> OK + ); ?>" title="translate('Services OK') ?>"> + services_ok; ?> translate('OK', 'icinga.state') ?> @@ -249,8 +249,8 @@ if (count($hostgroups) === 0) { 'hostgroup' => $h->hostgroup, 'sort' => 'service_severity' ) - ); ?>" title="Services PENDING"> - services_pending; ?> PENDING + ); ?>" title="translate('Services PENDING') ?>"> + services_pending; ?> translate('PENDING', 'icinga.state') ?> diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index 3a38829b4..5be77f89f 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -15,7 +15,7 @@ if ($this->compact): ?> widget('limiter')->setMaxLimit($this->hosts->count()) ?> paginationControl($hosts, null, null, array('preserve' => $this->preserve)) ?> -selectionToolbar('multi', $this->href('monitoring/multi/host?' . $this->filter->toQueryString())) ?> +selectionToolbar('multi', $this->href('monitoring/hosts/show?' . $this->filter->toQueryString())) ?>
    @@ -36,46 +36,46 @@ if ($hosts->count() === 0) { util()->getHostStateName($host->host_state)); - $hostLink = $this->href('/monitoring/show/host', array('host' => $host->host_name)); + $hostLink = $this->href('monitoring/host/show', array('host' => $host->host_name)); $icons = array(); if (! $host->host_handled && $host->host_state > 0){ - $icons[] = $this->icon('unhandled.png', 'Unhandled'); + $icons[] = $this->icon('unhandled.png', $this->translate('Unhandled')); } if ($host->host_acknowledged) { - $icons[] = $this->icon('acknowledgement.png', 'Acknowledged'); + $icons[] = $this->icon('acknowledgement.png', $this->translate('Acknowledged')); } if ($host->host_is_flapping) { - $icons[] = $this->icon('flapping.png', 'Flapping'); + $icons[] = $this->icon('flapping.png', $this->translate('Flapping')); } if (! $host->host_notifications_enabled) { - $icons[] = $this->icon('notification_disabled.png', 'Notifications Disabled'); + $icons[] = $this->icon('notification_disabled.png', $this->translate('Notifications Disabled')); } if ($host->host_in_downtime) { - $icons[] = $this->icon('in_downtime.png', 'In Downtime'); + $icons[] = $this->icon('in_downtime.png', $this->translate('In Downtime')); } if (! $host->host_active_checks_enabled) { if (! $host->host_passive_checks_enabled) { - $icons[] = $this->icon('active_passive_checks_disabled.png', 'Active And Passive Checks Disabled'); + $icons[] = $this->icon('active_passive_checks_disabled.png', $this->translate('Active And Passive Checks Disabled')); } else { - $icons[] = $this->icon('active_checks_disabled.png', 'Active Checks Disabled'); + $icons[] = $this->icon('active_checks_disabled.png', $this->translate('Active Checks Disabled')); } } if (isset($host->host_last_comment) && $host->host_last_comment !== null) { - $icons[] = $this->icon('comment.png', 'Comment: ' . $host->host_last_comment); + $icons[] = $this->icon('comment.png', $this->translate('Comment: ') . $host->host_last_comment); } ?> @@ -101,8 +101,10 @@ if ($hosts->count() === 0) { host_unhandled_services) && $host->host_unhandled_services > 0): ?> (qlink( - sprintf($this->translate('%d unhandled services'), $host->host_unhandled_services), + ?> translate('Service Problems on Host') ?>"> (qlink( + sprintf( + $this->translatePlural('%d unhandled service', '%d unhandled services', $host->host_unhandled_services), + $host->host_unhandled_services), 'monitoring/show/services', array( 'host' => $host->host_name, diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml index ee414f502..d1ff9cdad 100644 --- a/modules/monitoring/application/views/scripts/list/notifications.phtml +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -1,4 +1,3 @@ - compact): ?>
    tabs ?> @@ -10,7 +9,10 @@
    +inline): ?>
    + + notifications)) { @@ -44,7 +46,14 @@ foreach ($notifications as $notification): } ?>
    - +
    timeSince($notification->notification_start_time) ?> + dateTimeRenderer($notification->notification_start_time)->render( + $this->translate('on %s', 'datetime'), + $this->translate('at %s', 'time'), + $this->translate('%s ago', 'timespan') + ); + ?> + service ?> on host ?> @@ -68,4 +77,6 @@ foreach ($notifications as $notification):
    +inline): ?>
    + diff --git a/modules/monitoring/application/views/scripts/list/servicegroups.phtml b/modules/monitoring/application/views/scripts/list/servicegroups.phtml index 059ea9dcb..5af6899e7 100644 --- a/modules/monitoring/application/views/scripts/list/servicegroups.phtml +++ b/modules/monitoring/application/views/scripts/list/servicegroups.phtml @@ -43,8 +43,8 @@ if (count($servicegroups) === 0) { 'host_unhandled' => 1, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts DOWN Unhandled"> - hosts_down_unhandled; ?> DOWN + ); ?>" title="translate('Hosts DOWN Unhandled') ?>"> + hosts_down_unhandled; ?> translate('DOWN', 'icinga.state') ?> hosts_down_handled): ?> @@ -55,7 +55,7 @@ if (count($servicegroups) === 0) { 'host_handled' => 1, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts DOWN Handled"> + ); ?>" title="translate('Hosts DOWN Handled') ?>"> hosts_down_handled . ' ' . ($servicegroup->hosts_down_unhandled ? $this->translate('Acknowledged') : 'DOWN'); ?> @@ -73,8 +73,8 @@ if (count($servicegroups) === 0) { 'host_unhandled' => 1, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts UNREACHABLE Unhandled"> - hosts_unreachable_unhandled; ?> UNREACHABLE + ); ?>" title="translate('Hosts UNREACHABLE Unhandled') ?>"> + hosts_unreachable_unhandled; ?> translate('UNREACHABLE', 'icinga.state') ?> hosts_unreachable_handled): ?> @@ -85,7 +85,7 @@ if (count($servicegroups) === 0) { 'host_handled' => 1, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts UNREACHABLE Handled"> + ); ?>" title="translate('Hosts UNREACHABLE Handled') ?>"> hosts_unreachable_handled . ' ' . ($servicegroup->hosts_unreachable_unhandled ? $this->translate('Acknowledged') : 'UNREACHABLE'); ?> @@ -101,8 +101,8 @@ if (count($servicegroups) === 0) { 'host_state' => 0, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts UP"> - hosts_up; ?> UP + ); ?>" title="translate('Hosts UP') ?>"> + hosts_up; ?> translate('UP', 'icinga.state') ?> @@ -116,8 +116,8 @@ if (count($servicegroups) === 0) { 'host_state' => 99, 'servicegroup' => $servicegroup->servicegroup ) - ); ?>" title="Hosts PENDING"> - hosts_pending; ?> PENDING + ); ?>" title="translate('Hosts PENDING') ?>"> + hosts_pending; ?> translate('PENDING', 'icinga.state') ?> @@ -138,8 +138,8 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services CRITICAL Unhandled"> - services_critical_unhandled; ?> CRITICAL + ); ?>" title="translate('Services CRITICAL Unhandled') ?>"> + services_critical_unhandled; ?> translate('CRITICAL', 'icinga.state') ?> services_critical_handled): ?> @@ -151,7 +151,7 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services CRITICAL Handled"> + ); ?>" title="translate('Services CRITICAL Handled') ?>"> services_critical_handled . ' ' . ($servicegroup->services_critical_unhandled ? $this->translate('Acknowledged') : 'CRITICAL'); ?> @@ -172,8 +172,8 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services WARNING Unhandled"> - services_warning_unhandled; ?> WARNING + ); ?>" title="translate('Services WARNING Unhandled') ?>"> + services_warning_unhandled; ?> translate('WARNING', 'icinga.state') ?> services_warning_handled): ?> @@ -185,7 +185,7 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services WARNING Handled"> + ); ?>" title="translate('Services WARNING Handled') ?>"> services_warning_handled . ' ' . ($servicegroup->services_warning_unhandled ? $this->translate('Acknowledged') : 'WARNING'); ?> @@ -206,8 +206,8 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services UNKNOWN Unhandled"> - services_unknown_unhandled; ?> UNKNOWN + ); ?>" title="translate('Services UNKNOWN Unhandled') ?>"> + services_unknown_unhandled; ?> translate('UNKNOWN', 'icinga.state') ?> services_unknown_handled): ?> @@ -219,7 +219,7 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services UNKNOWN Handled"> + ); ?>" title="translate('Services UNKNOWN Handled') ?>"> services_unknown_handled . ' ' . ($servicegroup->services_unknown_unhandled ? $this->translate('Acknowledged') : 'UNKNOWN'); ?> @@ -236,8 +236,8 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services OK"> - services_ok; ?> OK + ); ?>" title="translate('Services OK') ?>"> + services_ok; ?> translate('OK', 'icinga.state') ?> @@ -252,8 +252,8 @@ if (count($servicegroups) === 0) { 'servicegroup' => $servicegroup->servicegroup, 'sort' => 'service_severity' ) - ); ?>" title="Services PENDING"> - services_pending; ?> PENDING + ); ?>" title="translate('Services PENDING') ?>"> + services_pending; ?> translate('PENDING', 'icinga.state') ?> diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml index dbb43217d..66cf14c54 100644 --- a/modules/monitoring/application/views/scripts/list/services.phtml +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -19,7 +19,7 @@ if (!$this->compact): ?> widget('limiter')->setCurrentPageCount($this->services->count()) ?> paginationControl($services, null, null, array('preserve' => $this->preserve)) ?> -selectionToolbar('multi', $this->href('monitoring/multi/service?' . $this->filter->toQueryString())) ?> +selectionToolbar('multi', $this->href('monitoring/services/show?' . $this->filter->toQueryString())) ?>
    @@ -30,7 +30,7 @@ if (!$this->compact): ?> " + data-icinga-multiselect-url="href("monitoring/services/show") ?>" data-icinga-multiselect-data="service,host"> href( - 'monitoring/show/service', + 'monitoring/service/show', array( 'host' => $service->host_name, 'service' => $service->service_description @@ -69,36 +69,36 @@ foreach ($services as $service): perfdata($service->service_perfdata, true, true) ?> service_handled && $service->service_state > 0): ?> - icon('unhandled.png', 'Unhandled') ?> + icon('unhandled.png', $this->translate('Unhandled')) ?> service_acknowledged && !$service->service_in_downtime): ?> - icon('acknowledgement.png', 'Acknowledged' . ( + icon('acknowledgement.png', $this->translate('Acknowledged') . ( $service->service_last_ack ? ': ' . $service->service_last_ack : '' )) ?> service_is_flapping): ?> - icon('flapping.png', 'Flapping') ?> + icon('flapping.png', $this->translate('Flapping')) ?> service_notifications_enabled): ?> - icon('notification_disabled.png', 'Notifications Disabled') ?> + icon('notification_disabled.png', $this->translate('Notifications Disabled')) ?> service_in_downtime): ?> - icon('in_downtime.png', 'In Downtime') ?> + icon('in_downtime.png', $this->translate('In Downtime')) ?> service_last_comment) && $service->service_last_comment !== null): ?> - icon('comment.png', 'Comment: ' . $service->service_last_comment) ?> + icon('comment.png', $this->translate('Comment: ') . $service->service_last_comment) ?> service_active_checks_enabled): ?> service_passive_checks_enabled): ?> - icon('active_passive_checks_disabled.png', 'Active And Passive Checks Disabled') ?> + icon('active_passive_checks_disabled.png', $this->translate('Active And Passive Checks Disabled')) ?> - icon('active_checks_disabled.png', 'Active Checks Disabled') ?> + icon('active_checks_disabled.png', $this->translate('Active Checks Disabled')) ?> diff --git a/modules/monitoring/application/views/scripts/list/statehistorysummary.phtml b/modules/monitoring/application/views/scripts/list/statehistorysummary.phtml index 55c41ffcb..cdb19dba7 100644 --- a/modules/monitoring/application/views/scripts/list/statehistorysummary.phtml +++ b/modules/monitoring/application/views/scripts/list/statehistorysummary.phtml @@ -1,42 +1,131 @@ + + + +
    + tabs->render($this); ?> +
    +
    + +
    + filterEditor): ?> + filterPreview ?> + + filterEditor ?> +
    + -?>
    -tabs ?> -

    History - Critical Events

    -
    setColor('#f05060'); +$settings = array( + 'cnt_up' => array( + 'tooltip' => t('%d ok on %s'), + 'color' => '#49DF96', + 'opacity' => '0.55' + ), + 'cnt_unreachable_hard' => array( + 'tooltip' => t('%d unreachable on %s'), + 'color' => '#77AAFF', + 'opacity' => '0.55' + ), + 'cnt_critical_hard' => array( + 'tooltip' => t('%d critical on %s'), + 'color' => '#ff5566', + 'opacity' => '0.9' + ), + + 'cnt_warning_hard' => array( + 'tooltip' => t('%d warning on %s'), + 'color' => '#ffaa44', + 'opacity' => '1.0' + ), + + 'cnt_down_hard' => array( + 'tooltip' => t('%d down on %s'), + 'color' => '#ff5566', + 'opacity' => '0.9' + ), + 'cnt_unknown_hard' => array( + 'tooltip' => t('%d unknown on %s'), + 'color' => '#cc77ff', + 'opacity' => '0.7' + ), + 'cnt_ok' => array( + 'tooltip' => t('%d ok on %s'), + 'color' => '#49DF96', + 'opacity' => '0.55' + ) +); + +$from = intval($form->getValue('from', strtotime('3 months ago'))); +$to = intval($form->getValue('to', time())); + +// don't display more than ten years, or else this will get really slow +if ($to - $from > 315360000) { + $from = $to - 315360000; +} $data = array(); if (count($summary) === 0) { - echo t('No history entry matching the filter'); + echo t('No state changes in the selected time period.'); } foreach ($summary as $entry) { $day = $entry->day; - $value = $entry->cnt_critical; - $caption = $value . ' ' . - t('critical events on ') . $this->dateFormat()->formatDate(strtotime($day)); - $filter = Filter::matchAll( + $value = $entry->$column; + $caption = sprintf( + $settings[$column]['tooltip'], + $value, + $this->dateFormat()->formatDate(strtotime($day)) + ); + $linkFilter = Filter::matchAll( Filter::expression('timestamp', '<', strtotime($day . ' 23:59:59')), Filter::expression('timestamp', '>', strtotime($day . ' 00:00:00')), - Filter::expression('object_type', '=', 'service'), - Filter::expression('state', '=', '2'), - Filter::matchAny( - Filter::expression('type', '=', 'hard_state'), - Filter::expression('type', '=', 'hard_state') - ) + $form->getFilter(), + $filter ); $data[$day] = array( 'value' => $value, 'caption' => $caption, - 'url' => $this->href('monitoring/list/eventhistory?' . $filter->toQueryString()) + 'url' => $this->href('monitoring/list/eventhistory?' . $linkFilter->toQueryString()) ); } -$grid->setData($data); + +$f = new DateTime(); +$f->setTimestamp($from); +$t = new DateTime(); +$t->setTimestamp($to); +$diff = $t->diff($f); +$step = 124; + +for ($i = 0; $i < $diff->days; $i += $step) { + $end = clone $f; + if ($diff->days - $i > $step) { + // full range, move last day to next chunk + $end->add(new DateInterval('P' . ($step - 1) . 'D')); + } else { + // include last day + $end->add(new DateInterval('P' . ($diff->days - $i) . 'D')); + } + $grid = new HistoryColorGrid(null, $f->getTimestamp(), $end->getTimestamp()); + $grid->setColor($settings[$column]['color']); + $grid->opacity = $settings[$column]['opacity']; + $grid->orientation = $orientation; + $grid->setData($data); + $grids[] = $grid; + + $f->add(new DateInterval('P' . $step . 'D')); +} ?> - +
    + $grid) { ?> +
    + + orientation === 'horizontal' ? '
    ' : '' ?> +
    + +
    diff --git a/modules/monitoring/application/views/scripts/partials/command-form.phtml b/modules/monitoring/application/views/scripts/partials/command-form.phtml new file mode 100644 index 000000000..0e5a4777e --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/command-form.phtml @@ -0,0 +1,29 @@ +
    + tabs->showOnlyCloseButton() ?> +
    + +
    +

    +
    + + + + + + + + getObjects() as $object): /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ ?> + + getType() === $object::TYPE_HOST): ?> + + + + + + + + +
    img('img/icons/host.png') ?> translate('Host') ?>img('img/icons/service.png') ?> translate('Service') ?>
    escape($object->getName()) ?>escape($object->getHost()->getName()) ?>escape($object->getName()) ?>
    +
    + +
    diff --git a/modules/monitoring/application/views/scripts/process/disable-notifications.phtml b/modules/monitoring/application/views/scripts/process/disable-notifications.phtml new file mode 100644 index 000000000..547b3ac37 --- /dev/null +++ b/modules/monitoring/application/views/scripts/process/disable-notifications.phtml @@ -0,0 +1,18 @@ +
    + tabs->showOnlyCloseButton() ?> +
    +
    +

    + notifications_enabled === false): ?> +
    + translate('Host and service notifications are already disabled.') ?> + programStatus->disable_notif_expire_time): ?> + translate('Notifications will be re-enabled in %s.'), + $this->timeUntil($this->programStatus->disable_notif_expire_time)); ?> + +
    + + + +
    diff --git a/modules/monitoring/application/views/scripts/process/info.phtml b/modules/monitoring/application/views/scripts/process/info.phtml index 8fb9bd0e3..c659544c2 100644 --- a/modules/monitoring/application/views/scripts/process/info.phtml +++ b/modules/monitoring/application/views/scripts/process/info.phtml @@ -1,179 +1,55 @@
    -tabs ?> + tabs ?>
    -programstatus; -$cf = $this->getHelper('CommandForm'); - -?>
    -

    Backend backendName; ?> -is_currently_running === '1' - ? sprintf( - $this->translate('has been running with PID %d '), - $ps->process_id - ) . $this->prefixedTimeSince($ps->program_start_time) - : $this->translate('is not running'); ?>. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    translate('Last status update'); ?>timeSince($ps->status_update_time) ?> ago
    translate('Last check command'); ?>timeSince($ps->last_command_check) ?> ago
    translate('Global host event handler'); ?>global_host_event_handler ? $ps->global_host_event_handler : $this->translate('Not set'); ?>
    translate('Global service event handler'); ?>global_service_event_handler ? $ps->global_service_event_handler : $this->translate('Not set'); ?>
    translate('Notifications enabled'); ?>toggleSubmitForm( - '', - $ps->notifications_enabled, - 'enablenotifications', - 'disablenotifications', - false, - array('global' => '1') - ) ?> -notifications_enabled === '1'): ?> - - translate('Temporarily disable'); ?> - -disable_notif_expire_time): ?> -translate('Will be re-enabled in %s'), '' . $this->timeUntil($ps->disable_notif_expire_time) . ''); ?> - - -
    translate('Execute active service checks'); ?>toggleSubmitForm( - '', - $ps->active_service_checks_enabled, - 'enableactivechecks', - 'disableactivechecks', - false, - array('global' => 'service') - ) ?>
    translate('Accept passive service checks'); ?>toggleSubmitForm( - '', - $ps->passive_service_checks_enabled, - 'startacceptingpassivechecks', - 'stopacceptingpassivechecks', - false, - array('global' => 'service') - ) ?>
    translate('Execute active host checks'); ?>toggleSubmitForm( - '', - $ps->active_host_checks_enabled, - 'enableactivechecks', - 'disableactivechecks', - false, - array('global' => 'host') - ) ?>
    translate('Accept passive host checks'); ?>toggleSubmitForm( - '', - $ps->passive_host_checks_enabled, - 'startacceptingpassivechecks', - 'stopacceptingpassivechecks', - false, - array('global' => 'host') - ) ?>
    translate('Eventhandlers enabled'); ?>toggleSubmitForm( - '', - $ps->event_handlers_enabled, - 'enableeventhandler', - 'disableeventhandler', - false, - array('global' => '1') - ) ?>
    translate('Obsessing over host checks'); ?>toggleSubmitForm( - '', - $ps->obsess_over_hosts, - 'startobsessing', - 'stopobsessing', - false, - array('global' => 'host') - ) ?>
    translate('Obsessing over service checks'); ?>toggleSubmitForm( - '', - $ps->obsess_over_services, - 'startobsessing', - 'stopobsessing', - false, - array('global' => 'service') - ) ?>
    translate('Flap detection enabled'); ?>toggleSubmitForm( - '', - $ps->flap_detection_enabled, - 'enableflapdetection', - 'disableflapdetection', - false, - array('global' => '1') - ) ?>
    translate('Process performance data'); ?>toggleSubmitForm( - '', - $ps->process_performance_data, - 'enableperformancedata', - 'disableperformancedata', - false, - array('global' => '1') - ) ?>
    translate('Monitoring Process'); ?>labelSubmitForm( - $this->translate('Restart'), - $this->translate('Restart the monitoring process'), - '', - 'restartprocess' - ) ?> - -
    - + programStatus->is_currently_running === true): ?> +

    + translate('%s has been up and running with PID %d since %s'), + $this->backendName, + $this->programStatus->process_id, + $this->timeSince($this->programStatus->program_start_time)) ?> +
    + +
    + translate('%s is not running'), $this->backendName) ?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    translate('Program Start Time') ?>dateFormat()->formatDateTime($this->programStatus->program_start_time) ?>
    translate('Last Status Update'); ?>timeSince($this->programStatus->status_update_time) ?> ago
    translate('Last External Command Check'); ?>timeSince($this->programStatus->last_command_check) ?> ago
    translate('Last Log File Rotation'); ?>programStatus->last_log_rotation + ? $this->timeSince($this->programStatus->last_log_rotation) + : $this->translate('N/A') ?>
    translate('Global Service Event Handler'); ?>programStatus->global_service_event_handler + ? $this->programStatus->global_service_event_handler + : $this->translate('N/A'); ?>
    translate('Global Host Event Handler'); ?>programStatus->global_host_event_handler + ? $this->programStatus->global_host_event_handler + : $this->translate('N/A'); ?>
    +
    + toggleFeaturesForm ?>
    diff --git a/modules/monitoring/application/views/scripts/service/show.phtml b/modules/monitoring/application/views/scripts/service/show.phtml new file mode 100644 index 000000000..5d0fd22c1 --- /dev/null +++ b/modules/monitoring/application/views/scripts/service/show.phtml @@ -0,0 +1,27 @@ +
    + render('show/components/header.phtml') ?> +

    translate("This service's current state") ?>

    +
    +
    + render('show/components/output.phtml') ?> + render('show/components/grapher.phtml') ?> + + + + render('show/components/acknowledgement.phtml') ?> + render('show/components/comments.phtml') ?> + render('show/components/notifications.phtml') ?> + render('show/components/downtime.phtml') ?> + render('show/components/flapping.phtml') ?> + render('show/components/perfdata.phtml') ?> + render('show/components/checksource.phtml') ?> + render('show/components/actions.phtml') ?> + render('show/components/command.phtml') ?> + render('show/components/servicegroups.phtml') ?> + render('show/components/contacts.phtml') ?> + render('show/components/checkstatistics.phtml') ?> + render('show/components/customvars.phtml') ?> + render('show/components/flags.phtml') ?> + +
    +
    diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml new file mode 100644 index 000000000..802aa8ef6 --- /dev/null +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -0,0 +1,125 @@ +
    + tabs ?> +
    +
    + + translate('No services matching the filter') ?> + + +
    +
    + translate('Services (%u)'), array_sum(array_values($serviceStates))) ?> +
    +
    + serviceStatesPieChart ?> +
    +
    + $count) { + echo sprintf(' %s: %u
    ', strtoupper($text), $count); + } ?> +
    +
    + +
    +
    + translate('Hosts (%u)'), array_sum(array_values($hostStates))) ?> +
    +
    + hostStatesPieChart ?> +
    +
    + $count) { + echo sprintf('%s: %u
    ', strtoupper($text), $count); + } ?> +
    +
    + +
    + + translate('List all') ?> + +
    + + +
    + +
    + +
    + + icon('reschedule_petrol.png') ?> + translate('Reschedule service checks') ?> + +
    + +
    + + icon('in_downtime_petrol.png') ?> + translate('Schedule service downtimes') ?> + +
    + + +

    + translatePlural( + '%u Unhandled Service Problem', + '%u Unhandled Service Problems', + count($unhandledObjects) + ), + count($unhandledObjects) + ) ?> +

    +
    + + icon('in_downtime_petrol.png') ?> + translate('Schedule downtimes for unhandled problem services') ?> + +
    +
    + + icon('acknowledgement_petrol.png') ?> + translate('Acknowledge unhandled problem services') ?> + +
    + + + +

    + translatePlural( + '%u Acknowledged Service Problem', + '%u Acknowledged Service Problems', + count($acknowledgedObjects) + ), + count($acknowledgedObjects) + ) ?> +

    +
    + +
    + + + +

    + + icon('in_downtime_petrol.png') ?> + translate(sprintf('%u services are in downtime', count($objectsInDowntime))) ?> + +

    + + + getComments())): ?> +

    + + icon('comment.png') ?> + translate(sprintf('%u comments', count($objects->getComments()))) ?> + +

    + + +
    diff --git a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml index 38359bf34..2ca0ca401 100644 --- a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml +++ b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml @@ -1,40 +1,39 @@ state, array(0, 99))) { + // Ignore this markup if the object is in a non-problem state or pending return; } -$cf = $this->getHelper('CommandForm'); - -$data = array('host' => $object->host_name); -if ($object->service_description) { - $data['service'] = $object->service_description; +if ($object->getType() === $object::TYPE_HOST) { + $ackLink = $this->href( + 'monitoring/host/acknowledge-problem', + array('host' => $object->getName()) + ); +} else { + $ackLink = $this->href( + 'monitoring/service/acknowledge-problem', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()) + ); } -?> - -host_acknowledged - || $object instanceof Service && $object->service_acknowledged): ?> - Issue acknowledged - labelSubmitForm( - $this->translate('Remove Acknowledgement'), - $this->translate('Remove problem acknowledgement'), - 'link-like', - 'removeacknowledgement', - $data - ) ?> +if ($object->acknowledged): ?> + + translate('Acknowledged') ?> + + - translate('Not acknowledged') ?> - - icon('acknowledgement_petrol.png') ?> translate('Acknowledge') ?> - + + translate('Not acknowledged') ?> + + + + icon('acknowledgement_petrol.png') ?> translate('Acknowledge') ?> + + + - diff --git a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml index f0b39e603..b9ad284eb 100644 --- a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml +++ b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml @@ -1,31 +1,36 @@ getHelper('CommandForm'); +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ -?> - translate('Last check') ?> - img('img/icons/refresh_petrol.png') ?> labelSubmitForm( - $this->translate('Check now'), - $this->translate('Reschedule next check immediately'), - 'link-like', - 'reschedulenextcheck', - array( - 'host' => $this->object->host_name, - 'service' => $this->object->service_description, - 'checktime' => time(), - 'forcecheck' => '1' - ) - ) ?> timeSince($object->last_check) ?> +if ($object->getType() === $object::TYPE_HOST) { + $reschedule = $this->href( + 'monitoring/host/reschedule-check', + array('host' => $object->getName()) + ); +} else { + $reschedule = $this->href( + 'monitoring/service/reschedule-check', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()) + ); +} + +?> + + + translate('Last check') ?> + + + timeSince($object->last_check) ?> + translate('Next check') ?> - img('img/icons/reschedule_petrol.png') ?> translate('Reschedule') ?> timeUntil($object->next_check) ?> + + + icon('reschedule_petrol.png') ?> translate('Reschedule') ?> + + timeUntil($object->next_check) ?> + check_execution_time): ?> @@ -39,4 +44,3 @@ $cf = $this->getHelper('CommandForm'); check_latency ?>s - diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml index 4365acdd5..372a17f15 100644 --- a/modules/monitoring/application/views/scripts/show/components/comments.phtml +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -1,15 +1,32 @@ + + translate('Comments') ?> + + getType() === $object::TYPE_HOST) { + $addCommentLink = $this->href( + 'monitoring/host/add-comment', + array('host' => $object->getName()) + ); + } else { + $addCommentLink = $this->href( + 'monitoring/service/add-comment', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()) + ); + } + + ?> + + icon('comment_petrol.png') ?> translate('Add comment') ?> + + + + getHelper('CommandForm'); - -$data = array('host' => $object->host_name); -if ($object->service_description) { - $data['service'] = $object->service_description; -} - -$list = array(); foreach ($object->comments as $comment) { - // Ticket hook sample $text = $this->tickets ? preg_replace_callback( $this->tickets->getPattern(), @@ -17,34 +34,26 @@ foreach ($object->comments as $comment) { $this->escape($comment->comment) ) : $this->escape($comment->comment); - $deleteData = $data; - $deleteData['commentid'] = $comment->id; - // $iconForm = $cf->iconSubmitForm( - // 'img/icons/remove_petrol.png', - $iconForm = $cf->labelSubmitForm( - 'X', - 'Remove comment', - 'link-like', - 'removecomment', - $deleteData - ); + $form = clone $delCommentForm; + $form->populate(array('comment_id' => $comment->id)); - $list[] = sprintf( - '%s (%s)
    %s (%s):%s
    ', - $this->escape($comment->author), - $this->timeSince($comment->timestamp), - $iconForm, - ucfirst($comment->type), - str_replace(array('\r\n', '\n'), '
    ', $text) - ); -} + ?> -?> - - translate('Comments') ?> - - icon('comment_petrol.png') ?> translate('Add comment') ?> - - - + + escape($comment->author) ?> (timeSince($comment->timestamp) ?>) + + + + + + +
    + (type) ?>): + + ', $text) ?> +
    + + + + diff --git a/modules/monitoring/application/views/scripts/show/components/contacts.phtml b/modules/monitoring/application/views/scripts/show/components/contacts.phtml index 550c87e12..5cb0a1d51 100644 --- a/modules/monitoring/application/views/scripts/show/components/contacts.phtml +++ b/modules/monitoring/application/views/scripts/show/components/contacts.phtml @@ -5,7 +5,7 @@ if (! empty($object->contacts)) { $list = array(); foreach ($object->contacts as $contact) { $list[] = $this->qlink($contact->contact_alias, 'monitoring/show/contact', array( - 'contact_name' => $contact->contact_name + 'contact' => $contact->contact_name )); } @@ -24,7 +24,7 @@ if (! empty($object->contactgroups)) { $list[] = $this->qlink( $contactgroup->contactgroup_alias, 'monitoring/list/contactgroups', - array('contactgroup_name' => $contactgroup->contactgroup_name) + array('contactgroup' => $contactgroup->contactgroup_name) ); } diff --git a/modules/monitoring/application/views/scripts/show/components/downtime.phtml b/modules/monitoring/application/views/scripts/show/components/downtime.phtml index f78666531..243373966 100644 --- a/modules/monitoring/application/views/scripts/show/components/downtime.phtml +++ b/modules/monitoring/application/views/scripts/show/components/downtime.phtml @@ -1,39 +1,31 @@ + + translate('Downtimes') ?> + + getType() === $object::TYPE_HOST) { + $scheduleDowntimeLink = $this->href( + 'monitoring/host/schedule-downtime', + array('host' => $object->getName()) + ); + } else { + $scheduleDowntimeLink = $this->href( + 'monitoring/service/schedule-downtime', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()) + ); + } + ?> + + icon('in_downtime_petrol.png') ?> translate('Schedule downtime') ?> + + + + getHelper('CommandForm'); - -$data = array('host' => $object->host_name); -if ($object->service_description) { - $data['service'] = $object->service_description; -} - - -$list = array(); foreach ($object->downtimes as $downtime) { - $deleteData = $data; - $deleteData['downtimeid'] = $downtime->id; - - // TODO: This needs improvement - if ((int) $downtime->is_in_effect) { - $state = 'in downtime since ' . $this->timeSince($downtime->start); - } else { - if ($downtime->is_fixed) { - $state = 'scheduled ' . $this->timeUntil($downtime->start); - } else { - $state = 'scheduled flexible ' . $this->timeUntil($downtime->start); - } - } - - // $iconForm = $cf->iconSubmitForm( - // 'img/icons/remove_petrol.png', - $iconForm = $cf->labelSubmitForm( - 'X', - 'Remove Downtime', - 'link-like', - 'removedowntime', - $deleteData - ); - // Ticket hook sample $text = $this->tickets ? preg_replace_callback( $this->tickets->getPattern(), @@ -41,20 +33,35 @@ foreach ($object->downtimes as $downtime) { $this->escape($downtime->comment) ) : $this->escape($downtime->comment); - $list[] = sprintf( - '%s
    %s %s:%s
    ', - $this->escape($downtime->author), - $iconForm, - $state, - str_replace(array('\r\n', '\n'), '
    ', $text) - ); -} + $form = clone $delDowntimeForm; + $form->populate(array('downtime_id' => $downtime->id)); -?> - - translate('Downtimes') ?> - - icon('in_downtime_petrol.png') ?> translate('Schedule downtime') ?> - - - + if ((bool) $downtime->is_in_effect) { + $state = 'in downtime since ' . $this->timeSince($downtime->start); + } else { + if ((bool) $downtime->is_fixed) { + $state = 'scheduled ' . $this->timeUntil($downtime->start); + } else { + $state = 'scheduled flexible ' . $this->timeUntil($downtime->start); + } + } + + ?> + + + escape($downtime->author) ?> + + + + + + +
    + + + ', $text) ?> +
    + + + + diff --git a/modules/monitoring/application/views/scripts/show/components/flags.phtml b/modules/monitoring/application/views/scripts/show/components/flags.phtml index fb02b97a3..1ecc788b7 100644 --- a/modules/monitoring/application/views/scripts/show/components/flags.phtml +++ b/modules/monitoring/application/views/scripts/show/components/flags.phtml @@ -1,77 +1,3 @@ -object; -$cf = $this->getHelper('CommandForm'); - -$data = array( - 'host' => $o->host_name, - 'service' => $o->service_description -); - -?> - - translate('Passive Checks') ?> - toggleSubmitForm( - '', - $o->passive_checks_enabled, - 'startacceptingpassivechecks', - 'stopacceptingpassivechecks', - $o->passive_checks_enabled_changed === '1', - $data - ) ?> - - - translate('Active Checks') ?> - toggleSubmitForm( - '', - $o->active_checks_enabled, - 'enableactivechecks', - 'disableactivechecks', - $o->active_checks_enabled_changed === '1', - $data - ) ?> - - - translate('Notifications') ?> - toggleSubmitForm( - '', - $o->notifications_enabled, - 'enablenotifications', - 'disablenotifications', - $o->notifications_enabled_changed === '1', - $data - ) ?> - - - translate('Event Handler') ?> - toggleSubmitForm( - '', - $o->event_handler_enabled, - 'enableeventhandler', - 'disableeventhandler', - $o->event_handler_enabled_changed === '1', - $data - ) ?> - - - translate('Flap Detection') ?> - toggleSubmitForm( - '', - $o->flap_detection_enabled, - 'enableflapdetection', - 'disableflapdetection', - $o->flap_detection_enabled_changed === '1', - $data - ) ?> - - - translate('Obsessing') ?> - toggleSubmitForm( - '', - $o->obsessing, - 'startobsessing', - 'stopobsessing', - $o->obsessing_changed === '1', - $data - ) ?> - + + + diff --git a/modules/monitoring/application/views/scripts/show/components/grapher.phtml b/modules/monitoring/application/views/scripts/show/components/grapher.phtml index 2d8d3e4bd..b46ad6113 100644 --- a/modules/monitoring/application/views/scripts/show/components/grapher.phtml +++ b/modules/monitoring/application/views/scripts/show/components/grapher.phtml @@ -1 +1,4 @@ -grapherHtml ?> +getPreviewHtml($object); +} ?> + diff --git a/modules/monitoring/application/views/scripts/show/components/header.phtml b/modules/monitoring/application/views/scripts/show/components/header.phtml index 49daaae09..a2c835e4a 100644 --- a/modules/monitoring/application/views/scripts/show/components/header.phtml +++ b/modules/monitoring/application/views/scripts/show/components/header.phtml @@ -1,4 +1,4 @@ -service_description && $tabs->getActiveName() !== 'host' ?> +getType() === $object::TYPE_SERVICE ?> compact): ?> diff --git a/modules/monitoring/application/views/scripts/show/components/hostservicesummary.phtml b/modules/monitoring/application/views/scripts/show/components/hostservicesummary.phtml index 0b4b9c9fe..0390bcffc 100644 --- a/modules/monitoring/application/views/scripts/show/components/hostservicesummary.phtml +++ b/modules/monitoring/application/views/scripts/show/components/hostservicesummary.phtml @@ -6,10 +6,10 @@ $currentUrl = Url::fromRequest()->without('limit')->getRelativeUrl(); ?>

    compact ? ' data-base-target="col1"' : '' ?>> -qlink(sprintf($this->translate('%s service configured:'), $this->stats->services_total), $selfUrl) ?> -stats->services_ok > 0): ?> +qlink(sprintf($this->translate('%s configured services:'), $object->stats->services_total), $selfUrl) ?> +stats->services_ok > 0): ?> qlink( - $this->stats->services_ok, + $object->stats->services_ok, $selfUrl, array('service_state' => 0), array('title' => sprintf($this->translate('Services with state %s'), strtoupper($this->translate('ok')))) @@ -19,12 +19,12 @@ $currentUrl = Url::fromRequest()->without('limit')->getRelativeUrl(); foreach (array(2 => 'critical', 3 => 'unknown', 1 => 'warning') as $stateId => $state) { $pre = 'services_' . $state; - if ($this->stats->$pre) { + if ($object->stats->$pre) { $handled = $pre . '_handled'; $unhandled = $pre . '_unhandled'; $paramsHandled = array('service_state' => $stateId, 'service_handled' => 1); $paramsUnhandled = array('service_state' => $stateId, 'service_handled' => 0); - if ($this->stats->$unhandled) { + if ($object->stats->$unhandled) { $compareUrl = $selfUrl->with($paramsUnhandled)->getRelativeUrl(); } else { $compareUrl = $selfUrl->with($paramsHandled)->getRelativeUrl(); @@ -35,34 +35,34 @@ foreach (array(2 => 'critical', 3 => 'unknown', 1 => 'warning') as $stateId => $ } else { $active = ''; } - - echo ''; - if ($this->stats->$unhandled) { + + echo ''; + if ($object->stats->$unhandled) { echo $this->qlink( - $this->stats->$unhandled, + $object->stats->$unhandled, $selfUrl, $paramsUnhandled, array('title' => sprintf($this->translate('Unhandled services with state %s'), strtoupper($this->translate($state)))) ); } - if ($this->stats->$handled) { + if ($object->stats->$handled) { if ($selfUrl->with($paramsHandled)->getRelativeUrl() === $currentUrl) { $active = ' active'; } else { $active = ''; } - if ($this->stats->$unhandled) { + if ($object->stats->$unhandled) { echo ''; } echo $this->qlink( - $this->stats->$handled, + $object->stats->$handled, $selfUrl, $paramsHandled, array('title' => sprintf($this->translate('Handled services with state %s'), strtoupper($this->translate($state)))) ); - if ($this->stats->$unhandled) { + if ($object->stats->$unhandled) { echo "\n"; } } @@ -71,9 +71,9 @@ foreach (array(2 => 'critical', 3 => 'unknown', 1 => 'warning') as $stateId => $ } ?> -stats->services_pending): ?> +stats->services_pending): ?> qlink( - $this->stats->services_pending, + $object->stats->services_pending, $selfUrl, array('service_state' => 99), array('title' => sprintf($this->translate('Services with state %s'), strtoupper($this->translate('pending')))) diff --git a/modules/monitoring/application/views/scripts/show/components/statusIcons.phtml b/modules/monitoring/application/views/scripts/show/components/statusIcons.phtml index 4e0f3845f..bfa7b7873 100644 --- a/modules/monitoring/application/views/scripts/show/components/statusIcons.phtml +++ b/modules/monitoring/application/views/scripts/show/components/statusIcons.phtml @@ -23,26 +23,26 @@ $obj->passive_checks_enabled = $i = array(); if (! $obj->handled && $obj->state > 0) { - $i[] = $this->icon('unhandled.png', 'Unhandled'); + $i[] = $this->icon('unhandled.png', $this->translate('Unhandled')); } if ($obj->acknowledged && ! $obj->in_downtime) { - $i[] = $this->icon('acknowledgement.png', 'Acknowledged'); + $i[] = $this->icon('acknowledgement.png', $this->translate('Acknowledged')); } if (!$obj->notifications_enabled) { - $i[] = $this->icon('notification_disabled.png', 'Notifications Disabled'); + $i[] = $this->icon('notification_disabled.png', $this->translate('Notifications Disabled')); } if ($obj->in_downtime) { - $i[] = $this->icon('in_downtime.png', 'In Downtime'); + $i[] = $this->icon('in_downtime.png', $this->translate('In Downtime')); } if (! $obj->active_checks_enabled) { if ($obj->passive_checks_enabled) { - $i[] = $this->icon('active_checks_disabled.png', 'Active Checks Disabled'); + $i[] = $this->icon('active_checks_disabled.png', $this->translate('Active Checks Disabled')); } else { - $i[] = $this->icon('active_passive_checks_disabled.png', 'Active And Passive Checks Disabled'); + $i[] = $this->icon('active_passive_checks_disabled.png', $this->translate('Active And Passive Checks Disabled')); } } diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml index c4dc41b9e..cdb6af12c 100644 --- a/modules/monitoring/application/views/scripts/show/contact.phtml +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -1,5 +1,6 @@ getHelper('ContactFlags') ?>
    + tabs ?>

    translate('Contact details') ?>

    - CRITICAL + translate('CRITICAL', 'icinga.state') ?> @@ -13,7 +13,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 2, 'service_handled' => 1) ); ?>"> - translate('Acknowledged') : 'CRITICAL'); ?> + translate('Acknowledged') : $this->translate('CRITICAL', 'icinga.state')); ?> @@ -75,7 +75,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 1, 'service_acknowledged' => 0, 'service_in_downtime' => 0) ); ?>"> - WARNING + translate('WARNING', 'icinga.state') ?> @@ -83,7 +83,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 1, 'service_handled' => 1) ); ?>"> - translate('Acknowledged') : 'WARNING'); ?> + translate('Acknowledged') : $this->translate('WARNING', 'icinga.state')); ?> @@ -145,7 +145,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 3, 'service_acknowledged' => 0, 'service_in_downtime' => 0) ); ?>"> - UNKNOWN + translate('UNKNOWN', 'icinga.state') ?> @@ -153,7 +153,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 3, 'service_handled' => 1) ); ?>"> - translate('Acknowledged') : 'UNKNOWN'); ?> + translate('Acknowledged') : $this->translate('UNKNOWN', 'icinga.state')); ?> @@ -214,7 +214,7 @@ 'monitoring/list/services', array('host_problem' => $host_problem, 'service_state' => 0) ); ?>"> - OK + translate('OK', 'icinga.state') ?> - PENDING + translate('PENDING', 'icinga.state') ?> + tabs ?>
    diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index b40a8c282..edcd37a24 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -122,6 +122,18 @@ $section->add($this->translate('Events'), array( )); $section->add($this->translate('Timeline'))->setUrl('monitoring/timeline'); +/* + * Reporting Section + */ +$section = $this->menuSection($this->translate('Reporting'), array( + 'icon' => 'img/icons/hostgroup.png', + 'priority' => 100 +)); + +$section->add($this->translate('Alert Summary'), array( + 'url' => 'monitoring/alertsummary/index' +)); + /* * System Section */ diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php index 7ec76d4a8..5145a96ad 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php @@ -19,9 +19,9 @@ class CommentQuery extends IdoQuery 'comment_is_persistent' => 'cm.is_persistent', 'comment_expiration' => 'CASE cm.expires WHEN 1 THEN UNIX_TIMESTAMP(cm.expiration_time) ELSE NULL END', 'comment_host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', - 'host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', + 'host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', // #7278, #7279 'comment_service' => 'so.name2 COLLATE latin1_general_ci', - 'service' => 'so.name2 COLLATE latin1_general_ci', + 'service' => 'so.name2 COLLATE latin1_general_ci', // #7278, #7279 'comment_objecttype' => "CASE WHEN ho.object_id IS NOT NULL THEN 'host' ELSE CASE WHEN so.object_id IS NOT NULL THEN 'service' ELSE NULL END END", ) ); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php index aef50ebdb..346f2458a 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php @@ -8,39 +8,39 @@ class ContactgroupQuery extends IdoQuery { protected $columnMap = array( 'contactgroups' => array( - 'contactgroup_name' => 'cgo.name1 COLLATE latin1_general_ci', - 'contactgroup_alias' => 'cg.alias', + 'contactgroup' => 'cgo.name1 COLLATE latin1_general_ci', + 'contactgroup_name' => 'cgo.name1 COLLATE latin1_general_ci', + 'contactgroup_alias' => 'cg.alias', ), 'contacts' => array( - 'contact_name' => 'co.name1 COLLATE latin1_general_ci', - 'contact_alias' => 'c.alias', - 'contact_email' => 'c.email_address', - 'contact_pager' => 'c.pager_address', - 'contact_has_host_notfications' => 'c.host_notifications_enabled', - 'contact_has_service_notfications' => 'c.service_notifications_enabled', - 'contact_can_submit_commands' => 'c.can_submit_commands', - 'contact_notify_service_recovery' => 'c.notify_service_recovery', - 'contact_notify_service_warning' => 'c.notify_service_warning', - 'contact_notify_service_critical' => 'c.notify_service_critical', - 'contact_notify_service_unknown' => 'c.notify_service_unknown', - 'contact_notify_service_flapping' => 'c.notify_service_flapping', - 'contact_notify_service_downtime' => 'c.notify_service_recovery', - 'contact_notify_host_recovery' => 'c.notify_host_recovery', - 'contact_notify_host_down' => 'c.notify_host_down', - 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', - 'contact_notify_host_flapping' => 'c.notify_host_flapping', - 'contact_notify_host_downtime' => 'c.notify_host_downtime', + 'contact' => 'co.name1 COLLATE latin1_general_ci', + 'contact_name' => 'co.name1 COLLATE latin1_general_ci', + 'contact_alias' => 'c.alias', + 'contact_email' => 'c.email_address', + 'contact_pager' => 'c.pager_address', + 'contact_has_host_notfications' => 'c.host_notifications_enabled', + 'contact_has_service_notfications' => 'c.service_notifications_enabled', + 'contact_can_submit_commands' => 'c.can_submit_commands', + 'contact_notify_service_recovery' => 'c.notify_service_recovery', + 'contact_notify_service_warning' => 'c.notify_service_warning', + 'contact_notify_service_critical' => 'c.notify_service_critical', + 'contact_notify_service_unknown' => 'c.notify_service_unknown', + 'contact_notify_service_flapping' => 'c.notify_service_flapping', + 'contact_notify_service_downtime' => 'c.notify_service_recovery', + 'contact_notify_host_recovery' => 'c.notify_host_recovery', + 'contact_notify_host_down' => 'c.notify_host_down', + 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', + 'contact_notify_host_flapping' => 'c.notify_host_flapping', + 'contact_notify_host_downtime' => 'c.notify_host_downtime', ), 'hosts' => array( - 'host_object_id' => 'ho.object_id', - 'host_name' => 'ho.name1', - 'host' => 'ho.name1' + 'host' => 'ho.name1', + 'host_name' => 'ho.name1' ), 'services' => array( - 'service_object_id' => 'so.object_id', - 'service_host_name' => 'so.name1 COLLATE latin1_general_ci', - 'service' => 'so.name2 COLLATE latin1_general_ci', - 'service_description' => 'so.name2 COLLATE latin1_general_ci', + 'service' => 'so.name2 COLLATE latin1_general_ci', + 'service_description' => 'so.name2 COLLATE latin1_general_ci', + 'service_host_name' => 'so.name1 COLLATE latin1_general_ci' ) ); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php index fe1152076..2393c6ed9 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php @@ -28,8 +28,10 @@ class DowntimeQuery extends IdoQuery 'downtime_duration' => 'sd.duration', 'downtime_is_in_effect' => 'sd.is_in_effect', 'downtime_internal_id' => 'sd.internal_downtime_id', - 'downtime_host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', + 'downtime_host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', // #7278, #7279 + 'host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', 'downtime_service' => 'so.name2 COLLATE latin1_general_ci', + 'service' => 'so.name2 COLLATE latin1_general_ci', // #7278, #7279 'downtime_objecttype' => "CASE WHEN ho.object_id IS NOT NULL THEN 'host' ELSE CASE WHEN so.object_id IS NOT NULL THEN 'service' ELSE NULL END END", 'downtime_host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', 'downtime_service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END' diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php index 3b7fdaa06..ab1947f7e 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php @@ -27,14 +27,14 @@ class GroupSummaryQuery extends IdoQuery 'services_ok' => 'SUM(CASE WHEN object_type = \'service\' AND state = 0 THEN 1 ELSE 0 END)', 'services_pending' => 'SUM(CASE WHEN object_type = \'service\' AND state = 99 THEN 1 ELSE 0 END)', 'services_warning' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 THEN 1 ELSE 0 END)', - 'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)', + 'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND acknowledged + in_downtime + host_state > 0 THEN 1 ELSE 0 END)', 'services_critical' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 THEN 1 ELSE 0 END)', - 'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)', + 'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND acknowledged + in_downtime + host_state > 0 THEN 1 ELSE 0 END)', 'services_unknown' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 THEN 1 ELSE 0 END)', - 'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)', - 'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)', - 'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)', - 'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)', + 'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND acknowledged + in_downtime + host_state > 0 THEN 1 ELSE 0 END)', + 'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND acknowledged + in_downtime + host_state = 0 THEN 1 ELSE 0 END)', + 'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND acknowledged + in_downtime + host_state = 0 THEN 1 ELSE 0 END)', + 'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND acknowledged + in_downtime + host_state = 0 THEN 1 ELSE 0 END)', 'servicegroup' => 'servicegroup' ) ); @@ -44,7 +44,6 @@ class GroupSummaryQuery extends IdoQuery $columns = array( 'object_type', 'host_state', - 'host_name' ); // Prepend group column since we'll use columns index 0 later for grouping @@ -61,6 +60,9 @@ class GroupSummaryQuery extends IdoQuery 'in_downtime' => 'host_in_downtime' ) ); + if (in_array('servicegroup', $this->desiredColumns)) { + $hosts->group(array('sgo.name1', 'ho.object_id', 'state', 'acknowledged', 'in_downtime')); + } $services = $this->createSubQuery( 'Status', $columns + array( @@ -77,7 +79,7 @@ class GroupSummaryQuery extends IdoQuery } $union = $this->db->select()->union(array($hosts, $services), Zend_Db_Select::SQL_UNION_ALL); - $this->select->from(array('statussummary' => $union), array($groupColumn))->group(array($groupColumn)); + $this->select->from(array('statussummary' => $union), array())->group(array($groupColumn)); $this->joinedVirtualTables = array( 'servicestatussummary' => true, diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php index 882600240..00b6957b8 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php @@ -18,7 +18,8 @@ class NotificationQuery extends IdoQuery 'notification' => array( 'notification_output' => 'n.output', 'notification_start_time' => 'UNIX_TIMESTAMP(n.start_time)', - 'notification_state' => 'n.state' + 'notification_state' => 'n.state', + 'notification_object_id' => 'n.object_id' ), 'objects' => array( 'host' => 'o.name1', @@ -30,6 +31,11 @@ class NotificationQuery extends IdoQuery ), 'command' => array( 'notification_command' => 'cmd_o.name1' + ), + 'acknowledgement' => array( + 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', + 'acknowledgement_author_name' => 'a.author_name', + 'acknowledgement_comment_data' => 'a.comment_data' ) ); @@ -95,4 +101,13 @@ class NotificationQuery extends IdoQuery array() ); } + + protected function joinAcknowledgement() + { + $this->select->joinLeft( + array('a' => $this->prefix . 'acknowledgements'), + 'n.object_id = a.object_id', + array() + ); + } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StateHistorySummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StateHistorySummaryQuery.php index cdaee0697..9004d5250 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StateHistorySummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StateHistorySummaryQuery.php @@ -10,6 +10,7 @@ class StateHistorySummaryQuery extends IdoQuery 'statehistory' => array( 'day' => 'DATE(sh.state_time)', 'cnt_events' => 'COUNT(*)', + 'objecttype_id' => 'sho.objecttype_id', 'cnt_up' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 0 THEN 1 ELSE 0 END)', 'cnt_down_hard' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)', 'cnt_down' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 THEN 1 ELSE 0 END)', @@ -23,6 +24,19 @@ class StateHistorySummaryQuery extends IdoQuery 'cnt_warning' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 THEN 1 ELSE 0 END)', 'cnt_warning_hard' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)', 'cnt_ok' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 0 THEN 1 ELSE 0 END)', + 'host' => 'sho.name1 COLLATE latin1_general_ci', + 'service' => 'sho.name2 COLLATE latin1_general_ci', + 'host_name' => 'sho.name1 COLLATE latin1_general_ci', + 'service_description' => 'sho.name2 COLLATE latin1_general_ci', + 'timestamp' => 'UNIX_TIMESTAMP(sh.state_time)' + ), + + 'servicegroups' => array( + 'servicegroup' => 'sgo.name1' + ), + + 'hostgroups' => array( + 'hostgroup' => 'hgo.name1' ) ); @@ -35,8 +49,42 @@ class StateHistorySummaryQuery extends IdoQuery array('sho' => $this->prefix . 'objects'), 'sh.object_id = sho.object_id AND sho.is_active = 1', array() - )->where('sh.state_time >= ?', '2013-11-20 00:00:00') + ) ->group('DATE(sh.state_time)'); $this->joinedVirtualTables = array('statehistory' => true); } + + protected function joinHostgroups() + { + $this->select->join( + array('hgm' => $this->prefix . 'hostgroup_members'), + 'hgm.host_object_id = sho.object_id', + array() + )->join( + array('hgs' => $this->prefix . 'hostgroups'), + 'hgm.hostgroup_id = hgs.hostgroup_id', + array() + )->join( + array('hgo' => $this->prefix . 'objects'), + 'hgo.object_id = hgs.hostgroup_object_id', + array() + ); + } + + protected function joinServicegroups() + { + $this->select->join( + array('sgm' => $this->prefix . 'servicegroup_members'), + 'sgm.service_object_id = sho.object_id', + array() + )->join( + array('sgs' => $this->prefix . 'servicegroups'), + 'sgm.servicegroup_id = sgs.servicegroup_id', + array() + )->join( + array('sgo' => $this->prefix . 'objects'), + 'sgo.object_id = sgs.servicegroup_object_id', + array() + ); + } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php index d3aca9934..e390aa114 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php @@ -8,6 +8,29 @@ use Zend_Db_Expr; class StatusQuery extends IdoQuery { + /** + * This mode represents whether we are in HostStatus or ServiceStatus + * + * Implemented for `distinct as workaround + * + * @TODO Subject to change, see #7344 + * + * @var string + */ + protected $mode; + + /** + * Sets the mode of the current query + * + * @TODO Subject to change, see #7344 + * + * @param string $mode + */ + public function setMode($mode) + { + $this->mode = $mode; + } + protected $allowCustomVars = true; protected $columnMap = array( @@ -430,6 +453,12 @@ class StatusQuery extends IdoQuery array() ); + // @TODO Subject to change, see #7344 + if ($this->mode === 'host' || $this->mode === 'service') { + $this->useSubqueryCount = true; + $this->distinct(); + } + return $this; } @@ -449,7 +478,11 @@ class StatusQuery extends IdoQuery . ' AND hgo.is_active = 1', array() ); - + // @TODO Subject to change, see #7344 + if ($this->mode === 'service') { + $this->distinct(); + $this->useSubqueryCount = true; + } return $this; } @@ -471,6 +504,14 @@ class StatusQuery extends IdoQuery array() ); + // @TODO Subject to change, see #7344 + if ($this->mode === 'host' || $this->mode === 'service') { + $this->distinct(); + } + if ($this->mode === 'host') { + $this->useSubqueryCount = true; + } + return $this; } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusSummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusSummaryQuery.php index 7d91f0f71..a311feedc 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusSummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusSummaryQuery.php @@ -43,25 +43,25 @@ class StatusSummaryQuery extends IdoQuery 'servicestatussummary' => array( 'services_total' => 'SUM(CASE WHEN object_type = \'service\' THEN 1 ELSE 0 END)', 'services_problem' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 THEN 1 ELSE 0 END)', - 'services_problem_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)', - 'services_problem_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)', + 'services_problem_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND acknowledged + in_downtime + host_problem > 0 THEN 1 ELSE 0 END)', + 'services_problem_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND acknowledged + in_downtime + host_problem = 0 THEN 1 ELSE 0 END)', 'services_ok' => 'SUM(CASE WHEN object_type = \'service\' AND state = 0 THEN 1 ELSE 0 END)', 'services_ok_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', 'services_pending' => 'SUM(CASE WHEN object_type = \'service\' AND state = 99 THEN 1 ELSE 0 END)', 'services_pending_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', 'services_warning' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 THEN 1 ELSE 0 END)', - 'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)', - 'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)', + 'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND acknowledged + in_downtime + host_problem > 0 THEN 1 ELSE 0 END)', + 'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND acknowledged + in_downtime + host_problem = 0 THEN 1 ELSE 0 END)', 'services_warning_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', 'services_warning_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', 'services_critical' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 THEN 1 ELSE 0 END)', - 'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)', - 'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)', + 'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND acknowledged + in_downtime + host_problem > 0 THEN 1 ELSE 0 END)', + 'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND acknowledged + in_downtime + host_problem = 0 THEN 1 ELSE 0 END)', 'services_critical_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', 'services_critical_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', 'services_unknown' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 THEN 1 ELSE 0 END)', - 'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)', - 'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)', + 'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND acknowledged + in_downtime + host_problem > 0 THEN 1 ELSE 0 END)', + 'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND acknowledged + in_downtime + host_problem = 0 THEN 1 ELSE 0 END)', 'services_unknown_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', 'services_unknown_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', 'services_active' => 'SUM(CASE WHEN object_type = \'service\' AND is_active_checked = 1 THEN 1 ELSE 0 END)', @@ -72,39 +72,47 @@ class StatusSummaryQuery extends IdoQuery 'services_without_flap_detection' => 'SUM(CASE WHEN object_type = \'service\' AND is_allowed_to_flap = 0 THEN 1 ELSE 0 END)', 'services_flapping' => 'SUM(CASE WHEN object_type = \'service\' AND is_flapping = 1 THEN 1 ELSE 0 END)', +/* +NOTE: in case you might wonder, please see #7303. As a quickfix I did: - 'services_ok_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 0 THEN 1 ELSE 0 END)', - 'services_ok_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_pending_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 99 THEN 1 ELSE 0 END)', - 'services_pending_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_warning_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 1 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_warning_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_warning_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_warning_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_critical_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 2 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_critical_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_critical_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_critical_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_unknown_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 3 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_unknown_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 3 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_unknown_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_unknown_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 0 OR host_state = 99) AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_ok_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 0 THEN 1 ELSE 0 END)', - 'services_ok_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_pending_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 99 THEN 1 ELSE 0 END)', - 'services_pending_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_warning_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 1 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_warning_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_warning_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_warning_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_critical_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 2 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_critical_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_critical_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_critical_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', - 'services_unknown_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 3 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', - 'services_unknown_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 3 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', - 'services_unknown_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', - 'services_unknown_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND (host_state = 1 OR host_state = 2) AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)' +:%s/(host_state = 0 OR host_state = 99)/host_state != 1 AND host_state != 2/g +:%s/(host_state = 1 OR host_state = 2)/host_state != 0 AND host_state != 99/g + +We have to find a better solution here. + +*/ + 'services_ok_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 0 THEN 1 ELSE 0 END)', + 'services_ok_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_pending_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 99 THEN 1 ELSE 0 END)', + 'services_pending_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_warning_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 1 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_warning_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_warning_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_warning_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_critical_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 2 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_critical_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_critical_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_critical_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_unknown_handled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 3 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_unknown_unhandled_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 3 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_unknown_passive_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_unknown_not_checked_on_ok_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 1 AND host_state != 2 AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_ok_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 0 THEN 1 ELSE 0 END)', + 'services_ok_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_pending_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 99 THEN 1 ELSE 0 END)', + 'services_pending_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_warning_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 1 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_warning_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_warning_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_warning_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_critical_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 2 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_critical_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_critical_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_critical_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)', + 'services_unknown_handled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 3 AND acknowledged + in_downtime > 0 THEN 1 ELSE 0 END)', + 'services_unknown_unhandled_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 3 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)', + 'services_unknown_passive_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)', + 'services_unknown_not_checked_on_problem_hosts' => 'SUM(CASE WHEN object_type = \'service\' AND host_state != 0 AND host_state != 99 AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)' ) ); diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php deleted file mode 100644 index 27b554ec8..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php +++ /dev/null @@ -1,83 +0,0 @@ - 'comment_id', - 'comment_internal_id' => 'comment_id', - 'comment_data' => 'comment_data', - 'comment_author' => 'author', - 'comment_timestamp' => 'entry_time', - 'comment_is_persistent' => 'persistent', - 'host_name' => 'host_name', - 'host' => 'host_name', - ); - - public static $handlerParameters = array( - 'comment_objecttype_id' => 'getCommentObjectType', - 'comment_type' => 'getCommentType', - 'comment_expiration_timestamp' => 'getExpirationTime', - 'service' => 'getServiceDescription', - 'service_name' => 'getServiceDescription', - 'service_description' => 'getServiceDescription' - ); - - public function getServiceDescription(&$obj) - { - if (isset($obj->service_description)) { - return $obj->service_description; - } - return ''; - } - - public function getExpirationTime(&$obj) - { - if ($obj->expires) { - return $obj->expire_time; - } else { - return null; - } - } - - public function getCommentObjectType(&$obj) - { - if (isset($obj->service_description)) { - return 2; - } else { - return 1; - } - } - - public function getCommentType(&$obj) - { - switch ($obj->entry_type) { - case 1: - return 'comment'; - case 2: - return 'downtime'; - case 3: - return 'flapping'; - case 4: - return 'ack'; - } - return ''; - - } - - public function getObjectType(&$obj) - { - return isset($obj->service_description) ? 'service ': 'host'; - } - - public function selectBase() - { - $this->select()->from("comments", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php deleted file mode 100644 index d4ea895e3..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php +++ /dev/null @@ -1,133 +0,0 @@ - 'contact_name', - 'contact_alias' => 'alias', - 'contact_email' => 'email', - 'contact_pager' => 'pager', - 'contact_has_host_notfications' => 'host_notifications_enabled', - 'contact_has_service_notfications' => 'service_notifications_enabled', - 'contact_can_submit_commands' => 'can_submit_commands', - 'contact_notify_host_timeperiod' => 'host_notification_period', - 'contact_notify_service_timeperiod'=> 'service_notification_period', - 'contact_service_notification_options' => 'service_notification_options', - 'contact_host_notification_options' => 'host_notification_options', - 'contactgroup_name' => 'group.contactgroup_name', - 'contactgroup_alias' => 'group.alias' - ); - - public static $handlerParameters = array( - 'contact_notify_service_recovery' => 'getServiceRecoveryFlag', - 'contact_notify_service_warning' => 'getServiceWarningFlag', - 'contact_notify_service_critical' => 'getServiceCriticalFlag', - 'contact_notify_service_unknown' => 'getServiceUnknownFlag', - 'contact_notify_service_flapping' => 'getServiceFlappingFlag', - 'contact_notify_service_downtime' => 'getServiceDowntimeFlag', - 'contact_notify_host_recovery' => 'getHostRecoveryFlag', - 'contact_notify_host_down' => 'getHostDownFlag', - 'contact_notify_host_unreachable' => 'getHostUnreachableFlag', - 'contact_notify_host_flapping' => 'getHostFlappingFlag', - 'contact_notify_host_downtime' => 'getHostDowntimeFlag', - 'host_name' => 'getHost', - 'host' => 'getHost', - 'service_host_name' => 'getHost', - 'service' => 'getService', - 'service_description' => 'getService' - ); - - - public function getHost(&$obj) - { - $result = array(); - foreach ($this->state['host'] as $values) { - if (!isset($values->contacts)) { - continue; - } - if (stripos($values->contacts, $obj->contacts) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getService(&$obj) - { - $result = array(); - foreach ($this->state['service'] as $values) { - if (!isset($values->contacts)) { - continue; - } - if (stripos($values->contact_groups, $obj->contacts) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getServiceRecoveryFlag(&$obj) - { - return stripos($obj->service_notification_options, 'r') === false ? 0 : 1; - } - - public function getServiceWarningFlag(&$obj) - { - return stripos($obj->service_notification_options, 'w') === false ? 0 : 1; - } - - public function getServiceCriticalFlag(&$obj) - { - return stripos($obj->service_notification_options, 'c') === false ? 0 : 1; - } - - public function getServiceUnknownFlag(&$obj) - { - return stripos($obj->service_notification_options, 'u') === false ? 0 : 1; - } - - public function getServiceFlappingFlag(&$obj) - { - return stripos($obj->service_notification_options, 'f') === false ? 0 : 1; - } - - public function getServiceDowntimeFlag(&$obj) - { - return stripos($obj->service_notification_options, 's') === false ? 0 : 1; - } - - public function getHostRecoveryFlag(&$obj) - { - return stripos($obj->host_notification_options, 'r') === false ? 0 : 1; - } - - public function getHostDownFlag(&$obj) - { - return stripos($obj->host_notification_options, 'd') === false ? 0 : 1; - } - - public function getHostUnreachableFlag(&$obj) - { - return stripos($obj->host_notification_options, 'u') === false ? 0 : 1; - } - - public function getHostFlappingFlag(&$obj) - { - return stripos($obj->host_notification_options, 'f') === false ? 0 : 1; - } - - public function getHostDowntimeFlag(&$obj) - { - return strpos($obj->host_notification_options, 's') === false ? 0 : 1; - } - - public function selectBase() - { - $this->state = $this->ds->getState(); - $this->select()->from("contacts", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php deleted file mode 100644 index 11b7f4b44..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php +++ /dev/null @@ -1,61 +0,0 @@ - 'contactgroup_name', - 'contactgroup_alias' => 'alias', - 'contact_name' => 'contact.contact_name', - 'contact_alias' => 'contact.alias', - 'contact_email' => 'contact.email', - 'contact_pager' => 'contact.pager', - 'contact_has_host_notfications' => 'contact.host_notifications_enabled', - 'contact_has_service_notfications' => 'contact.service_notifications_enabled', - 'contact_can_submit_commands' => 'contact.can_submit_commands', - 'contact_notify_host_timeperiod' => 'contact.host_notification_period', - 'contact_notify_service_timeperiod' => 'contact.service_notification_period', - 'contact_service_notification_options' => 'contact.service_notification_options', - 'contact_host_notification_options' => 'contact.host_notification_options', - ); - - public static $handlerParameters = array( - 'host_name' => 'getHosts', - 'host' => 'getHosts', - 'service_host_name' => 'getHosts', - 'service' => 'getService', - 'service_description' => 'getService' - ); - - public function getHosts(&$obj) - { - $result = array(); - foreach ($this->state['host'] as $values) { - if (stripos($values->contact_groups, $obj->contactgroup_name) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getService(&$obj) - { - $result = array(); - foreach ($this->state['service'] as $values) { - if (stripos($values->contact_groups, $obj->contactgroup_name) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function selectBase() - { - $this->state = $this->ds->getState(); - return $this->select()->from("contactgroups", array()); - - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php deleted file mode 100644 index d0d8e1eaa..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php +++ /dev/null @@ -1,79 +0,0 @@ - 'author', - 'downtime_comment' => 'comment', - 'downtime_duration' => 'duration', - 'downtime_end' => 'end_time', - 'downtime_was_started' => 'was_started', - 'downtime_is_fixed' => 'fixed', - 'downtime_is_in_effect' => 'is_in_effect', - 'downtime_trigger_time' => 'trigger_time', - 'downtime_triggered_by_id' => 'triggered_by_id', - 'downtime_internal_downtime_id' => 'downtime_id', - 'downtime_scheduled_start_time' => 'start_time', - 'host' => 'host_name', - 'host_name' => 'host_name', - 'service_host_name' => 'host_name' - ); - - public static $handlerParameters = array( - 'object_type' => 'getObjectType', - 'downtime_start' => 'getDowntimeStart', - 'downtime_is_flexible' => 'getFlexibleFlag', - 'service_description' => 'getServiceDescription' - ); - - public static $fieldTypes = array( - 'downtime_end' => self::TIMESTAMP, - 'downtime_trigger_time' => self::TIMESTAMP, - 'downtime_start' => self::TIMESTAMP - ); - - public function getServiceDescription(&$obj) - { - if (isset ($obj->service_description)) { - return $obj->service_description; - } - return ''; - } - - - public function getDowntimeStart(&$obj) - { - if ($obj->trigger_time != '0') { - return $obj->trigger_time; - } else { - return $obj->start_time; - } - } - - - public function getFlexibleFlag(&$obj) - { - return $obj->fixed ? 0 : 1; - } - - public function getObjectType(&$obj) - { - return isset($obj->service_description) ? 'service ': 'host'; - } - - public function selectBase() - { - $this->select()->from("downtimes", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php deleted file mode 100644 index 1168d6da3..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php +++ /dev/null @@ -1,248 +0,0 @@ - 'hostgroup_name', - 'servicegroup' => 'servicegroup_name' - ); - - public static $handlerParameters = array( - 'hosts_up' => 'getHostUpSum', - 'hosts_unreachable_handled' => 'getHostUnreachableSum', - 'hosts_unreachable_unhandled' => 'getHostUnreachableUnhandledSum', - 'hosts_down_handled' => 'getHostDownSum', - 'hosts_down_unhandled' => 'getHostDownUnhandledSum', - 'hosts_pending' => 'getHostPendingSum', - 'services_ok' => 'getServiceOkSum', - 'services_unknown_handled' => 'getServiceUnknownSum', - 'services_unknown_unhandled' => 'getServiceUnknownUnhandledSum', - 'services_critical_handled' => 'getServiceCriticalSum', - 'services_critical_unhandled' => 'getServiceCriticalUnhandledSum', - 'services_warning_handled' => 'getServiceWarningSum', - 'services_warning_unhandled' => 'getServiceWarningUnhandledSum', - 'services_pending' => 'getServicePendingSum', - - ); - - private function getMembers(&$obj, $hint = null) - { - if (!isset($obj->service) && !isset($obj->host)) { - return null; - } - $memberList = isset($obj->service) ? $obj->service : $obj->host; - - if (isset($obj->host) && $hint == 'service') { - $result = array(); - foreach ($memberList as &$member) { - if (isset($member->services)) { - $result = $result + $member->services; - } - } - return $result; - } - return $memberList; - } - - private function getMembersByCriteria(&$obj, $type, $namefield, $criteriaFn) - { - $memberList = $this->getMembers($obj, $type); - if ($memberList === null) { - return 0; - } - $ids = array(); - foreach ($memberList as $member) { - $name = $member->$type->$namefield; - if ($namefield === 'service_description') { - $name .= ';' . $member->$type->host_name; - } - - if (isset($ids[$name])) { - continue; - } else { - if ($criteriaFn($member->$type)) { - $ids[$name] = true; - } - } - } - return count(array_keys($ids)); - } - - public function getHostUpSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 0 - && $member->status->has_been_checked); - }); - } - - public function getHostUnreachableSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostUnreachableUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostDownSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostDownUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostPendingSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return (!$member->status->has_been_checked); - }); - } - - public function getServiceOkSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 0 - && $member->status->has_been_checked); - }); - } - - public function getServiceUnknownSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - - return ($member->status->current_state == 3 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceUnknownUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 3 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceCriticalSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceCriticalUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceWarningSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceWarningUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServicePendingSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return (!$member->status->has_been_checked); - }); - } - - private function getTarget() - { - if (in_array('servicegroup', $this->getColumns())) { - return 'servicegroups'; - } - return 'hostgroups'; - } - - public function selectBase() - { - $this->select()->from($this->getTarget(), array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php deleted file mode 100644 index 3855375c2..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php +++ /dev/null @@ -1,25 +0,0 @@ - 'hostgroup_name', - 'hostgroup_name' => 'hostgroup_name', - 'hostgroup_alias' => 'alias', - 'host' => 'host.host_name', - 'host_name' => 'host.host_name' - ); - - public function selectBase() - { - $this->select()->from("hostgroups", array()); - } -} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php deleted file mode 100644 index 48ee82284..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php +++ /dev/null @@ -1,33 +0,0 @@ -select()->from("hosts", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php deleted file mode 100644 index 8d3f9a4ec..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php +++ /dev/null @@ -1,28 +0,0 @@ - 'servicegroup_name', - 'servicegroup_name' => 'servicegroup_name', - 'servicegroup_alias' => 'alias', - 'host' => 'service.host_name', - 'host_name' => 'service.host_name', - 'service' => 'service.service_description', - 'service_description'=> 'service.service_description' - - ); - - public function selectBase() - { - $this->select()->from("servicegroups", array()); - } -} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php deleted file mode 100644 index cd712d801..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php +++ /dev/null @@ -1,22 +0,0 @@ -select()->from("services", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php deleted file mode 100644 index e78942f4c..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php +++ /dev/null @@ -1,361 +0,0 @@ - 'host.host_name', - 'host_name' => 'host.host_name', - 'host_display_name' => 'host.host_name', - 'host_alias' => 'host.alias', - 'host_address' => 'host.address', - 'host_icon_image' => 'host.icon_image', - 'host_action_url' => 'host.action_url', - 'host_notes_url' => 'host.notes_url', - 'host_output' => 'host.status.plugin_output', - 'host_long_output' => 'host.status.long_plugin_output', - 'host_perfdata' => 'host.status.performance_data', - 'host_check_source' => 'host.status.check_source', - 'host_acknowledged' => 'host.status.problem_has_been_acknowledged', - 'host_last_state_change' => 'host.status.last_state_change', - 'host_last_hard_state' => 'host.status.last_hard_state', - 'host_last_hard_state_change' => 'host.status.last_hard_state_change', - 'host_check_command' => 'host.status.check_command', - 'host_last_check' => 'host.status.last_check', - 'host_next_check' => 'host.status.next_check', - 'host_check_execution_time' => 'host.status.check_execution_time', - 'host_check_latency' => 'host.status.check_latency', - 'host_notifications_enabled' => 'host.status.notifications_enabled', - 'host_last_time_up' => 'host.status.last_time_up', - 'host_last_time_down' => 'host.status.last_time_down', - 'host_last_time_unreachable' => 'host.status.last_time_unreachable', - 'host_current_check_attempt' => 'host.status.current_attempt', - 'host_max_check_attempts' => 'host.status.max_attempts', - 'host_check_type' => 'host.status.check_type', - 'host_state_type' => 'host.status.state_type', - 'host_last_notification' => 'host.status.last_notification', - 'host_next_notification' => 'host.status.next_notification', - 'host_no_more_notifications' => 'host.status.no_more_notifications', - 'host_problem_has_been_acknowledged' => 'host.status.problem_has_been_acknowledged', - 'host_acknowledgement_type' => 'host.status.acknowledgement_type', - 'host_current_notification_number' => 'host.status.current_notification_number', - 'host_passive_checks_enabled' => 'host.status.passive_checks_enabled', - 'host_active_checks_enabled' => 'host.status.active_checks_enabled', - 'host_event_handler_enabled' => 'host.status.event_handler_enabled', - 'host_flap_detection_enabled' => 'host.status.flap_detection_enabled', - 'host_is_flapping' => 'host.status.is_flapping', - 'host_percent_state_change' => 'host.status.percent_state_change', - 'host_scheduled_downtime_depth' => 'host.status.scheduled_downtime_depth', - 'host_failure_prediction_enabled' => 'host.status.failure_prediction_enabled', - 'host_process_performance_data' => 'host.status.process_performance_data', - 'host_obsessing' => 'host.status.obsess_over_host', - 'host_modified_host_attributes' => 'host.status.modified_host_attributes', - 'host_event_handler' => 'host.status.event_handler', - 'host_check_command' => 'host.status.check_command', - 'host_normal_check_interval' => 'host.status.normal_check_interval', - 'host_retry_check_interval' => 'host.status.retry_check_interval', - 'host_check_timeperiod_object_id' => 'host.status.check_timeperiod_object_id', - 'host_status_update_time' => 'host.status.status_update_time', - - 'service_host_name' => 'service.host_name', - 'service' => 'service.service_description', - 'service_description' => 'service.service_description', - 'service_display_name' => 'service.service_description', - 'service_icon_image' => 'service.icon_image', - 'service_action_url' => 'service.action_url', - 'service_notes_url' => 'service.notes_url', - 'service_state_type' => 'service.status.state_type', - 'service_output' => 'service.status.output', - 'service_long_output' => 'service.status.long_output', - 'service_perfdata' => 'service.status.perfdata', - 'service_check_source' => 'service.status.check_source', - 'service_acknowledged' => 'service.status.problem_has_been_acknowledged', - 'service_last_state_change' => 'service.status.last_state_change', - 'service_check_command' => 'service.status.check_command', - 'service_last_time_ok' => 'service.status.last_time_ok', - 'service_last_time_warning' => 'service.status.last_time_warning', - 'service_last_time_critical' => 'service.status.last_time_critical', - 'service_last_time_unknown' => 'service.status.last_time_unknown', - 'service_current_check_attempt' => 'service.status.current_check_attempt', - 'service_max_check_attempts' => 'service.status.max_check_attempts', - 'service_last_check' => 'service.status.last_check', - 'service_next_check' => 'service.status.next_check', - 'service_check_type' => 'service.status.check_type', - 'service_last_hard_state_change' => 'service.status.last_hard_state_change', - 'service_last_hard_state' => 'service.status.last_hard_state', - 'service_last_notification' => 'service.status.last_notification', - 'service_next_notification' => 'service.status.next_notification', - 'service_no_more_notifications' => 'service.status.no_more_notifications', - 'service_notifications_enabled' => 'service.status.notifications_enabled', - 'service_problem_has_been_acknowledged' => 'service.status.problem_has_been_acknowledged', - 'service_acknowledgement_type' => 'service.status.acknowledgement_type', - 'service_current_notification_number' => 'service.status.current_notification_number', - 'service_passive_checks_enabled' => 'service.status.passive_checks_enabled', - 'service_active_checks_enabled' => 'service.status.active_checks_enabled', - 'service_event_handler_enabled' => 'service.status.event_handler_enabled', - 'service_flap_detection_enabled' => 'service.status.flap_detection_enabled', - 'service_is_flapping' => 'service.status.is_flapping', - 'service_percent_state_change' => 'service.status.percent_state_change', - 'service_check_latency' => 'service.status.latency', - 'service_check_execution_time' => 'service.status.execution_time', - 'service_scheduled_downtime_depth' => 'service.status.scheduled_downtime_depth', - 'service_failure_prediction_enabled' => 'service.status.failure_prediction_enabled', - 'service_process_performance_data' => 'service.status.process_performance_data', - 'service_obsessing' => 'service.status.obsess_over_service', - 'service_modified_service_attributes' => 'service.status.modified_service_attributes', - 'service_event_handler' => 'service.status.event_handler', - 'service_check_command' => 'service.status.check_command', - 'service_normal_check_interval' => 'service.status.normal_check_interval', - 'service_retry_check_interval' => 'service.status.retry_check_interval', - 'service_check_timeperiod_object_id' => 'service.status.check_timeperiod_object_id', - 'service_status_update_time' => 'service.status.status_update_time', - 'hostgroup' => 'host.group', - 'servicegroup' => 'service.group' - ); - - /** - * @var mixed - */ - private $state; - - /** - * @var array - */ - public static $handlerParameters = array( - 'host_ipv4' => 'getAddress', - 'host_unhandled_service_count' => 'getNrOfUnhandledServices', - 'host_last_comment' => 'getLastComment', - 'service_last_comment' => 'getLastComment', - 'host_state' => 'getStateForHost', - 'host_hard_state' => 'getHardStateForHost', - 'host_handled' => 'isHandledForHost', - 'host_unhandled' => 'isHostUnhandled', - 'host_severity' => 'getSeverityForHost', - 'host_in_downtime' => 'isInDowntimeForHost', - 'host_problem' => 'isProblemForHost', - 'host_attempt' => 'getAttemptStringForHost', - 'service_state' => 'getState', - 'service_hard_state' => 'getHardState', - 'service_handled' => 'isHandled', - 'service_unhandled' => 'isUnhandled', - 'service_severity' => 'getSeverity', - 'service_in_downtime' => 'isInDowntime', - 'service_problem' => 'isProblem', - 'service_attempt' => 'getAttemptString', - ); - - public static $fieldTypes = array( - 'host_last_state_change' => self::TIMESTAMP, - 'host_last_hard_state_change' => self::TIMESTAMP, - 'host_last_check' => self::TIMESTAMP, - 'host_next_check' => self::TIMESTAMP, - 'host_last_time_up' => self::TIMESTAMP, - 'host_last_time_down' => self::TIMESTAMP, - 'host_last_time_unreachable' => self::TIMESTAMP, - 'host_status_update_time' => self::TIMESTAMP, - 'service_last_state_change' => self::TIMESTAMP, - 'service_last_hard_state_change' => self::TIMESTAMP, - 'service_last_check' => self::TIMESTAMP, - 'service_next_check' => self::TIMESTAMP, - 'service_last_time_ok' => self::TIMESTAMP, - 'service_last_time_warning' => self::TIMESTAMP, - 'service_last_time_critical' => self::TIMESTAMP, - 'service_last_time_unknown' => self::TIMESTAMP, - 'service_status_update_time' => self::TIMESTAMP - ); - - public function selectBase() - { - $target = $this->getTarget(); - $this->select()->from($target."s", array()); - } - - public function getAttemptString(&$obj) - { - return $obj->status->current_attempt . '/' . $obj->status->max_attempts; - } - - public function isProblem(&$obj) - { - return $obj->status->current_state > 0 ? 1 : 0; - } - - public function isInDowntime(&$obj) - { - return $obj->status->scheduled_downtime_depth > 0 ? 1 : 0; - } - - public function getAddress(&$obj) - { - return inet_pton($obj->host->address); - } - - public function getState(&$obj) - { - if (!$obj->status->has_been_checked) { - return 99; - } - return $obj->status->current_state; - } - - public function getHardState(&$obj) - { - if (!$obj->status->has_been_checked) { - return 99; - } else { - if ($obj->status->state_type == 1) { - return $this->status->current_state; - } else { - return $this->status->last_hard_state; - } - } - } - - - public function getSeverity(&$host) - { - $status = $host->status; - $severity = 0; - - if (!$status->has_been_checked) { - $severity += 16; - } elseif($status->current_state == 0) { - return $severity; - } elseif ($status->current_state == 1) { - $severity += 32; - } elseif ($status->current_state == 2) { - $severity += 64; - } else { - $severity += 256; - } - - if ($status->problem_has_been_acknowledged == 1) { - $severity += 2; - } elseif ($status->scheduled_downtime_depth > 0) { - $severity += 1; - } else { - $severity += 4; - } - - return $severity; - } - - public function isHandled(&$host) - { - return ($host->status->current_state == 0 || - $host->status->problem_has_been_acknowledged == 1 || - $host->status->scheduled_downtime_depth > 0) ? 1 : 0; - } - - public function isUnhandled(&$hostOrService) - { - return +!$this->isHandled($hostOrService); - } - - public function getNrOfUnhandledServices(&$obj) - { - $host = &$obj->host; - $ct = 0; - if (!isset($host->services)) { - return $ct; - } - foreach ($host->services as &$service) { - if ($service->status->current_state > 0 - && $service->status->problem_has_been_acknowledged == 0 - && $service->status->scheduled_downtime_depth == 0) { - $ct++; - } - } - return $ct; - } - - public function getLastComment(&$host) - { - if (!isset($host->comment) || empty($host->comment)) { - return null; - } - $comment = end($host->comment); - return $comment->comment_id; - } - - - /** - * @param $item - * @return null - */ - public function getHost(&$item) - { - if (!isset($this->state['service'][$item->host_name])) { - return null; - } - if (!isset($this->state['host'][$item->host_name])) { - return null; - } - return $this->state['host'][$item->host_name]; - } - - - private function getTarget() - { - foreach ($this->getColumns() as $column) { - if (preg_match("/^service/",$column)) { - return "service"; - } - } - - return "host"; - } - - - public function getStateForHost(&$obj) - { - return $this->getState($obj->host); - } - - public function getHardStateForHost(&$obj) - { - return $this->getHardState($obj->host); - } - - public function isHandledForHost(&$obj) - { - return $this->isHandled($obj->host); - } - - public function isHostUnhandled(&$obj) - { - return $this->isUnhandled($obj->host); - } - - public function getSeverityForHost(&$obj) - { - return $this->getSeverity($obj->host); - } - - public function isInDowntimeForHost(&$obj) - { - return $this->isInDowntime($obj->host); - } - - public function isProblemForHost(&$obj) - { - return $this->isProblem($obj->host); - } - - public function getAttemptStringForHost(&$obj) - { - return $this->getAttemptStringForHost($obj->host); - } - - -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php deleted file mode 100644 index 6f1e87f89..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php +++ /dev/null @@ -1,115 +0,0 @@ -select()->from('hosts', array())->groupByFunction( - 'groupByStatus', - $this - ); - } - - public function groupByStatus(&$indices) - { - $hostsPending = 0; - $hostsUp = 0; - $hostsDownHandled = 0; - $hostsDownUnhandled = 0; - $hostsUnreachableHandled = 0; - $hostsUnreachableUnhandled = 0; - $servicesPending = 0; - $servicesOk = 0; - $servicesCriticalHandled = 0; - $servicesCriticalUnhandled = 0; - $servicesWarningHandled = 0; - $servicesWarningUnhandled = 0; - $servicesUnknownHandled = 0; - $servicesUnknownUnhandled = 0; - foreach ($indices['host'] as $hostName) { - $host = $this->ds->getObjectByName('host', $hostName); - $hostStatus = $host->status; - if ($hostStatus->has_been_checked !== '1') { - ++$hostsPending; - } elseif ($hostStatus->current_state === '0') { - ++$hostsUp; - } elseif ($hostStatus->current_state === '2') { - if ($hostStatus->problem_has_been_acknowledged === '1' - || $hostStatus->scheduled_downtime_depth === '1' - ) { - ++$hostsDownHandled; - } else { - ++$hostsDownUnhandled; - } - } elseif ($hostStatus->current_state === '1') { - if ($hostStatus->problem_has_been_acknowledged === '1' - || $hostStatus->scheduled_downtime_depth === '1' - ) { - ++$hostsUnreachableHandled; - } else { - ++$hostsUnreachableUnhandled; - } - } - if ($host->services === null) { - // Host does not have any service associated - continue; - } - foreach ($host->services as $service) { - $serviceStatus = $service->status; - if ($serviceStatus->has_been_checked !== '1') { - ++$servicesPending; - } elseif ($serviceStatus->current_state === '0') { - ++$servicesOk; - } elseif ($serviceStatus->current_state === '2') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesCriticalHandled; - } else { - ++$servicesCriticalUnhandled; - } - } elseif ($serviceStatus->current_state === '1') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesWarningHandled; - } else { - ++$servicesWarningUnhandled; - } - } elseif ($serviceStatus->current_state === '3') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesUnknownHandled; - } else { - ++$servicesUnknownUnhandled; - } - } - } - } - $rs = array( - 'hosts_up' => $hostsUp, - 'hosts_unreachable_handled' => $hostsUnreachableHandled, - 'hosts_unreachable_unhandled' => $hostsUnreachableUnhandled, - 'hosts_down_handled' => $hostsDownHandled, - 'hosts_down_unhandled' => $hostsDownUnhandled, - 'hosts_pending' => $hostsPending, - 'services_ok' => $servicesOk, - 'services_unknown_handled' => $servicesUnknownHandled, - 'services_unknown_unhandled' => $servicesUnknownUnhandled, - 'services_critical_handled' => $servicesCriticalHandled, - 'services_critical_unhandled' => $servicesCriticalUnhandled, - 'services_warning_handled' => $servicesWarningHandled, - 'services_warning_unhandled' => $servicesWarningUnhandled, - 'services_pending' => $servicesPending - ); - return array((object) array_intersect_key($rs, array_flip($this->getColumns()))); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php deleted file mode 100644 index 33b006c86..000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php +++ /dev/null @@ -1,218 +0,0 @@ -selectBase(); - } - - abstract public function selectBase(); - - /** - * Orders the resultset - * - * @param string $column Either a string in the 'FIELD ASC/DESC format or only the field - * @param null $dir 'asc' or 'desc' - * @return Query Returns this query,for fluent interface - */ - public function order($column, $dir = null, $isFunction = false) - { - - if ($column) { - $column = strval($column); - if (isset(static::$mappedParameters[$column])) { - parent::order(static::$mappedParameters[$column], strtolower($dir)); - } elseif (isset(static::$handlerParameters[$column])) { - parent::orderByFn(array($this, static::$handlerParameters[$column]), strtolower($dir)); - } else { - Logger::info("Tried to sort by unknown column %s", $column); - } - } - return $this; - } - - - - private $functionMap = array( - "TO_DATE" => "toDateFormat" - ); - - - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $item - * @param The $field - * @return The|string - * @throws \InvalidArgumentException - */ - public function get(&$item, $field) - { - $result = null; - if (isset($item->$field)) { - $result = $item->$field; - } elseif (isset(static::$mappedParameters[$field])) { - $result = $this->getMappedParameter($item, $field); - } elseif (isset(static::$handlerParameters[$field])) { - $hdl = static::$handlerParameters[$field]; - $result = $this->$hdl($item); - } - - return $result; - } - - private function applyPropertyFunction($function, $value) - { - if (!isset($this->functionMap[$function])) { - return $value; - } - $fn = $this->functionMap[$function]; - - return $this->$fn($value); - } - - private function toDateFormat($value) - { - if (is_numeric($value)) { - return date("Y-m-d H:i:s", intval($value)); - } else { - return $value; - } - } - - private function getMappedParameter(&$item, $field) - { - $matches = array(); - $fieldDef = static::$mappedParameters[$field]; - - $function = false; - if (preg_match_all('/(?P\w+)\((?P.*)\)/', $fieldDef, $matches)) { - $function = $matches["FUNCTION"][0]; - $fieldDef = $matches["PARAMETER"][0]; - } - $mapped = explode(".", $fieldDef); - $res = $item; - - foreach ($mapped as $map) { - if (is_array($res)) { - $subResult = array(); - foreach ($res as $subitem) { - if (!isset($subitem->$map)) { - continue; - } - $subResult[] = $subitem->$map; - } - $res = join(',', $subResult); - } else { - if (!isset($res->$map)) { - return ""; - } - $res = $res->$map; - } - } - - if ($function) { - return $this->applyPropertyFunction($function, $res); - } - return $res; - } - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $field - * @return The|string - */ - public function getMappedField($field) - { - if (isset(static::$mappedParameters[$field])) { - return static::$mappedParameters[$field]; - } - return $field; - } - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $item - * @param The $field - * @return bool - */ - public function exists(&$item, $field) - { - return (isset($item->$field) - || isset(static::$mappedParameters[$field]) - || isset(static::$handlerParameters[$field]) - ); - } - - - public function isValidFilterTarget($field) - { - return true; - } - - public function isTimestamp($field) - { - return isset(static::$fieldTypes[$field]) && static::$fieldTypes[$field] === self::TIMESTAMP; - } - -} - diff --git a/modules/monitoring/library/Monitoring/Command/AcknowledgeCommand.php b/modules/monitoring/library/Monitoring/Command/AcknowledgeCommand.php deleted file mode 100644 index 641a2ae73..000000000 --- a/modules/monitoring/library/Monitoring/Command/AcknowledgeCommand.php +++ /dev/null @@ -1,164 +0,0 @@ -expireTime = $expire; - $this->comment = $comment; - $this->notify = $notify; - $this->sticky = $sticky; - } - - /** - * Set the time when this acknowledgement should expire - * - * @param int $expireTime The time as UNIX timestamp or -1 if it shouldn't expire - * - * @return self - */ - public function setExpire($expireTime) - { - $this->expireTime = (int) $expireTime; - return $this; - } - - /** - * Set the comment for this acknowledgement - * - * @param Comment $comment - * - * @return self - */ - public function setComment(Comment $comment) - { - $this->comment = $comment; - return $this; - } - - /** - * Set whether the notify flag of this acknowledgment should be set - * - * @param bool $state - * - * @return self - */ - public function setNotify($state) - { - $this->notify = (bool) $state; - return $this; - } - - /** - * Set whether this acknowledgement is of type sticky - * - * @param bool $state - * - * @return self - */ - public function setSticky($state) - { - $this->sticky = (bool) $state; - return $this; - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - $parameters = array_merge( - array( - $this->sticky ? '2' : '0', - $this->notify ? '1' : '0' - ), - $this->comment->getArguments() - ); - - if ($this->expireTime > -1) { - array_splice($parameters, 3, 0, array($this->expireTime)); - } - - return $parameters; - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - $parameters = $this->getArguments(); - return sprintf('ACKNOWLEDGE_HOST_PROBLEM%s;', $this->expireTime > -1 ? '_EXPIRE' : '') - . implode(';', array_merge(array($hostname), $parameters)); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - $parameters = $this->getArguments(); - return sprintf('ACKNOWLEDGE_SVC_PROBLEM%s;', $this->expireTime > -1 ? '_EXPIRE' : '') - . implode(';', array_merge(array($hostname, $servicename), $parameters)); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/AddCommentCommand.php b/modules/monitoring/library/Monitoring/Command/AddCommentCommand.php deleted file mode 100644 index 0464d5cb2..000000000 --- a/modules/monitoring/library/Monitoring/Command/AddCommentCommand.php +++ /dev/null @@ -1,79 +0,0 @@ -comment = $comment; - } - - /** - * Set the comment for this command - * - * @param Comment $comment - * - * @return self - */ - public function setComment(Comment $comment) - { - $this->comment = $comment; - return $this; - } - - public function getArguments() - { - return $this->comment->getArguments(); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - return sprintf('ADD_HOST_COMMENT;%s;', $hostname) . implode(';', $this->getArguments()); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - return sprintf('ADD_SVC_COMMENT;%s;%s;', $hostname, $servicename) - . implode(';', $this->getArguments()); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/CustomNotificationCommand.php b/modules/monitoring/library/Monitoring/Command/CustomNotificationCommand.php deleted file mode 100644 index 21cf22520..000000000 --- a/modules/monitoring/library/Monitoring/Command/CustomNotificationCommand.php +++ /dev/null @@ -1,139 +0,0 @@ -comment = $comment; - $this->forced = $forced; - $this->broadcast = $broadcast; - } - - /** - * Set the comment for this notification - * - * @param Comment $comment - * - * @return self - */ - public function setComment(Comment $comment) - { - $this->comment = $comment; - return $this; - } - - /** - * Set whether this notification is forced - * - * @param bool $state - * - * @return self - */ - public function setForced($state) - { - $this->forced = (bool) $state; - return $this; - } - - /** - * Set whether this notification is sent to all contacts - * - * @param bool $state - * - * @return self - */ - public function setBroadcast($state) - { - $this->broadcast = (bool) $state; - return $this; - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - $options = 0; - if ($this->forced) { - $options |= 2; - } - if ($this->broadcast) { - $options |= 1; - } - return array_merge(array($options), $this->comment->getArguments(true)); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - return 'SEND_CUSTOM_HOST_NOTIFICATION;' . implode(';', array_merge(array($hostname), $this->getArguments())); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - return 'SEND_CUSTOM_SVC_NOTIFICATION;' . implode( - ';', - array_merge( - array($hostname, $servicename), - $this->getArguments() - ) - ); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/DelayNotificationCommand.php b/modules/monitoring/library/Monitoring/Command/DelayNotificationCommand.php deleted file mode 100644 index 7f8dcf749..000000000 --- a/modules/monitoring/library/Monitoring/Command/DelayNotificationCommand.php +++ /dev/null @@ -1,87 +0,0 @@ -delay = $delay; - } - - /** - * Set how long notifications should be delayed - * - * @param int $seconds In seconds - * - * @return self - */ - public function setDelay($seconds) - { - $this->delay = (int) $seconds; - return $this; - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - return array($this->delay); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - return 'DELAY_HOST_NOTIFICATION;' . implode(';', array_merge(array($hostname), $this->getArguments())); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - return 'DELAY_SVC_NOTIFICATION;' . implode( - ';', - array_merge( - array($hostname, $servicename), - $this->getArguments() - ) - ); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/DisableNotificationWithExpireCommand.php b/modules/monitoring/library/Monitoring/Command/DisableNotificationWithExpireCommand.php deleted file mode 100644 index d6fb6322a..000000000 --- a/modules/monitoring/library/Monitoring/Command/DisableNotificationWithExpireCommand.php +++ /dev/null @@ -1,92 +0,0 @@ -globalCommand = true; - } - - /** - * Setter for expiration timestamp - * - * @param integer $timestamp - */ - public function setExpirationTimestamp($timestamp) - { - $this->expirationTimestamp = $timestamp; - } - - /** - * Return this command's arguments in the order expected by the actual command definition - * - * @return array - */ - public function getArguments() - { - return array($this->expirationTimestamp); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * @throws ProgrammingError - * - * @return string The string representation of the command - */ - public function getHostCommand($hostname) - { - throw new ProgrammingError('This is not supported for single objects'); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * @throws ProgrammingError - * @return string The string representation of the command# - */ - public function getServiceCommand($hostname, $servicename) - { - throw new ProgrammingError('This is not supported for single objects'); - } - - /** - * Create a global command - * - * @param string $instance - * - * @return string - */ - public function getGlobalCommand($instance = null) - { - return sprintf( - 'DISABLE_NOTIFICATIONS_EXPIRE_TIME;%d;%s', - time(), - implode(';', $this->getArguments()) - ); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/Exception/TransportException.php b/modules/monitoring/library/Monitoring/Command/Exception/TransportException.php new file mode 100644 index 000000000..440f21770 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Exception/TransportException.php @@ -0,0 +1,14 @@ +expireTime = (int) $expireTime; + return $this; + } + + /** + * Get the date and time when notifications should be re-enabled after disabling + * + * @return int|null Unix timestamp + */ + public function getExpireTime() + { + return $this->expireTime; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Instance/ToggleInstanceFeatureCommand.php b/modules/monitoring/library/Monitoring/Command/Instance/ToggleInstanceFeatureCommand.php new file mode 100644 index 000000000..01b26a009 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Instance/ToggleInstanceFeatureCommand.php @@ -0,0 +1,123 @@ +feature = (string) $feature; + return $this; + } + + /** + * Get the feature that is to be enabled or disabled + * + * @return string + */ + public function getFeature() + { + return $this->feature; + } + + /** + * Set whether the feature should be enabled or disabled + * + * @param bool $enabled + * + * @return $this + */ + public function setEnabled($enabled = true) + { + $this->enabled = (bool) $enabled; + return $this; + } + + /** + * Get whether the feature should be enabled or disabled + * + * @return bool + */ + public function getEnabled() + { + return $this->enabled; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/AcknowledgeProblemCommand.php b/modules/monitoring/library/Monitoring/Command/Object/AcknowledgeProblemCommand.php new file mode 100644 index 000000000..4dff07c4a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/AcknowledgeProblemCommand.php @@ -0,0 +1,145 @@ +sticky = (bool) $sticky; + return $this; + } + + /** + * Is the acknowledgement sticky? + * + * @return bool + */ + public function getSticky() + { + return $this->sticky; + } + + /** + * Set whether to send a notification about the acknowledgement + * + * @param bool $notify + * + * @return $this + */ + public function setNotify($notify = true) + { + $this->notify = (bool) $notify; + return $this; + } + + /** + * Get whether to send a notification about the acknowledgement + * + * @return bool + */ + public function getNotify() + { + return $this->notify; + } + + /** + * Set whether the comment associated with the acknowledgement is persistent + * + * @param bool $persistent + * + * @return $this + */ + public function setPersistent($persistent = true) + { + $this->persistent = (bool) $persistent; + return $this; + } + + /** + * Is the comment associated with the acknowledgement is persistent? + * + * @return bool + */ + public function getPersistent() + { + return $this->persistent; + } + + /** + * Set the time when the acknowledgement should expire + * + * @param int $expireTime + * + * @return $this + */ + public function setExpireTime($expireTime) + { + $this->expireTime = (int) $expireTime; + return $this; + } + + /** + * Get the time when the acknowledgement should expire + * + * @return int|null + */ + public function getExpireTime() + { + return $this->expireTime; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/AddCommentCommand.php b/modules/monitoring/library/Monitoring/Command/Object/AddCommentCommand.php new file mode 100644 index 000000000..a7540f4f8 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/AddCommentCommand.php @@ -0,0 +1,50 @@ +persistent = $persistent; + return $this; + } + + /** + * Is the comment persistent? + * + * @return bool + */ + public function getPersistent() + { + return $this->persistent; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php new file mode 100644 index 000000000..850566ff9 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php @@ -0,0 +1,50 @@ +commentId = (int) $commentId; + return $this; + } + + /** + * Get the ID of the comment that is to be deleted + * + * @return int + */ + public function getCommentId() + { + return $this->commentId; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php new file mode 100644 index 000000000..67e271dbb --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php @@ -0,0 +1,50 @@ +downtimeId = (int) $downtimeId; + return $this; + } + + /** + * Get the ID of the downtime that is to be deleted + * + * @return int + */ + public function getDowntimeId() + { + return $this->downtimeId; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ObjectCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ObjectCommand.php new file mode 100644 index 000000000..1c8910066 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ObjectCommand.php @@ -0,0 +1,62 @@ +assertOneOf($this->allowedObjects); + $this->object = $object; + return $this; + } + + /** + * Get the involved object + * + * @return MonitoredObject + */ + public function getObject() + { + return $this->object; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ProcessCheckResultCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ProcessCheckResultCommand.php new file mode 100644 index 000000000..9866aeb52 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ProcessCheckResultCommand.php @@ -0,0 +1,177 @@ + array( + self::HOST_UP, self::HOST_DOWN, self::HOST_UNREACHABLE + ), + self::TYPE_SERVICE => array( + self::SERVICE_OK, self::SERVICE_WARNING, self::SERVICE_CRITICAL, self::SERVICE_UNKNOWN + ) + ); + + /** + * Status code of the host or service check result + * + * @var int + */ + protected $status; + + /** + * Text output of the host or service check result + * + * @var string + */ + protected $output; + + /** + * Optional performance data of the host or service check result + * + * @var string + */ + protected $performanceData; + + + /** + * Set the status code of the host or service check result + * + * @param int $status + * + * @return $this + * + * @throws LogicException If the object is null + * @throws InvalidArgumentException If status is not one of the valid status codes for the object's type + */ + public function setStatus($status) + { + if ($this->object === null) { + throw new LogicException('You\'re required to call setObject() before calling setStatus()'); + } + $status = (int) $status; + if (! in_array($status, self::$statusCodes[$this->object->getType()])) { + throw new InvalidArgumentException(sprintf( + 'The status code %u you provided is not one of the valid status codes for type %s', + $status, + $this->object->getType() + )); + } + $this->status = $status; + return $this; + } + + /** + * Get the status code of the host or service check result + * + * @return int + */ + public function getStatus() + { + return $this->status; + } + + /** + * Set the text output of the host or service check result + * + * @param string $output + * + * @return $this + */ + public function setOutput($output) + { + $this->output = (string) $output; + return $this; + } + + /** + * Get the text output of the host or service check result + * + * @return string + */ + public function getOutput() + { + return $this->output; + } + + /** + * Set the performance data of the host or service check result + * + * @param string $performanceData + * + * @return $this + */ + public function setPerformanceData($performanceData) + { + $this->performanceData = (string) $performanceData; + return $this; + } + + /** + * Get the performance data of the host or service check result + * + * @return string + */ + public function getPerformanceData() + { + return $this->performanceData; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/PropagateHostDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/PropagateHostDowntimeCommand.php new file mode 100644 index 000000000..89591c8fc --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/PropagateHostDowntimeCommand.php @@ -0,0 +1,49 @@ +triggered = (bool) $triggered; + return $this; + } + + /** + * Get whether the downtime for child hosts are all set to be triggered by this' host downtime + * + * @return bool + */ + public function getTriggered() + { + return $this->triggered; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/RemoveAcknowledgementCommand.php b/modules/monitoring/library/Monitoring/Command/Object/RemoveAcknowledgementCommand.php new file mode 100644 index 000000000..6f9f99432 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/RemoveAcknowledgementCommand.php @@ -0,0 +1,20 @@ +ofAllServices = (bool) $ofAllServices; + return $this; + } + + /** + * Get whether to schedule a check of all services associated with a particular host + * + * @return bool + */ + public function getOfAllServices() + { + return $this->ofAllServices; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ScheduleHostDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ScheduleHostDowntimeCommand.php new file mode 100644 index 000000000..ea7e31dc6 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ScheduleHostDowntimeCommand.php @@ -0,0 +1,49 @@ +forAllServices = (bool) $forAllServices; + return $this; + } + + /** + * Get whether to schedule a downtime for all services associated with a particular host + * + * @return bool + */ + public function getForAllServices() + { + return $this->forAllServices; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php new file mode 100644 index 000000000..fc2037146 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php @@ -0,0 +1,95 @@ +checkTime = (int) $checkTime; + return $this; + } + + /** + * Get the time when the next check of a host or service is to be scheduled + * + * @return int Unix timestamp + */ + public function getCheckTime() + { + return $this->checkTime; + } + + /** + * Set whether the check is forced + * + * @param bool $forced + * + * @return $this + */ + public function setForced($forced = true) + { + $this->forced = (bool) $forced; + return $this; + } + + /** + * Is the check forced? + * + * @return bool + */ + public function getForced() + { + return $this->forced; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Module\Monitoring\Command\Object\IcingaCommand::getName() For the method documentation. + */ + public function getName() + { + return 'ScheduleCheck'; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceDowntimeCommand.php new file mode 100644 index 000000000..24be3512b --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceDowntimeCommand.php @@ -0,0 +1,191 @@ +start = (int) $start; + return $this; + } + + /** + * Get the time when the downtime should start + * + * @return int Unix timestamp + */ + public function getStart() + { + return $this->start; + } + + /** + * Set the time when the downtime should end + * + * @param int $end Unix timestamp + * + * @return $this + */ + public function setEnd($end) + { + $this->end = (int) $end; + return $this; + } + + /** + * Get the time when the downtime should end + * + * @return int Unix timestamp + */ + public function getEnd() + { + return $this->end; + } + + /** + * Set whether it's a fixed or flexible downtime + * + * @param boolean $fixed + * + * @return $this + */ + public function setFixed($fixed = true) + { + $this->fixed = (bool) $fixed; + return $this; + } + + /** + * Is the downtime fixed? + * + * @return boolean + */ + public function getFixed() + { + return $this->fixed; + } + + /** + * Set the ID of the downtime which triggers this downtime + * + * @param int $triggerId + * + * @return $this + */ + public function setTriggerId($triggerId) + { + $this->triggerId = (int) $triggerId; + return $this; + } + + /** + * Get the ID of the downtime which triggers this downtime + * + * @return int|null + */ + public function getTriggerId() + { + return $this->triggerId; + } + + /** + * Set the duration in seconds the downtime must last if it's a flexible downtime + * + * @param int $duration + * + * @return $this + */ + public function setDuration($duration) + { + $this->duration = (int) $duration; + return $this; + } + + /** + * Get the duration in seconds the downtime must last if it's a flexible downtime + * + * @return int|null + */ + public function getDuration() + { + return $this->duration; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Module\Monitoring\Command\Object\IcingaCommand::getName() For the method documentation. + */ + public function getName() + { + return 'ScheduleDowntime'; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/ToggleObjectFeatureCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ToggleObjectFeatureCommand.php new file mode 100644 index 000000000..c6cbeaa50 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/ToggleObjectFeatureCommand.php @@ -0,0 +1,114 @@ +feature = (string) $feature; + return $this; + } + + /** + * Get the feature that is to be enabled or disabled + * + * @return string + */ + public function getFeature() + { + return $this->feature; + } + + /** + * Set whether the feature should be enabled or disabled + * + * @param bool $enabled + * + * @return $this + */ + public function setEnabled($enabled = true) + { + $this->enabled = (bool) $enabled; + return $this; + } + + /** + * Get whether the feature should be enabled or disabled + * + * @return bool + */ + public function getEnabled() + { + return $this->enabled; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Object/WithCommentCommand.php b/modules/monitoring/library/Monitoring/Command/Object/WithCommentCommand.php new file mode 100644 index 000000000..695c53cc6 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Object/WithCommentCommand.php @@ -0,0 +1,71 @@ +author = (string) $author; + return $this; + } + + /** + * Get the author + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Set the comment + * + * @param string $comment + * + * @return $this + */ + public function setComment($comment) + { + $this->comment = (string) $comment; + return $this; + } + + /** + * Get the comment + * + * @return string + */ + public function getComment() + { + return $this->comment; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandFileCommandRenderer.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandFileCommandRenderer.php new file mode 100644 index 000000000..8b4c2d0a0 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandFileCommandRenderer.php @@ -0,0 +1,487 @@ +getName(); + if (! method_exists($this, $renderMethod)) { + die($renderMethod); + } + if ($now === null) { + $now = time(); + } + return sprintf('[%u] %s', $now, $this->$renderMethod($command)); + } + + public function renderAddComment(AddCommentCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + 'ADD_HOST_COMMENT;%s', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + 'ADD_SVC_COMMENT;%s;%s', + $object->getHost()->getName(), + $object->getName() + ); + } + return sprintf( + '%s;%u;%s;%s', + $commandString, + $command->getPersistent(), + $command->getAuthor(), + $command->getComment() + ); + } + + public function renderSendCustomNotification(SendCustomNotificationCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + 'SEND_CUSTOM_HOST_NOTIFICATION;%s', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + 'SEND_CUSTOM_SVC_NOTIFICATION;%s;%s', + $object->getHost()->getName(), + $object->getName() + ); + } + $options = 0; // 0 for no options + if ($command->getBroadcast() === true) { + $options |= 1; + } + if ($command->getForced() === true) { + $options |= 2; + } + return sprintf( + '%s;%u;%s;%s', + $commandString, + $options, + $command->getAuthor(), + $command->getComment() + ); + } + + public function renderProcessCheckResult(ProcessCheckResultCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + 'PROCESS_HOST_CHECK_RESULT;%s', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + 'PROCESS_SVC_CHECK_RESULT;%s;%s', + $object->getHost()->getName(), + $object->getName() + ); + } + $output = $command->getOutput(); + if ($command->getPerformanceData() !== null) { + $output .= '|' . $command->getPerformanceData(); + } + return sprintf( + '%s;%u;%s', + $commandString, + $command->getStatus(), + $output + ); + } + + public function renderScheduleCheck(ScheduleServiceCheckCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + /** @var \Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand $command */ + if ($command->getOfAllServices() === true) { + if ($command->getForced() === true) { + $commandName = 'SCHEDULE_FORCED_HOST_SVC_CHECKS'; + } else { + $commandName = 'SCHEDULE_HOST_SVC_CHECKS'; + } + } else { + if ($command->getForced() === true) { + $commandName = 'SCHEDULE_FORCED_HOST_CHECK'; + } else { + $commandName = 'SCHEDULE_HOST_CHECK'; + } + } + $commandString = sprintf( + '%s;%s', + $commandName, + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + '%s;%s;%s', + $command->getForced() === true ? 'SCHEDULE_FORCED_SVC_CHECK' : 'SCHEDULE_SVC_CHECK', + $object->getHost()->getName(), + $object->getName() + ); + } + return sprintf( + '%s;%u', + $commandString, + $command->getCheckTime() + ); + } + + public function renderScheduleDowntime(ScheduleServiceDowntimeCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + /** @var \Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand $command */ + if ($command instanceof PropagateHostDowntimeCommand) { + /** @var \Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand $command */ + $commandName = $command->getTriggered() === true ? 'SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME' + : 'SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME'; + } elseif ($command->getForAllServices() === true) { + $commandName = 'SCHEDULE_HOST_SVC_DOWNTIME'; + } else { + $commandName = 'SCHEDULE_HOST_DOWNTIME'; + } + $commandString = sprintf( + '%s;%s', + $commandName, + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + '%s;%s;%s', + 'SCHEDULE_SVC_DOWNTIME', + $object->getHost()->getName(), + $object->getName() + ); + } + return sprintf( + '%s;%u;%u;%u;%u;%u;%s;%s', + $commandString, + $command->getStart(), + $command->getEnd(), + $command->getFixed(), + $command->getTriggerId(), + $command->getDuration(), + $command->getAuthor(), + $command->getComment() + ); + } + + public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + '%s;%s', + $command->getExpireTime() !== null ? 'ACKNOWLEDGE_HOST_PROBLEM_EXPIRE' : 'ACKNOWLEDGE_HOST_PROBLEM', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + '%s;%s;%s', + $command->getExpireTime() !== null ? 'ACKNOWLEDGE_SVC_PROBLEM_EXPIRE' : 'ACKNOWLEDGE_SVC_PROBLEM', + $object->getHost()->getName(), + $object->getName() + ); + } + $commandString = sprintf( + '%s;%u;%u;%u', + $commandString, + $command->getSticky(), + $command->getNotify(), + $command->getPersistent() + ); + if ($command->getExpireTime() !== null) { + $commandString = sprintf( + '%s;%u', + $commandString, + $command->getExpireTime() + ); + } + return sprintf( + '%s;%s;%s', + $commandString, + $command->getAuthor(), + $command->getComment() + ); + } + + public function renderToggleObjectFeature(ToggleObjectFeatureCommand $command) + { + if ($command->getEnabled() === true) { + $commandPrefix = 'ENABLE'; + } else { + $commandPrefix = 'DISABLE'; + } + switch ($command->getFeature()) { + case ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS: + $commandFormat = sprintf('%s_%%s_CHECK', $commandPrefix); + break; + case ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS: + $commandFormat = sprintf('%s_PASSIVE_%%s_CHECKS', $commandPrefix); + break; + case ToggleObjectFeatureCommand::FEATURE_OBSESSING: + if ($command->getEnabled() === true) { + $commandPrefix = 'START'; + } else { + $commandPrefix = 'STOP'; + } + $commandFormat = sprintf('%s_OBSESSING_OVER_%%s', $commandPrefix); + break; + case ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS: + $commandFormat = sprintf('%s_%%s_NOTIFICATIONS', $commandPrefix); + break; + case ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER: + $commandFormat = sprintf('%s_%%s_EVENT_HANDLER', $commandPrefix); + break; + case ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION: + $commandFormat = sprintf('%s_%%s_FLAP_DETECTION', $commandPrefix); + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + $object = $command->getObject(); + if ($object->getType() === ToggleObjectFeatureCommand::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + $commandFormat . ';%s', + 'HOST', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + $commandFormat . ';%s;%s', + 'SVC', + $object->getHost()->getName(), + $object->getName() + ); + } + return $commandString; + } + + public function renderDeleteComment(DeleteCommentCommand $command) + { + if ($command->getObject()->getType() === $command::TYPE_HOST) { + $commandString = 'DEL_HOST_COMMENT'; + } else { + $commandString = 'DEL_SVC_COMMENT'; + } + return sprintf( + '%s;%u', + $commandString, + $command->getCommentId() + ); + } + + public function renderDeleteDowntime(DeleteDowntimeCommand $command) + { + if ($command->getObject()->getType() === $command::TYPE_HOST) { + $commandString = 'DEL_HOST_DOWNTIME'; + } else { + $commandString = 'DEL_SVC_DOWNTIME'; + } + return sprintf( + '%s;%u', + $commandString, + $command->getDowntimeId() + ); + } + + public function renderRemoveAcknowledgement(RemoveAcknowledgementCommand $command) + { + $object = $command->getObject(); + if ($command->getObject()->getType() === $command::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $commandString = sprintf( + '%s;%s', + 'REMOVE_HOST_ACKNOWLEDGEMENT', + $object->getName() + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $commandString = sprintf( + '%s;%s;%s', + 'REMOVE_SVC_ACKNOWLEDGEMENT', + $object->getHost()->getName(), + $object->getName() + ); + } + return $commandString; + } + + public function renderDisableNotificationsExpire(DisableNotificationsExpireCommand $command) + { + return sprintf( + '%s;%u;%u', + 'DISABLE_NOTIFICATIONS_EXPIRE_TIME', + time(), + $command->getExpireTime() + ); + } + + public function renderToggleInstanceFeature(ToggleInstanceFeatureCommand $command) + { + switch ($command->getFeature()) { + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS: + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS: + case ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING: + case ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING: + case ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS: + case ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS: + if ($command->getEnabled() === true) { + $commandPrefix = 'START'; + } else { + $commandPrefix = 'STOP'; + } + break; + case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS: + case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION: + case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS: + case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA: + if ($command->getEnabled() === true) { + $commandPrefix = 'ENABLE'; + } else { + $commandPrefix = 'DISABLE'; + } + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + switch ($command->getFeature()) { + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'EXECUTING_HOST_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'EXECUTING_SVC_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'EVENT_HANDLERS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'FLAP_DETECTION' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'NOTIFICATIONS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'OBSESSING_OVER_HOST_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'OBSESSING_OVER_SVC_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'ACCEPTING_PASSIVE_HOST_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'ACCEPTING_PASSIVE_SVC_CHECKS' + ); + break; + case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA: + $commandString = sprintf( + '%s_%s', + $commandPrefix, + 'PERFORMANCE_DATA' + ); + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + return $commandString; + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandRendererInterface.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandRendererInterface.php new file mode 100644 index 000000000..f901e3850 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaCommandRendererInterface.php @@ -0,0 +1,10 @@ +checkTime = $checkTime; - $this->forced = $forced; - } - - /** - * Set when to schedule this check - * - * @param int $checkTime The time as UNIX timestamp - * - * @return self - */ - public function setCheckTime($checkTime) - { - $this->checkTime = (int) $checkTime; - return $this; - } - - /** - * Set whether this check is forced - * - * @param bool $state - * - * @return self - */ - public function setForced($state) - { - $this->forced = (bool) $state; - return $this; - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - return array($this->checkTime); - } - - /** - * Return the command as a string for the given host or all of it's services - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - return sprintf( - 'SCHEDULE%s_HOST_%s;', - $this->forced ? '_FORCED' : '', - $this->onlyServices ? 'SVC_CHECKS' : 'CHECK' - ) . implode(';', array_merge(array($hostname), $this->getArguments())); - } - - /** - * Return the command as a string for the given service - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - return sprintf('SCHEDULE%s_SVC_CHECK;', $this->forced ? '_FORCED' : '') - . implode(';', array_merge(array($hostname, $servicename), $this->getArguments())); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/ScheduleDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/ScheduleDowntimeCommand.php deleted file mode 100644 index e1d79ddce..000000000 --- a/modules/monitoring/library/Monitoring/Command/ScheduleDowntimeCommand.php +++ /dev/null @@ -1,257 +0,0 @@ -startTime = (int) $startTime; - return $this; - } - - /** - * Set when to end this downtime - * - * @param int $endTime - * - * @return self - */ - public function setEnd($endTime) - { - $this->endTime = (int) $endTime; - return $this; - } - - /** - * Set the comment for this downtime - * - * @param Comment $comment - * - * @return self - */ - public function setComment(Comment $comment) - { - $this->comment = $comment; - return $this; - } - - /** - * Set whether this downtime is fixed or flexible - * - * @param bool $state - * - * @return self - */ - public function setFixed($state) - { - $this->fixed = (bool) $state; - return $this; - } - - /** - * Set the duration of this downtime - * - * @param int $duration - * - * @return self - */ - public function setDuration($duration) - { - $this->duration = (int) $duration; - return $this; - } - - /** - * Set the triggering id for this downtime - * - * @param int $triggerId - * - * @return self - */ - public function setTriggerId($triggerId) - { - $this->triggerId = (int) $triggerId; - return $this; - } - - /** - * Initialise a new command object to schedule a downtime - * - * @param int $startTime When to start this downtime as UNIX timestamp - * @param int $endTime When to end this downtime as UNIX timestamp - * @param Comment $comment The comment to use for this downtime - * @param bool $fixed Whether this downtime is fixed or flexible - * @param int $duration How long in seconds this downtime should apply if flexible - * @param int $triggerId The ID of the triggering downtime - */ - public function __construct($startTime, $endTime, Comment $comment, $fixed = true, $duration = 0, $triggerId = 0) - { - $this->startTime = $startTime; - $this->endTime = $endTime; - $this->comment = $comment; - $this->fixed = $fixed; - $this->duration = $duration; - $this->triggerId = $triggerId; - } - - /** - * Include all children hosts with this command - * - * @param bool $state - * @param bool $trigger Whether children are triggered by this downtime - * - * @return self - */ - public function includeChildren($state = true, $trigger = false) { - $this->triggerChildren = (bool) $trigger; - return parent::includeChildren($state); - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - return array_merge( - array( - $this->startTime, - $this->endTime, - $this->fixed ? '1' : '0', - $this->triggerId, - $this->duration - ), - $this->comment->getArguments(true) - ); - } - - /** - * Return the command as a string for the given host - * - * @param type $hostname The name of the host to insert - * - * @return string The string representation of the command - */ - public function getHostCommand($hostname) - { - if ($this->withChildren) { - return sprintf('SCHEDULE_AND_PROPAGATE%s_HOST_DOWNTIME;', $this->triggerChildren ? '_TRIGGERED' : '') - . implode(';', array_merge(array($hostname), $this->getArguments())); - } else { - return sprintf('SCHEDULE_HOST%s_DOWNTIME;', $this->onlyServices ? '_SVC' : '') - . implode(';', array_merge(array($hostname), $this->getArguments())); - } - } - - /** - * Return the command as a string for the given service - * - * @param type $hostname The name of the host to insert - * @param type $servicename The name of the service to insert - * - * @return string The string representation of the command - */ - public function getServiceCommand($hostname, $servicename) - { - return 'SCHEDULE_SVC_DOWNTIME;' . implode( - ';', - array_merge( - array($hostname, $servicename), - $this->getArguments() - ) - ); - } - - /** - * Return the command as a string for the given hostgroup - * - * @param type $hostgroup The name of the hostgroup to insert - * - * @return string The string representation of the command - */ - public function getHostgroupCommand($hostgroup) - { - return sprintf('SCHEDULE_HOSTGROUP_%s_DOWNTIME;', $this->withoutHosts ? 'SVC' : 'HOST') - . implode(';', array_merge(array($hostgroup), $this->getArguments())); - } - - /** - * Return the command as a string for the given servicegroup - * - * @param type $servicegroup The name of the servicegroup to insert - * - * @return string The string representation of the command - */ - public function getServicegroupCommand($servicegroup) - { - return sprintf('SCHEDULE_SERVICEGROUP_%s_DOWNTIME;', $this->withoutServices ? 'HOST' : 'SVC') - . implode(';', array_merge(array($servicegroup), $this->getArguments())); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/SingleArgumentCommand.php b/modules/monitoring/library/Monitoring/Command/SingleArgumentCommand.php deleted file mode 100644 index 27fecd48d..000000000 --- a/modules/monitoring/library/Monitoring/Command/SingleArgumentCommand.php +++ /dev/null @@ -1,183 +0,0 @@ -value = $value; - } - - /** - * Setter for command names - * - * @param string $hostCommand - * @param string $serviceCommand - */ - public function setCommand($hostCommand, $serviceCommand) - { - $this->hostCommand = $hostCommand; - $this->serviceCommand = $serviceCommand; - } - - /** - * Set a bunch of global commands - * - * @param array $commands One or more commands to control global parameters - */ - public function setGlobalCommands(array $commands) - { - $this->globalCommands = $commands; - $this->globalCommand = true; - } - - /** - * Ignore object values upon command creation - * - * @param bool $flag - */ - public function setObjectIgnoreFlag($flag = true) - { - $this->ignoreObject = (bool) $flag; - } - - /** - * Return this command's arguments in the order expected by the actual command definition - * - * @return array - */ - public function getArguments() - { - if ($this->value !== null) { - return array($this->value); - } else { - return array(); - } - } - - /** - * Build the argument string based on objects and arguments - * - * @param array $objectNames - * - * @return string String to append to command - */ - private function getArgumentString(array $objectNames) - { - $data = array(); - if ($this->ignoreObject === true) { - $data = $this->getArguments(); - } else { - $data = array_merge($objectNames, $this->getArguments()); - } - - return implode(';', $data); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - */ - public function getHostCommand($hostname) - { - return strtoupper($this->hostCommand). ';' . $this->getArgumentString(array($hostname)); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - */ - public function getServiceCommand($hostname, $servicename) - { - return strtoupper($this->serviceCommand) - . ';' - . $this->getArgumentString(array($hostname, $servicename)); - } - - /** - * Getter for global command if configured - * - * @param string $instance - * - * @throws ProgrammingError - * @return string - */ - public function getGlobalCommand($instance = null) - { - if (!count($this->globalCommands)) { - // This throws exception for us that globalCommand - // is not implemented properly - parent::getGlobalCommand(); - } - - if ($this->value === 'host') { - return strtoupper($this->globalCommands[0]); - } - - if ($this->value === 'service') { - if (count($this->globalCommands) < 2) { - throw new ProgrammingError('If use global values you need at least 2 global commands'); - } - - return strtoupper($this->globalCommands[1]); - } - - return strtoupper(implode(';', $this->globalCommands)); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/SubmitPassiveCheckresultCommand.php b/modules/monitoring/library/Monitoring/Command/SubmitPassiveCheckresultCommand.php deleted file mode 100644 index 262987de1..000000000 --- a/modules/monitoring/library/Monitoring/Command/SubmitPassiveCheckresultCommand.php +++ /dev/null @@ -1,133 +0,0 @@ -state = $state; - $this->output = $output; - $this->perfData = $perfData; - } - - /** - * Set which plugin-state is being reported - * - * @param int $state - * - * @return self - */ - public function setState($state) - { - $this->state = (int) $state; - return $this; - } - - /** - * Set the plugin-output to include in the result - * - * @param string $output - * - * @return self - */ - public function setOutput($output) - { - $this->output = (string) $output; - return $this; - } - - /** - * Set the performance data to include in the result - * - * @param string $perfData - * @return self - */ - public function setPerformanceData($perfData) - { - $this->perfData = (string) $perfData; - return $this; - } - - /** - * Return this command's parameters properly arranged in an array - * - * @return array - * @see Command::getArguments() - */ - public function getArguments() - { - return array( - $this->state, - $this->perfData ? $this->output . '|' . $this->perfData : $this->output - ); - } - - /** - * Return the command as a string with the given host being inserted - * - * @param string $hostname The name of the host to insert - * - * @return string The string representation of the command - * @see Command::getHostCommand() - */ - public function getHostCommand($hostname) - { - return 'PROCESS_HOST_CHECK_RESULT;' . implode(';', array_merge(array($hostname), $this->getArguments())); - } - - /** - * Return the command as a string with the given host and service being inserted - * - * @param string $hostname The name of the host to insert - * @param string $servicename The name of the service to insert - * - * @return string The string representation of the command - * @see Command::getServiceCommand() - */ - public function getServiceCommand($hostname, $servicename) - { - return 'PROCESS_SERVICE_CHECK_RESULT;' . implode( - ';', - array_merge( - array($hostname, $servicename), - $this->getArguments() - ) - ); - } -} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php new file mode 100644 index 000000000..84845f23f --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php @@ -0,0 +1,101 @@ +count() === 0) { + throw new ConfigurationError; + } + } + return self::$config; + } + + /** + * Create a transport from config + * + * @param Zend_Config $config + * + * @return LocalCommandFile|RemoteCommandFile + * @throws ConfigurationError + */ + public static function fromConfig(Zend_Config $config) + { + switch (strtolower($config->transport)) { + case 'remote': + $transport = new RemoteCommandFile(); + break; + case 'local': + case '': // Casting null to string is the empty string + $transport = new LocalCommandFile(); + break; + default: + throw new ConfigurationError(); + } + unset($config->transport); + foreach ($config as $key => $value) { + $method = 'set' . ucfirst($key); + if (! method_exists($transport, $method)) { + throw new ConfigurationError(); + } + $transport->$method($value); + } + return $transport; + } + + /** + * Create a transport by name + * + * @param string $name + * + * @return LocalCommandFile|RemoteCommandFile + * @throws ConfigurationError + */ + public static function create($name) + { + $config = self::getConfig()->get($name); + if ($config === null) { + throw new ConfigurationError(); + } + return self::fromConfig($config); + } + + /** + * Create a transport by the first section of the configuration + * + * @return LocalCommandFile|RemoteCommandFile + */ + public static function first() + { + $config = self::getConfig()->current(); + return self::fromConfig($config); + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransportInterface.php b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransportInterface.php new file mode 100644 index 000000000..21b532d49 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransportInterface.php @@ -0,0 +1,12 @@ +renderer = new IcingaCommandFileCommandRenderer(); + } + + /** + * Set the path to the local Icinga command file + * + * @param string $path + * + * @return self + */ + public function setPath($path) + { + $this->path = (string) $path; + return $this; + } + + /** + * Get the path to the local Icinga command file + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Set the mode used to open the icinga command file + * + * @param string $openMode + * + * @return self + */ + public function setOpenMode($openMode) + { + $this->openMode = (string) $openMode; + return $this; + } + + /** + * Get the mode used to open the icinga command file + * + * @return string + */ + public function getOpenMode() + { + return $this->openMode; + } + + /** + * Write the command to the local Icinga command file + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws LogicException + * @throws TransportException + */ + public function send(IcingaCommand $command, $now = null) + { + if (! isset($this->path)) { + throw new LogicException; + } + $commandString = $this->renderer->render($command, $now); + Logger::debug( + sprintf( + mt('monitoring', 'Sending external Icinga command "%s" to the local command file "%s"'), + $commandString, + $this->path + ) + ); + try { + $file = new File($this->path, $this->openMode); + $file->fwrite($commandString . "\n"); + $file->fflush(); + } catch (Exception $e) { + throw new TransportException( + mt( + 'monitoring', + 'Can\'t send external Icinga command "%s" to the local command file "%s": %s' + ), + $commandString, + $this->path, + $e + ); + } + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php new file mode 100644 index 000000000..fad4eb0a2 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php @@ -0,0 +1,209 @@ +renderer = new IcingaCommandFileCommandRenderer(); + } + + /** + * Set the remote host + * + * @param string $host + * + * @return self + */ + public function setHost($host) + { + $this->host = (string) $host; + return $this; + } + + /** + * Get the remote host + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the port to connect to on the remote host + * + * @param int $port + * + * @return self + */ + public function setPort($port) + { + $this->port = (int) $port; + return $this; + } + + /** + * Get the port to connect on the remote host + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Set the user to log in as on the remote host + * + * @param string $user + * + * @return self + */ + public function setUser($user) + { + $this->user = (string) $user; + return $this; + } + + /** + * Get the user to log in as on the remote host + * + * Defaults to current PHP process' user + * + * @return string|null + */ + public function getUser() + { + return $this->user; + } + + /** + * Set the path to the Icinga command file on the remote host + * + * @param string $path + * + * @return self + */ + public function setPath($path) + { + $this->path = (string) $path; + return $this; + } + + /** + * Get the path to the Icinga command file on the remote host + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Write the command to the Icinga command file on the remote host + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws LogicException + * @throws TransportException + */ + public function send(IcingaCommand $command, $now = null) + { + if (! isset($this->path)) { + throw new LogicException; + } + if (! isset($this->host)) { + throw new LogicException; + } + $commandString = $this->renderer->render($command, $now); + Logger::debug( + sprintf( + mt('monitoring', 'Sending external Icinga command "%s" to the remote command file "%s:%u%s"'), + $commandString, + $this->host, + $this->port, + $this->path + ) + ); + $ssh = sprintf('ssh -o BatchMode=yes -p %u', $this->port); + // -o BatchMode=yes for disabling interactive authentication methods + if (isset($this->user)) { + $ssh .= sprintf(' -l %s', escapeshellarg($this->user)); + } + $ssh .= sprintf( + ' %s "echo %s > %s" 2>&1', // Redirect stderr to stdout + escapeshellarg($this->host), + escapeshellarg($commandString), + escapeshellarg($this->path) + ); + exec($ssh, $output, $status); + if ($status !== 0) { + throw new TransportException( + mt( + 'monitoring', + 'Can\'t send external Icinga command "%s": %s' + ), + $ssh, + implode(' ', $output) + ); + } + } +} diff --git a/modules/monitoring/library/Monitoring/DataView/Contact.php b/modules/monitoring/library/Monitoring/DataView/Contact.php index 9f78a3b6a..27639b0c6 100644 --- a/modules/monitoring/library/Monitoring/DataView/Contact.php +++ b/modules/monitoring/library/Monitoring/DataView/Contact.php @@ -18,6 +18,7 @@ class Contact extends DataView public function getColumns() { return array( + 'contact', 'contact_name', 'contact_alias', 'contact_email', diff --git a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php index 870a5407b..7dd7a1cd4 100644 --- a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php +++ b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php @@ -18,7 +18,9 @@ class Contactgroup extends DataView public function getColumns() { return array( + 'contact', 'contact_name', + 'contactgroup', 'contactgroup_name', 'contactgroup_alias', 'host', diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index b7035a850..df331999e 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -4,13 +4,15 @@ namespace Icinga\Module\Monitoring\DataView; +use Countable; use Icinga\Data\Filter\Filter; -use Icinga\Data\SimpleQuery; +use Icinga\Data\Filter\FilterMatch; use Icinga\Data\Browsable; use Icinga\Data\PivotTable; use Icinga\Data\Sortable; use Icinga\Data\ConnectionInterface; use Icinga\Data\Filterable; +use Icinga\Exception\QueryException; use Icinga\Web\Request; use Icinga\Web\Url; use Icinga\Module\Monitoring\Backend; @@ -18,15 +20,15 @@ use Icinga\Module\Monitoring\Backend; /** * A read-only view of an underlying query */ -abstract class DataView implements Browsable, Filterable, Sortable +abstract class DataView implements Browsable, Countable, Filterable, Sortable { /** * The query used to populate the view * - * @var SimpleQuery + * @var \Icinga\Data\SimpleQuery */ - private $query; - + protected $query; + protected $filter; protected $connection; @@ -36,8 +38,8 @@ abstract class DataView implements Browsable, Filterable, Sortable /** * Create a new view * - * @param SimpleQuery $query Which backend to query - * @param array $columns Select columns + * @param ConnectionInterface $connection + * @param array $columns */ public function __construct(ConnectionInterface $connection, array $columns = null) { @@ -45,6 +47,18 @@ abstract class DataView implements Browsable, Filterable, Sortable $queryClass = $connection->getQueryClass($this->getQueryName()); $this->query = new $queryClass($this->connection->getResource(), $columns); $this->filter = Filter::matchAll(); + $this->init(); + } + + /** + * Initializer for `distinct purposes + * + * Implemented for `distinct as workaround + * + * @TODO Subject to change, see #7344 + */ + public function init() + { } /** @@ -61,17 +75,17 @@ abstract class DataView implements Browsable, Filterable, Sortable return $tableName; } -public function where($condition, $value = null) -{ - $this->filter->addFilter(Filter::where($condition, $value)); - $this->query->where($condition, $value); - return $this; -} + public function where($condition, $value = null) + { + $this->filter->addFilter(Filter::where($condition, $value)); + $this->query->where($condition, $value); + return $this; + } -public function dump() -{ - return $this->query->dump(); -} + public function dump() + { + return $this->query->dump(); + } /** * Retrieve columns provided by this view @@ -191,9 +205,10 @@ public function dump() * Sort the rows, according to the specified sort column and order * * @param string $column Sort column - * @param int $order Sort order, one of the SORT_ constants + * @param string $order Sort order, one of the SORT_ constants * - * @return self + * @return $this + * @throws QueryException If the sort column is not allowed * @see DataView::SORT_ASC * @see DataView::SORT_DESC * @deprecated Use DataView::order() instead @@ -201,35 +216,43 @@ public function dump() public function sort($column = null, $order = null) { $sortRules = $this->getSortRules(); - - if ($sortRules !== null) { - if ($column === null) { - $sortColumns = reset($sortRules); - if (!isset($sortColumns['columns'])) { - $sortColumns['columns'] = array(key($sortRules)); + if ($column === null) { + // Use first available sort rule as default + if (empty($sortRules)) { + return $this; + } + $sortColumns = reset($sortRules); + if (! isset($sortColumns['columns'])) { + $sortColumns['columns'] = array(key($sortRules)); + } + } else { + if (isset($sortRules[$column])) { + $sortColumns = $sortRules[$column]; + if (! isset($sortColumns['columns'])) { + $sortColumns['columns'] = array($column); } } else { - if (isset($sortRules[$column])) { - $sortColumns = $sortRules[$column]; - if (!isset($sortColumns['columns'])) { - $sortColumns['columns'] = array($column); - } - } else { - $sortColumns = array( - 'columns' => array($column), - 'order' => $order - ); - }; - } - - $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : self::SORT_ASC) : $order; - $order = (strtoupper($order) === self::SORT_ASC) ? 'ASC' : 'DESC'; - - foreach ($sortColumns['columns'] as $column) { - $this->query->order($column, $order); - } - $this->isSorted = true; + $sortColumns = array( + 'columns' => array($column), + 'order' => $order + ); + }; } + + $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : static::SORT_ASC) : $order; + $order = (strtoupper($order) === static::SORT_ASC) ? 'ASC' : 'DESC'; + + foreach ($sortColumns['columns'] as $column) { + if (! $this->isValidFilterTarget($column)) { + throw new QueryException( + t('The sort column "%s" is not allowed in "%s".'), + $column, + get_class($this) + ); + } + $this->query->order($column, $order); + } + $this->isSorted = true; return $this; } @@ -240,7 +263,7 @@ public function dump() */ public function getSortRules() { - return null; + return array(); } /** @@ -249,7 +272,7 @@ public function dump() * @param string $column * @param string $direction * - * @return self + * @return $this */ public function order($column = null, $direction = null) { @@ -284,19 +307,48 @@ public function dump() /** * Return the query which was created in the constructor * - * @return mixed + * @return \Icinga\Data\SimpleQuery */ public function getQuery() { - if (! $this->isSorted) { $this->sort(); } + if (! $this->isSorted) { + $this->order(); + } return $this->query; } public function applyFilter(Filter $filter) { + $this->validateFilterColumns($filter); + return $this->addFilter($filter); } + /** + * Validates recursive the Filter columns against the isValidFilterTarget() method + * + * @param Filter $filter + * + * @throws \Icinga\Data\Filter\FilterException + */ + public function validateFilterColumns(Filter $filter) + { + if ($filter instanceof FilterMatch) { + if (! $this->isValidFilterTarget($filter->getColumn())) { + throw new QueryException( + t('The filter column "%s" is not allowed here.'), + $filter->getColumn() + ); + } + } + + if (method_exists($filter, 'filters')) { + foreach ($filter->filters() as $filter) { + $this->validateFilterColumns($filter); + } + } + } + public function clearFilter() { $this->query->clearFilter(); @@ -326,6 +378,19 @@ public function dump() */ public function paginate($itemsPerPage = null, $pageNumber = null) { + if (! $this->isSorted) { + $this->order(); + } return $this->query->paginate($itemsPerPage, $pageNumber); } + + /** + * Count result set + * + * @return int + */ + public function count() + { + return count($this->query); + } } diff --git a/modules/monitoring/library/Monitoring/DataView/Downtime.php b/modules/monitoring/library/Monitoring/DataView/Downtime.php index 214e1afb5..217fb2cac 100644 --- a/modules/monitoring/library/Monitoring/DataView/Downtime.php +++ b/modules/monitoring/library/Monitoring/DataView/Downtime.php @@ -31,7 +31,7 @@ class Downtime extends DataView 'downtime_host', 'downtime_service', 'downtime_host_state', - 'downtiem_service_state' + 'downtime_service_state' ); } diff --git a/modules/monitoring/library/Monitoring/DataView/HostStatus.php b/modules/monitoring/library/Monitoring/DataView/HostStatus.php index 60f224264..e9b5b60dd 100644 --- a/modules/monitoring/library/Monitoring/DataView/HostStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/HostStatus.php @@ -6,6 +6,14 @@ namespace Icinga\Module\Monitoring\DataView; class HostStatus extends DataView { + /** + * @see DataView::init() + */ + public function init() + { + $this->query->setMode('host'); + } + /** * Retrieve columns provided by this view * @@ -64,7 +72,8 @@ class HostStatus extends DataView 'host_percent_state_change', 'host_modified_host_attributes', 'host_severity', - 'host_problem' + 'host_problem', + 'host_ipv4' ); } @@ -105,7 +114,7 @@ class HostStatus extends DataView public function getFilterColumns() { - return array('hostgroup', 'service_problems'); + return array('hostgroup', 'service_problems', 'servicegroup'); } public function isValidFilterTarget($column) diff --git a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php index b81b56764..9edcabc87 100644 --- a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php @@ -6,6 +6,16 @@ namespace Icinga\Module\Monitoring\DataView; class ServiceStatus extends DataView { + /** + * Sets the mode for `distinct as workaround + * + * @TODO Subject to change, see #7344 + */ + public function init() + { + $this->query->setMode('service'); + } + /** * Retrieve columns provided by this view * @@ -101,6 +111,7 @@ class ServiceStatus extends DataView 'service_flap_detection_enabled', 'service_flap_detection_enabled_changed', 'service_modified_service_attributes', + 'service_host_name' ); } diff --git a/modules/monitoring/library/Monitoring/Object/Host.php b/modules/monitoring/library/Monitoring/Object/Host.php index 1773337eb..e7521ae6b 100644 --- a/modules/monitoring/library/Monitoring/Object/Host.php +++ b/modules/monitoring/library/Monitoring/Object/Host.php @@ -4,32 +4,92 @@ namespace Icinga\Module\Monitoring\Object; -use Icinga\Module\Monitoring\DataView\HostStatus; -use Icinga\Data\Db\DbQuery; +use InvalidArgumentException; +use Icinga\Module\Monitoring\Backend; +/** + * A Icinga host + */ class Host extends MonitoredObject { - public $type = 'host'; + /** + * Host state 'UP' + */ + const STATE_UP = 0; + + /** + * Host state 'DOWN' + */ + const STATE_DOWN = 1; + + /** + * Host state 'UNREACHABLE' + */ + const STATE_UNREACHABLE = 2; + + /** + * Host state 'PENDING' + */ + const STATE_PENDING = 99; + + /** + * Type of the Icinga host + * + * @var string + */ + public $type = self::TYPE_HOST; + + /** + * Prefix of the Icinga host + * + * @var string + */ public $prefix = 'host_'; - protected function applyObjectFilter(DbQuery $query) + /** + * Host name + * + * @var string + */ + protected $host; + + /** + * The services running on the hosts + * + * @var \Icinga\Module\Monitoring\Object\Service[] + */ + protected $services; + + /** + * Create a new host + * + * @param Backend $backend Backend to fetch host information from + * @param string $host Host name + */ + public function __construct(Backend $backend, $host) { - return $query->where('host_name', $this->host_name); + parent::__construct($backend); + $this->host = $host; } - public function populate() + /** + * Get the host name + * + * @return string + */ + public function getName() { - $this->fetchComments() - ->fetchHostgroups() - ->fetchContacts() - ->fetchContactGroups() - ->fetchCustomvars() - ->fetchDowntimes(); + return $this->host; } - protected function getProperties() + /** + * Get the data view to fetch the host information from + * + * @return \Icinga\Module\Monitoring\DataView\HostStatus + */ + protected function getDataView() { - $this->view = HostStatus::fromParams(array('backend' => null), array( + return $this->backend->select()->from('hostStatus', array( 'host_name', 'host_alias', 'host_address', @@ -70,8 +130,57 @@ class Host extends MonitoredObject 'host_notes_url', 'host_modified_host_attributes', 'host_problem', - 'process_perfdata' => 'host_process_performance_data', - ))->where('host_name', $this->params->get('host')); - return $this->view->getQuery()->fetchRow(); + 'host_process_performance_data' + )) + ->where('host_name', $this->host); + } + + /** + * Fetch the services running on the host + * + * @return $this + */ + public function fetchServices() + { + $services = array(); + foreach ($this->backend->select()->from('serviceStatus', array('service_description')) + ->where('host_name', $this->host) + ->getQuery() + ->fetchAll() as $service) { + $services[] = new Service($this->backend, $this->host, $service->service_description); + } + $this->services = $services; + return $this; + } + + /** + * Get the optional translated textual representation of a host state + * + * @param int $state + * @param bool $translate + * + * @return string + * @throws InvalidArgumentException If the host state is not valid + */ + public static function getStateText($state, $translate = false) + { + switch ((int) $state) { + case self::STATE_UP: + $text = $translate ? mt('monitoring', 'up') : 'up'; + break; + case self::STATE_DOWN: + $text = $translate ? mt('monitoring', 'down') : 'down'; + break; + case self::STATE_UNREACHABLE: + $text = $translate ? mt('monitoring', 'unreachable') : 'unreachable'; + break; + case self::STATE_PENDING: + $text = $translate ? mt('monitoring', 'pending') : 'pending'; + break; + default: + throw new InvalidArgumentException('Invalid host state \'%s\'', $state); + break; + } + return $text; } } diff --git a/modules/monitoring/library/Monitoring/Object/HostList.php b/modules/monitoring/library/Monitoring/Object/HostList.php new file mode 100644 index 000000000..2d3f57495 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Object/HostList.php @@ -0,0 +1,27 @@ +backend->select()->from($this->dataViewName, $this->columns)->applyFilter($this->filter) + ->getQuery()->getSelectQuery()->query(); + foreach ($query as $row) { + /** @var object $row */ + $host = new Host($this->backend, $row->host_name); + $host->setProperties($row); + $hosts[] = $host; + } + return $hosts; + } +} diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 8ff79f7b8..3d3bde8dd 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -2,127 +2,270 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -/* -CREATE INDEX tgelf_comments ON icinga_comments (object_id, comment_type, comment_time); - -CREATE INDEX tgelf_scheduleddowntime ON icinga_scheduleddowntime (object_id, is_in_effect, scheduled_start_time); - -*/ - namespace Icinga\Module\Monitoring\Object; -use Icinga\Module\Monitoring\DataView\Contact; -use Icinga\Module\Monitoring\DataView\Contactgroup; -use Icinga\Module\Monitoring\DataView\Downtime; -use Icinga\Module\Monitoring\DataView\EventHistory; -use Icinga\Module\Monitoring\DataView\Hostgroup; -use Icinga\Module\Monitoring\DataView\Comment; -use Icinga\Module\Monitoring\DataView\Servicegroup; -use Icinga\Module\Monitoring\DataView\Customvar; -use Icinga\Web\UrlParams; +use InvalidArgumentException; use Icinga\Application\Config; +use Icinga\Exception\InvalidPropertyException; +use Icinga\Module\Monitoring\Backend; +use Icinga\Web\UrlParams; - +/** + * A monitored Icinga object, i.e. host or service + */ abstract class MonitoredObject { - public $type; - public $prefix; + /** + * Type host + */ + const TYPE_HOST = 'host'; - public $comments = array(); - public $downtimes = array(); - public $hostgroups = array(); - public $servicegroups = array(); - public $contacts = array(); - public $contactgroups = array(); - public $customvars = array(); - public $events = array(); + /** + * Type service + */ + const TYPE_SERVICE = 'service'; - protected $view; - private $properties = array(); - protected $params; + /** + * Backend to fetch object information from + * + * @var Backend + */ + protected $backend; - // TODO: Fetching parent states if any would be nice - // Same goes for host/service dependencies + /** + * Type of the Icinga object, i.e. 'host' or 'service' + * + * @var string + */ + protected $type; - public function __construct(UrlParams $params) + /** + * Prefix of the Icinga object, i.e. 'host_' or 'service_' + * + * @var string + */ + protected $prefix; + + /** + * Properties + * + * @var object + */ + protected $properties; + + /** + * Comments + * + * @var array + */ + protected $comments; + + /** + * Downtimes + * + * @var array + */ + protected $downtimes; + + /** + * Host groups + * + * @var array + */ + protected $hostgroups; + + /** + * Service groups + * + * @var array + */ + protected $servicegroups; + + /** + * Contacts + * + * @var array + */ + protected $contacts; + + /** + * Contact groups + * + * @var array + */ + protected $contactgroups; + + /** + * Custom variables + * + * @var array + */ + protected $customvars; + + /** + * Event history + * + * @var \Icinga\Module\Monitoring\DataView\EventHistory + */ + protected $eventhistory; + + /** + * Stats + * + * @var object + */ + protected $stats; + + /** + * Create a monitored object, i.e. host or service + * + * @param Backend $backend Backend to fetch object information from + */ + public function __construct(Backend $backend) { - $this->params = $params; - $this->properties = $this->getProperties(); + $this->backend = $backend; } - abstract protected function getProperties(); + /** + * Get the object's data view + * + * @return \Icinga\Module\Monitoring\DataView\DataView + */ + abstract protected function getDataView(); + /** + * Fetch the object's properties + * + * @return bool + */ + public function fetch() + { + $this->properties = $this->getDataView()->getQuery()->fetchRow(); + return $this->properties !== false; + } + + /** + * Get the type of the object + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Require the object's type to be one of the given types + * + * @param array $oneOf + * + * @return bool + * @throws InvalidArgumentException If the object's type is not one of the given types. + */ + public function assertOneOf(array $oneOf) + { + if (! in_array($this->type, $oneOf)) { + throw new InvalidArgumentException; + } + return true; + } + + /** + * Set the object's properties + * + * @param object $properties + * + * @return $this + */ + public function setProperties($properties) + { + $this->properties = (object) $properties; + return $this; + } + + /** + * Fetch the object's comments + * + * @return $this + */ public function fetchComments() { - // WTF??? - $query = Comment::fromParams(array('backend' => null), array( + $comments = $this->backend->select()->from('comment', array( 'id' => 'comment_internal_id', 'timestamp' => 'comment_timestamp', 'author' => 'comment_author', 'comment' => 'comment_data', 'type' => 'comment_type', - )); - $query->where('comment_type', array('comment', 'ack')); - $query->where('comment_objecttype', $this->type); - $query->where('comment_host', $this->host_name); - if ($this->type === 'service') { - $query->where('comment_service', $this->service_description); + )) + ->where('comment_type', array('comment', 'ack')) + ->where('comment_objecttype', $this->type) + ->where('comment_host', $this->host_name); + if ($this->type === self::TYPE_SERVICE) { + $comments->where('comment_service', $this->service_description); } - $this->comments = $query->getQuery()->fetchAll(); + $this->comments = $comments->getQuery()->fetchAll(); return $this; } + /** + * Fetch the object's downtimes + * + * @return $this + */ public function fetchDowntimes() { - // TODO: We want to check for objecttype = 'host', not type_id = 1 - - // WTF??? - $query = Downtime::fromParams(array('backend' => null), array( - 'id' => 'downtime_internal_id', - 'objecttype' => 'downtime_objecttype', - 'comment' => 'downtime_comment', - 'author' => 'downtime_author', - 'start' => 'downtime_start', - 'scheduled_start' => 'downtime_scheduled_start', - 'end' => 'downtime_end', - 'duration' => 'downtime_duration', - 'is_flexible' => 'downtime_is_flexible', - 'is_fixed' => 'downtime_is_fixed', - 'is_in_effect' => 'downtime_is_in_effect', - 'entry_time' => 'downtime_entry_time', - 'host' => 'downtime_host', - 'service' => 'downtime_service' - )); - - $query->where('downtime_objecttype', $this->type); - $query->where('downtime_host', $this->host_name); - if ($this->type === 'service') { - $query->where('downtime_service', $this->service_description); + $downtimes = $this->backend->select()->from('downtime', array( + 'id' => 'downtime_internal_id', + 'objecttype' => 'downtime_objecttype', + 'comment' => 'downtime_comment', + 'author' => 'downtime_author', + 'start' => 'downtime_start', + 'scheduled_start' => 'downtime_scheduled_start', + 'end' => 'downtime_end', + 'duration' => 'downtime_duration', + 'is_flexible' => 'downtime_is_flexible', + 'is_fixed' => 'downtime_is_fixed', + 'is_in_effect' => 'downtime_is_in_effect', + 'entry_time' => 'downtime_entry_time', + 'host' => 'downtime_host', + 'service' => 'downtime_service' + )) + ->where('downtime_objecttype', $this->type) + ->where('downtime_host', $this->host_name) + ->order('downtime_is_in_effect', 'DESC') + ->order('downtime_scheduled_start', 'ASC'); + if ($this->type === self::TYPE_SERVICE) { + $downtimes->where('downtime_service', $this->service_description); } - $query->order('downtime_is_in_effect', 'DESC')->order('downtime_scheduled_start', 'ASC'); - - $this->downtimes = $query->getQuery()->fetchAll(); - return $this; - - $this->downtimes = Downtime::fromRequest($this->request)->getQuery()->fetchAll(); + $this->downtimes = $downtimes->getQuery()->fetchAll(); return $this; } + /** + * Fetch the object's host groups + * + * @return $this + */ public function fetchHostgroups() { - $query = HostGroup::fromParams(array('backend' => null), array( + $hostGroups = $this->backend->select()->from('hostgroup', array( 'hostgroup_name', 'hostgroup_alias' - ))->where('host_name', $this->host_name); - - $this->hostgroups = $query->getQuery()->fetchPairs(); + )) + ->where('host_name', $this->host); + $this->hostgroups = $hostGroups->getQuery()->fetchPairs(); return $this; } + /** + * Fetch the object's custom variables + * + * @return $this + */ public function fetchCustomvars() { $blacklist = array(); - $blacklistPattern = '/^(.*pw.*|.*pass.*|community)$/'; + $blacklistPattern = '/^(.*pw.*|.*pass.*|community)$/i'; if ($security = Config::module('monitoring')->get('security')) { @@ -138,23 +281,20 @@ abstract class MonitoredObject $blacklistPattern = '/^(' . implode('|', $blacklist) . ')$/i'; } - $query = Customvar::fromParams(array('backend' => null), array( - 'varname', - 'varvalue' - ) - ); - - if ($this->type === 'host') { - $query->where('host_name', $this->host_name) - ->where('object_type', 'host'); - } else { - $query->where('host_name', $this->host_name) - ->where('object_type', 'service') - ->where('service_description', $this->service_description); + $query = $this->backend->select()->from('customvar', array( + 'varname', + 'varvalue' + )) + ->where('object_type', $this->type) + ->where('host_name', $this->host_name); + if ($this->type === self::TYPE_SERVICE) { + $query->where('service_description', $this->service_description); } + $this->customvars = array(); + $customvars = $query->getQuery()->fetchPairs(); - foreach ($customvars as $name => &$value) { + foreach ($customvars as $name => $value) { $name = ucwords(str_replace('_', ' ', strtolower($name))); if ($blacklistPattern && preg_match($blacklistPattern, $name)) { $value = '***'; @@ -165,83 +305,71 @@ abstract class MonitoredObject return $this; } + /** + * Fetch the object's contacts + * + * @return $this + */ public function fetchContacts() { -/* - $query = Contact::fromRequest( - $this->request, - array( + $contacts = $this->backend->select()->from('contact', array( 'contact_name', 'contact_alias', 'contact_email', 'contact_pager', - ) - )->getQuery() + )) ->where('host_name', $this->host_name); -*/ - - $query = Contact::fromParams(array('backend' => null), array( - 'contact_name', - 'contact_alias', - 'contact_email', - 'contact_pager', - )); - - if ($this->type === 'service') { - $query->where('service_host_name', $this->host_name); - $query->where('service_description', $this->service_description); - } else { - $query->where('host_name', $this->host_name); + if ($this->type === self::TYPE_SERVICE) { + $contacts->where('service_description', $this->service_description); } - - $this->contacts = $query->getQuery()->fetchAll(); + $this->contacts = $contacts->getQuery()->fetchAll(); return $this; } + /** + * Fetch the object's service groups + * + * @return $this + */ public function fetchServicegroups() { - $query = Servicegroup::fromParams(array('backend' => null), array( + $serviceGroups = $this->backend->select()->from('servicegroup', array( 'servicegroup_name', - 'servicegroup_alias', - ) - ); - $query->where('service_host_name', $this->host_name); - $query->where('service_description', $this->service_description); - $this->servicegroups = $query->getQuery()->fetchPairs(); + 'servicegroup_alias' + )) + ->where('service_host_name', $this->host_name) + ->where('service_description', $this->service_description); + $this->servicegroups = $serviceGroups->getQuery()->fetchPairs(); return $this; } + /** + * Fetch the object's contact groups + * + * @return $this + */ public function fetchContactgroups() { - - $query = Contactgroup::fromParams(array('backend' => null), array( + $contactsGroups = $this->backend->select()->from('contactgroup', array( 'contactgroup_name', 'contactgroup_alias' - )); - - if ($this->type === 'service') { - $query->where('service_host_name', $this->host_name); - $query->where('service_description', $this->service_description); - } else { - $query->where('host_name', $this->host_name); + )) + ->where('host_name', $this->host_name); + if ($this->type === self::TYPE_SERVICE) { + $contactsGroups->where('service_description', $this->service_description); } -/* - $query = Contactgroup::fromRequest( - $this->request, - array( - 'contactgroup_name', - 'contactgroup_alias' - ) - )->getQuery(); -*/ - $this->contactgroups = $query->getQuery()->fetchAll(); - + $this->contactgroups = $contactsGroups->getQuery()->fetchAll(); return $this; } - public function fetchEventHistory() + /** + * Fetch the object's event history + * + * @return $this + */ + public function fetchEventhistory() { - $query = EventHistory::fromParams(array('backend' => null), array( + $eventHistory = $this->backend->select()->from('eventHistory', array( 'object_type', 'host_name', 'service_description', @@ -251,42 +379,105 @@ abstract class MonitoredObject 'max_attempts', 'output', 'type' - ) - )->sort('timestamp', 'DESC'); - if ($this->type === 'service') { - $query->where('service_host_name', $this->host_name); - $query->where('service_description', $this->service_description); - } else { - $query->where('host_name', $this->host_name); + )) + ->order('timestamp', 'DESC') + ->where('host_name', $this->host_name); + if ($this->type === self::TYPE_SERVICE) { + $eventHistory->where('service_description', $this->service_description); } - - $this->eventhistory = $query->getQuery(); + $this->eventhistory = $eventHistory->getQuery(); return $this; } - public function __get($param) + /** + * Fetch stats + * + * @return $this + */ + public function fetchStats() { - - if (isset($this->properties->$param)) { - return $this->properties->$param; - } elseif (isset($this->$param)) { - return $this->$param; - } - if (substr($param, 0, strlen($this->prefix)) === $this->prefix) { - return false; - } - $expandedName = $this->prefix . strtolower($param); - return $this->$expandedName; + $this->stats = $this->backend->select()->from('statusSummary', array( + 'services_total', + 'services_ok', + 'services_problem', + 'services_problem_handled', + 'services_problem_unhandled', + 'services_critical', + 'services_critical_unhandled', + 'services_critical_handled', + 'services_warning', + 'services_warning_unhandled', + 'services_warning_handled', + 'services_unknown', + 'services_unknown_unhandled', + 'services_unknown_handled', + 'services_pending', + )) + ->where('service_host_name', $this->host_name) + ->getQuery() + ->fetchRow(); + return $this; } + /** + * Fetch all available data of the object + * + * @return $this + */ + public function populate() + { + $this + ->fetchComments() + ->fetchContacts() + ->fetchContactgroups() + ->fetchCustomvars() + ->fetchDowntimes(); + // Call fetchHostgroups or fetchServicegroups depending on the object's type + $fetchGroups = 'fetch' . ucfirst($this->type) . 'groups'; + $this->$fetchGroups(); + return $this; + } + + public function __get($name) + { + if (property_exists($this->properties, $name)) { + return $this->properties->$name; + } elseif (property_exists($this, $name) && $this->$name !== null) { + return $this->$name; + } elseif (property_exists($this, $name)) { + $fetchMethod = 'fetch' . ucfirst($name); + $this->$fetchMethod(); + return $this->$name; + } + if (substr($name, 0, strlen($this->prefix)) !== $this->prefix) { + $prefixedName = $this->prefix . strtolower($name); + if (property_exists($this->properties, $prefixedName)) { + return $this->properties->$prefixedName; + } + } + throw new InvalidPropertyException('Can\'t access property \'%s\'. Property does not exist.', $name); + } + + public function __isset($name) + { + if (property_exists($this->properties, $name)) { + return isset($this->properties->$name); + } elseif (property_exists($this, $name)) { + return isset($this->$name); + } + return false; + } + + /** + * @deprecated + */ public static function fromParams(UrlParams $params) { if ($params->has('service') && $params->has('host')) { - return new Service($params); + return new Service(Backend::createBackend(), $params->get('host'), $params->get('service')); } elseif ($params->has('host')) { - return new Host($params); + return new Host(Backend::createBackend(), $params->get('host')); } + return null; } - - abstract public function populate(); } diff --git a/modules/monitoring/library/Monitoring/Object/ObjectList.php b/modules/monitoring/library/Monitoring/Object/ObjectList.php new file mode 100644 index 000000000..1e5595d58 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Object/ObjectList.php @@ -0,0 +1,87 @@ +backend = $backend; + } + + public function setColumns(array $columns) + { + $this->columns = $columns; + return $this; + } + + public function getColumns() + { + return $this->columns; + } + + public function setFilter($filter) + { + $this->filter = $filter; + return $this; + } + + public function getFilter() + { + return $this->filter; + } + + abstract protected function fetchObjects(); + + public function fetch() + { + if ($this->objects === null) { + $this->objects = $this->fetchObjects(); + } + return $this->objects; + } + + public function count() + { + if ($this->count === null) { + $this->count = (int) $this->backend->select()->from($this->dataViewName)->applyFilter($this->filter) + ->getQuery()->count(); + } + return $this->count; + } + + public function getIterator() + { + if ($this->objects === null) { + $this->fetch(); + } + return new ArrayIterator($this->objects); + } + + /** + * Get the comments + * + * @return \Icinga\Module\Monitoring\DataView\Comment + */ + public function getComments() + { + return $this->backend->select()->from('comment')->applyFilter($this->filter); + } +} diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index c7b80f4a0..36a763101 100644 --- a/modules/monitoring/library/Monitoring/Object/Service.php +++ b/modules/monitoring/library/Monitoring/Object/Service.php @@ -4,33 +4,109 @@ namespace Icinga\Module\Monitoring\Object; -use Icinga\Module\Monitoring\DataView\ServiceStatus; -use Icinga\Data\Db\DbQuery; +use InvalidArgumentException; +use Icinga\Module\Monitoring\Backend; +/** + * A Icinga service + */ class Service extends MonitoredObject { - public $type = 'service'; + /** + * Service state 'OK' + */ + const STATE_OK = 0; + + /** + * Service state 'WARNING' + */ + const STATE_WARNING = 1; + + /** + * Service state 'CRITICAL' + */ + const STATE_CRITICAL = 2; + + /** + * Service state 'UNKNOWN' + */ + const STATE_UNKNOWN = 3; + + /** + * Service state 'PENDING' + */ + const STATE_PENDING = 99; + + /** + * Type of the Icinga service + * + * @var string + */ + public $type = self::TYPE_SERVICE; + + /** + * Prefix of the Icinga service + * + * @var string + */ public $prefix = 'service_'; - protected function applyObjectFilter(DbQuery $query) + /** + * Host the service is running on + * + * @var Host + */ + protected $host; + + /** + * Service name + * + * @var string + */ + protected $service; + + /** + * Create a new service + * + * @param Backend $backend Backend to fetch service information from + * @param string $host Host name the service is running on + * @param string $service Service name + */ + public function __construct(Backend $backend, $host, $service) { - return $query->where('service_host_name', $this->host_name) - ->where('service_description', $this->service_description); + parent::__construct($backend); + $this->host = new Host($backend, $host); + $this->service = $service; } - public function populate() + /** + * Get the host the service is running on + * + * @return Host + */ + public function getHost() { - $this->fetchComments() - ->fetchServicegroups() - ->fetchContacts() - ->fetchContactGroups() - ->fetchCustomvars() - ->fetchDowntimes(); + return $this->host; } - protected function getProperties() + /** + * Get the service name + * + * @return string + */ + public function getName() { - $this->view = ServiceStatus::fromParams(array('backend' => null), array( + return $this->service; + } + + /** + * Get the data view + * + * @return \Icinga\Module\Monitoring\DataView\ServiceStatus + */ + protected function getDataView() + { + return $this->backend->select()->from('serviceStatus', array( 'host_name', 'host_state', 'host_state_type', @@ -114,10 +190,44 @@ class Service extends MonitoredObject 'service_flap_detection_enabled', 'service_flap_detection_enabled_changed', 'service_modified_service_attributes', - 'process_perfdata' => 'service_process_performance_data', - ))->where('host_name', $this->params->get('host')) - ->where('service_description', $this->params->get('service')); + 'service_process_performance_data', + 'service_percent_state_change', + 'service_host_name' + )) + ->where('host_name', $this->host->getName()) + ->where('service_description', $this->service); + } - return $this->view->getQuery()->fetchRow(); + /** + * Get the translated textual representation of a service state + * + * @param int $state + * + * @return string + * @throws InvalidArgumentException If the service state is not valid + */ + public static function getStateText($state) + { + switch ((int) $state) { + case self::STATE_OK: + $text = mt('monitoring', 'ok'); + break; + case self::STATE_WARNING: + $text = mt('monitoring', 'warning'); + break; + case self::STATE_CRITICAL: + $text = mt('monitoring', 'critical'); + break; + case self::STATE_UNKNOWN: + $text = mt('monitoring', 'unknown'); + break; + case self::STATE_PENDING: + $text = mt('monitoring', 'pending'); + break; + default: + throw new InvalidArgumentException('Invalid service state \'%s\'', $state); + break; + } + return $text; } } diff --git a/modules/monitoring/library/Monitoring/Object/ServiceList.php b/modules/monitoring/library/Monitoring/Object/ServiceList.php new file mode 100644 index 000000000..858d04b5b --- /dev/null +++ b/modules/monitoring/library/Monitoring/Object/ServiceList.php @@ -0,0 +1,27 @@ +backend->select()->from($this->dataViewName, $this->columns)->applyFilter($this->filter) + ->getQuery()->getSelectQuery()->query(); + foreach ($query as $row) { + /** @var object $row */ + $service = new Service($this->backend, $row->host_name, $row->service_description); + $service->setProperties($row); + $services[] = $service; + } + return $services; + } +} diff --git a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php index be3820e19..3a795df32 100644 --- a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php +++ b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php @@ -11,7 +11,6 @@ use Icinga\Exception\IcingaException; use IteratorAggregate; use Icinga\Data\Filter\Filter; use Icinga\Web\Hook; -use Icinga\Web\Session; use Icinga\Web\Session\SessionNamespace; use Icinga\Module\Monitoring\DataView\DataView; diff --git a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php new file mode 100644 index 000000000..856496ca8 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php @@ -0,0 +1,228 @@ +view->tickets = Hook::first('ticket'); + } + if (Hook::has('grapher')) { + $this->view->grapher = Hook::first('grapher'); + } + } + + /** + * Show a host or service + */ + public function showAction() + { + $this->setAutorefreshInterval(10); + $checkNowForm = new CheckNowCommandForm(); + $checkNowForm + ->setObjects($this->object) + ->handleRequest(); + $this->view->checkNowForm = $checkNowForm; + if ( ! in_array((int) $this->object->state, array(0, 99))) { + if ((bool) $this->object->acknowledged) { + $removeAckForm = new RemoveAcknowledgementCommandForm(); + $removeAckForm + ->setObjects($this->object) + ->handleRequest(); + $this->view->removeAckForm = $removeAckForm; + } else { + $ackForm = new AcknowledgeProblemCommandForm(); + $ackForm + ->setObjects($this->object) + ->handleRequest(); + $this->view->ackForm = $ackForm; + } + } + if (count($this->object->comments) > 0) { + $delCommentForm = new DeleteCommentCommandForm(); + $delCommentForm + ->setObjects($this->object) + ->handleRequest(); + $this->view->delCommentForm = $delCommentForm; + } + if (count($this->object->downtimes > 0)) { + $delDowntimeForm = new DeleteDowntimeCommandForm(); + $delDowntimeForm + ->setObjects($this->object) + ->handleRequest(); + $this->view->delDowntimeForm = $delDowntimeForm; + } + $toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(); + $toggleFeaturesForm + ->load($this->object) + ->setObjects($this->object) + ->handleRequest(); + $this->view->toggleFeaturesForm = $toggleFeaturesForm; + $this->view->object = $this->object->populate(); + } + + /** + * Handle a command form + * + * @param ObjectsCommandForm $form + * + * @return ObjectsCommandForm + */ + protected function handleCommandForm(ObjectsCommandForm $form) + { + $form + ->setObjects($this->object) + ->setRedirectUrl(Url::fromPath($this->commandRedirectUrl)->setParams($this->params)) + ->handleRequest(); + $this->view->form = $form; + $this->_helper->viewRenderer('partials/command-form', null, true); + return $form; + } + + /** + * Acknowledge a problem + */ + abstract public function acknowledgeProblemAction(); + + /** + * Add a comment + */ + abstract public function addCommentAction(); + + /** + * Reschedule a check + */ + abstract public function rescheduleCheckAction(); + + /** + * Schedule a downtime + */ + abstract public function scheduleDowntimeAction(); + + /** + * Remove a comment + */ + public function removeCommentAction() + { + /* + * TODO(el): This is here because monitoring/list/comments has buttons to remove comments. Because of the nature + * of an action, the form is accessible via GET which does not make much sense because the form requires + * us to populate the ID of the comment which is to be deleted. We may introduce a combo box for choosing + * the comment ID on GET or deny GET access. + */ + $this->handleCommandForm(new DeleteCommentCommandForm()); + } + + /** + * Remove a downtime + */ + public function deleteDowntimeAction() + { + /* + * TODO(el): This is here because monitoring/list/downtimes has buttons to remove comments. Because of the + * nature of an action, the form is accessible via GET which does not make much sense because the form requires + * us to populate the ID of the downtime which is to be deleted. We may introduce a combo box for choosing + * the downtime ID on GET or deny GET access. + */ + $this->handleCommandForm(new DeleteDowntimeCommandForm()); + } + + /** + * Create tabs + */ + protected function createTabs() + { + $tabs = $this->getTabs(); + $object = $this->object; + if ($object->getType() === $object::TYPE_HOST) { + $params = array( + 'host' => $object->getName() + ); + } else { + $params = array( + 'host' => $object->getHost()->getName(), + 'service' => $object->getName() + ); + } + $tabs->add( + 'host', + array( + 'title' => 'Host', + 'icon' => 'img/icons/host.png', + 'url' => 'monitoring/host/show', + 'urlParams' => $params + ) + ); + if (isset($params['service'])) { + $tabs->add( + 'service', + array( + 'title' => 'Service', + 'icon' => 'img/icons/service.png', + 'url' => 'monitoring/service/show', + 'urlParams' => $params + ) + ); + } + $tabs->add( + 'services', + array( + 'title' => 'Services', + 'icon' => 'img/icons/service.png', + 'url' => 'monitoring/show/services', + 'urlParams' => $params + ) + ); + $tabs->add( + 'history', + array( + 'title' => 'History', + 'icon' => 'img/icons/history.png', + 'url' => 'monitoring/show/history', + 'urlParams' => $params + ) + ); + $tabs + ->extend(new OutputFormat()) + ->extend(new DashboardAction()); + } +} diff --git a/modules/monitoring/library/Monitoring/Web/Widget/TimelineIntervalBox.php b/modules/monitoring/library/Monitoring/Web/Widget/SelectBox.php similarity index 75% rename from modules/monitoring/library/Monitoring/Web/Widget/TimelineIntervalBox.php rename to modules/monitoring/library/Monitoring/Web/Widget/SelectBox.php index 307fffba8..c58540faf 100644 --- a/modules/monitoring/library/Monitoring/Web/Widget/TimelineIntervalBox.php +++ b/modules/monitoring/library/Monitoring/Web/Widget/SelectBox.php @@ -8,10 +8,7 @@ use Icinga\Web\Form; use Icinga\Web\Request; use Icinga\Web\Widget\AbstractWidget; -/** - * @todo Might be better if this is a generic selection widget. - */ -class TimelineIntervalBox extends AbstractWidget +class SelectBox extends AbstractWidget { /** * The name of the form that will be created @@ -27,6 +24,20 @@ class TimelineIntervalBox extends AbstractWidget */ private $values; + /** + * The label displayed next to the select box + * + * @var string + */ + private $label; + + /** + * The name of the url parameter to set + * + * @var string + */ + private $parameter; + /** * A request object used for initial form population * @@ -39,11 +50,15 @@ class TimelineIntervalBox extends AbstractWidget * * @param string $name The name of the form that will be created * @param array $values An array containing all intervals with their associated labels + * @param string $label The label displayed next to the select box + * @param string $param The request parameter name to set */ - public function __construct($name, array $values) + public function __construct($name, array $values, $label = 'Select', $param = 'selection') { $this->name = $name; $this->values = $values; + $this->label = $label; + $this->parameter = $param; } /** @@ -84,20 +99,20 @@ class TimelineIntervalBox extends AbstractWidget $form = new Form(); $form->setAttrib('class', 'inline'); $form->setMethod('GET'); + $form->setUidDisabled(); $form->setTokenDisabled(); $form->setName($this->name); $form->addElement( 'select', - 'interval', + $this->parameter, array( - 'label' => 'Timeline Interval', + 'label' => $this->label, 'multiOptions' => $this->values, - 'class' => 'autosubmit' + 'autosubmit' => true ) ); if ($this->request) { - $form->setAction($this->request->getRequestUri()); $form->populate($this->request->getParams()); } diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 4ee048529..7e734bb33 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -117,7 +117,7 @@ span.state.warning { span.state.handled.warning { background: @colorWarningHandled; -} +} span.state.unknown { background: @colorUnknown; @@ -130,3 +130,31 @@ span.state.handled.unknown { span.state.pending { background: @colorPending; } + +form.instance-features span.description, form.object-features span.description { + display: inline; +} + +.alertsummary-flex-container { + display: -ms-Flexbox; + -ms-box-orient: horizontal; + + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: flex; + + -webkit-flex-flow: row wrap; + -moz-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; +} + +.alertsummary-flex { + flex: 1 1 auto; + overflow: auto; + border: 1px #333 solid; + padding: 5px; + margin: 0 10px 0 0; + border-spacing: 10px 10px; +} diff --git a/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php b/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php deleted file mode 100644 index 32d8e35d3..000000000 --- a/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php +++ /dev/null @@ -1,111 +0,0 @@ -createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => 'Comment', - 'persistent' => '0', - 'expire' => '0', - 'sticky' => '0', - 'notify' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Legal request data without expire time must be considered valid' - ); - - $formWithExpireTime = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => 'Comment', - 'persistent' => '0', - 'expire' => '1', - 'expiretime' => '10/07/2013 5:32 PM', - 'sticky' => '0', - 'notify' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertTrue( - $formWithExpireTime->isSubmittedAndValid(), - 'Legal request data with expire time must be considered valid' - ); - } - - public function testFormInvalidWhenCommentMissing() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => '', - 'persistent' => '0', - 'expire' => '0', - 'sticky' => '0', - 'notify' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Missing comment must be considered not valid' - ); - } - - public function testFormInvalidWhenExpireTimeMissingAndExpireSet() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => 'Comment', - 'persistent' => '0', - 'expire' => '1', - 'sticky' => '0', - 'notify' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'If expire is set and expire time is missing, the form must not be valid' - ); - } - - public function testFormInvalidWhenExpireTimeIsIncorrectAndExpireSet() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => 'Comment', - 'persistent' => '0', - 'expire' => '1', - 'expiretime' => 'Not a date', - 'sticky' => '0', - 'notify' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'If expire is set and expire time is incorrect, the form must not be valid' - ); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php b/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php deleted file mode 100644 index 3228af399..000000000 --- a/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php +++ /dev/null @@ -1,64 +0,0 @@ -createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => 'Comment', - 'sticky' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Legal request data must be considered valid' - ); - } - - public function testFormInvalidWhenCommentMissing() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => '', - 'sticky' => '0', - 'btn_submit' => 'Submit' - - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Missing comment must be considered not valid' - ); - } - - public function testFormInvalidWhenAuthorMissing() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => '', - 'comment' => 'Comment', - 'sticky' => '0', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Missing author must be considered not valid' - ); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/CustomNotificationFormTest.php b/modules/monitoring/test/php/application/forms/Command/CustomNotificationFormTest.php deleted file mode 100644 index 9c03ff3ec..000000000 --- a/modules/monitoring/test/php/application/forms/Command/CustomNotificationFormTest.php +++ /dev/null @@ -1,28 +0,0 @@ -createForm( - self::FORM_CLASS, - array( - 'author' => 'Author', - 'comment' => '', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Missing comment must be considered not valid' - ); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/DelayNotificationFormTest.php b/modules/monitoring/test/php/application/forms/Command/DelayNotificationFormTest.php deleted file mode 100644 index 3a955f191..000000000 --- a/modules/monitoring/test/php/application/forms/Command/DelayNotificationFormTest.php +++ /dev/null @@ -1,58 +0,0 @@ -createForm( - self::FORM_CLASS, - array( - 'minutes' => '', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Missing notification delay must be considered invalid' - ); - } - - public function testFormInvalidWhenNotificationDelayNaN() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'minutes' => 'A String', - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Incorrect notification delay, i.e. NaN must be considered invalid' - ); - } - - public function testFormInvalidWhenNotificationDelayOverflows() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'minutes' => DelayNotificationForm::MAX_DELAY + 1, - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Notification delay bigger than constant "DelayNotificationForm::MAX_DELAY" must be considered invalid' - ); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/RescheduleNextCheckFormTest.php b/modules/monitoring/test/php/application/forms/Command/RescheduleNextCheckFormTest.php deleted file mode 100644 index 72a4914f5..000000000 --- a/modules/monitoring/test/php/application/forms/Command/RescheduleNextCheckFormTest.php +++ /dev/null @@ -1,54 +0,0 @@ -createForm( - self::FORM_CLASS, - array( - 'checktime' => '2013-24-12 17:30:00', - 'forcecheck' => 0, - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Asserting a logically incorrect checktime as invalid' - ); - - $form2 = $this->createForm( - self::FORM_CLASS, - array( - 'checktime' => 'Captain Morgan', - 'forcecheck' => 1, - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form2->isSubmittedAndValid(), - 'Providing arbitrary strings as checktime must be considered invalid' - ); - - $form3 = $this->createForm( - self::FORM_CLASS, - array( - 'checktime' => '', - 'forcecheck' => 0, - 'btn_submit' => 'Submit' - ) - ); - $this->assertFalse( - $form3->isSubmittedAndValid(), - 'Missing checktime must be considered invalid' - ); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/ScheduleDowntimeFormTest.php b/modules/monitoring/test/php/application/forms/Command/ScheduleDowntimeFormTest.php deleted file mode 100644 index 5c7138b17..000000000 --- a/modules/monitoring/test/php/application/forms/Command/ScheduleDowntimeFormTest.php +++ /dev/null @@ -1,356 +0,0 @@ -createForm(self::FORM_CLASS); - $formFixed->setCurrentDowntimes(array('foo')); - $formFixed->buildForm(); - $formFlexible = $this->createForm( - self::FORM_CLASS, - array( - 'type' => 'flexible' - ) - ); - $formFlexible->setCurrentDowntimes(array('foo')); - $formFlexible->buildForm(); - - $form = $this->createForm(self::FORM_CLASS); - $form->setCurrentDowntimes(array('foo')); - $form->setWithChildren(true); - $form->buildForm(); - } - - public function testCorrectValidationWithChildrend() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - 'btn_submit' => 'foo', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Asserting a correct fixed downtime form to be considered valid' - ); - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FLEXIBLE, - 'hours' => '10', - 'minutes' => '10', - 'btn_submit' => 'foo' - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Asserting a correct flexible downtime form to be considered valid' - ); - } - - public function testMissingFlexibleDurationRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FLEXIBLE, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert missing hours and minutes in downtime form to cause failing validation' - ); - } - - public function testMissingAuthorRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => '', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert missing author to cause validation errors in fixed downtime' - ); - } - - public function testMissingCommentRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => '', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert missing comment to cause validation errors in fixed downtime' - ); - } - - public function testInvalidTriggeredFieldValueRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => 'OK', - 'triggered' => 'HAHA', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert invalid trigger field to cause validation to fail' - ); - } - - public function testInvalidStartTimeRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => 'OK', - 'triggered' => '0', - 'starttime' => '17/07/2013', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert incorrect start time to cause validation errors in fixed downtime' - ); - } - - public function testInvalidEndTimeRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => 'OK', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => 'DING', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert invalid endtime to cause validation errors in fixed downtime' - ); - } - - public function testInvalidHoursValueRecognitionInFlexibleDowntime() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => 'OK', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FLEXIBLE, - 'hours' => '-1', - 'minutes' => '12', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert negative hours to cause validation errors in flexible downtime' - ); - } - - public function testInvalidMinutesValueRecognitionInFlexibleDowntime() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'OK', - 'comment' => 'OK', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FLEXIBLE, - 'hours' => '12', - 'minutes' => 'DING', - ) - ); - $form->setWithChildren(true); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert non numeric valud to cause validation errors in flexible downtime ' - ); - - } - - public function testCorrectScheduleDowntimeWithoutChildrenForm() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - 'btn_submit' => 'foo', - 'childobjects' => '0', - ) - ); - $form->setWithChildren(false); - $form->setCurrentDowntimes(array('foo')); - - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Assert a correct schedule downtime without children form to be considered valid' - ); - } - - public function testIncorrectChildObjectsRecognition() { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - 'childobjects' => 'AHA', - ) - ); - $form->setWithChildren(false); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert and incorrect (non-numeric) childobjects value to cause validation errors' - ); - - $form = $this->createForm( - self::FORM_CLASS, - array( - 'author' => 'TEST_AUTHOR', - 'comment' => 'DING DING', - 'triggered' => '0', - 'starttime' => '17/07/2013 10:30 AM', - 'endtime' => '18/07/2013 10:30 AM', - 'type' => ScheduleDowntimeForm::TYPE_FIXED, - 'hours' => '', - 'minutes' => '', - 'childobjects' => '4', - ) - ); - $form->setWithChildren(false); - $form->setCurrentDowntimes(array('foo')); - - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert and incorrect (numeric) childobjects value to cause validation errors' - ); - } - - public function testTimeRange() - { - $form = $this->createForm(self::FORM_CLASS); - $form->setCurrentDowntimes(array('foo')); - $form->buildForm(); - - $time1 = $form->getElement('starttime')->getValue(); - $time2 = $form->getElement('endtime')->getValue(); - - $this->assertEquals(3600, ($time2 - $time1)); - } -} diff --git a/modules/monitoring/test/php/application/forms/Command/SubmitPassiveCheckResultTest.php b/modules/monitoring/test/php/application/forms/Command/SubmitPassiveCheckResultTest.php deleted file mode 100644 index 4c83294df..000000000 --- a/modules/monitoring/test/php/application/forms/Command/SubmitPassiveCheckResultTest.php +++ /dev/null @@ -1,117 +0,0 @@ -createForm(self::FORM_CLASS, array()); - - $form->setType(SubmitPassiveCheckResultForm::TYPE_SERVICE); - $options = $form->getOptions(); - $this->assertCount(4, $options, 'Assert correct number of states in service passive checks form'); - $this->assertEquals('OK', $options[0], 'Assert OK state to be available in service passive check form'); - $this->assertEquals( - 'WARNING', - $options[1], - 'Assert WARNING state to be available in service passive check form' - ); - $this->assertEquals( - 'CRITICAL', - $options[2], - 'Assert CRITICAL state to be available in service passive check form' - ); - $this->assertEquals( - 'UNKNOWN', - $options[3], - 'Assert UNKNOWN state to be available in service passive check form' - ); - $form->setType(SubmitPassiveCheckResultForm::TYPE_HOST); - $options = $form->getOptions(); - $this->assertCount(3, $options, 'Assert correct number of states in host passive checks form'); - $this->assertEquals('UP', $options[0], 'Assert UP state to be available in host passive check form'); - $this->assertEquals('DOWN', $options[1], 'Assert DOWN state to be available in host passive check form'); - $this->assertEquals( - 'UNREACHABLE', - $options[2], - 'Assert UNREACHABLE state to be available in host passive check form' - ); - } - - /** - * @expectedException Icinga\Exception\ProgrammingError - * @expectedExceptionMessage Type is not valid - */ - public function testMissingTypeThrowingException() - { - $form = $this->createForm(self::FORM_CLASS, array()); - $form->buildForm(); - } - - public function testCorrectFormCreation() - { - $form = $this->createForm(self::FORM_CLASS, array()); - $form->setType(SubmitPassiveCheckResultForm::TYPE_SERVICE); - $form->buildForm(); - } - - public function testCorrectServicePassiveCheckSubmission() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'pluginstate' => 0, - 'checkoutput' => 'DING', - 'performancedata' => '', - 'btn_submit' => 'foo' - ) - ); - $form->setType(SubmitPassiveCheckResultForm::TYPE_SERVICE); - $this->assertTrue( - $form->isSubmittedAndValid(), - 'Assert a correct passive service check form to pass form validation' - ); - } - - public function testIncorrectCheckoutputRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'pluginstate' => 0, - 'checkoutput' => '', - 'performancedata' => '' - ) - ); - $form->setType(SubmitPassiveCheckResultForm::TYPE_SERVICE); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert empty checkoutput to cause validation errors in passive service check ' - ); - } - - public function testIncorrectStateRecognition() - { - $form = $this->createForm( - self::FORM_CLASS, - array( - 'pluginstate' => 'LA', - 'checkoutput' => 'DING', - 'performancedata' => '' - ) - ); - $form->setType(SubmitPassiveCheckResultForm::TYPE_SERVICE); - $this->assertFalse( - $form->isSubmittedAndValid(), - 'Assert invalid (non-numeric) state to cause validation errors in passive service check' - ); - } -} diff --git a/modules/translation/doc/translation.md b/modules/translation/doc/translation.md index 74cd54004..55dc415ad 100644 --- a/modules/translation/doc/translation.md +++ b/modules/translation/doc/translation.md @@ -42,6 +42,40 @@ The same works also for views: If you need to provide placeholders in your messages, you should wrap the `$this->translate()` with `sprintf()` for e.g. sprintf($this->translate('Hello User: (%s)'), $user->getName()) +## Translating plural forms + +To provide a plural translation, just use the `translatePlural()` function. + +```php +view->message = $this->translatePlural('Service', 'Services', 3); + } +} +``` + +## Context based translation + +If you want to provide context based translations, you can easily do it with an extra parameter in both methods +`translate()` and `translatePlural()`. + +```php +view->title = $this->translate('My Titile', 'mycontext'); + $this->view->message = $this->translatePlural('Service', 'Services', 3, 'mycontext'); + } +} +``` + # Translation for Translators Icinga Web 2 internally uses the UNIX standard gettext tool to perform internationalization, this means translation diff --git a/modules/translation/library/Translation/Util/GettextTranslationHelper.php b/modules/translation/library/Translation/Util/GettextTranslationHelper.php index 9d72b9c77..5702def85 100644 --- a/modules/translation/library/Translation/Util/GettextTranslationHelper.php +++ b/modules/translation/library/Translation/Util/GettextTranslationHelper.php @@ -237,8 +237,16 @@ class GettextTranslationHelper '/usr/bin/xgettext', '--language=PHP', '--keyword=translate', + '--keyword=translate:1,2c', + '--keyword=translatePlural:1,2', + '--keyword=translatePlural:1,2,4c', '--keyword=mt:2', + '--keyword=mtp:2,3', + '--keyword=mtp:2,3,5c', '--keyword=t', + '--keyword=t:1,2c', + '--keyword=tp:1,2', + '--keyword=tp:1,2,4c', '--sort-output', '--force-po', '--omit-header', @@ -332,6 +340,7 @@ class GettextTranslationHelper '"MIME-Version: 1.0\n"', '"Content-Type: text/plain; charset=' . $headerInfo['charset'] . '\n"', '"Content-Transfer-Encoding: 8bit\n"', + '"Plural-Forms: nplurals=2; plural=(n != 1);\n"', '' ) ) . PHP_EOL . substr($content, strpos($content, '#: ')) diff --git a/packages/rpm/README.md b/packages/rpm/README.md index d218a51eb..5dd1006c5 100644 --- a/packages/rpm/README.md +++ b/packages/rpm/README.md @@ -42,8 +42,8 @@ Decide whether to use MySQL or PostgreSQL. FLUSH PRIVILEGES; quit - mysql -u root -p icingaweb < /usr/share/doc/icingaweb2-*/schema/accounts.mysql.sql - mysql -u root -p icingaweb < /usr/share/doc/icingaweb2-*/schema/preferences.mysql.sql + mysql -u root -p icingaweb < /usr/share/doc/icingaweb2*/schema/accounts.mysql.sql + mysql -u root -p icingaweb < /usr/share/doc/icingaweb2*/schema/preferences.mysql.sql ### PostgreSQL @@ -62,8 +62,8 @@ in `/var/lib/pgsql/data/pg_hba.conf` and restart the PostgreSQL server. Now install the `icingaweb` schema - bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2-*/schema/accounts.pgsql.sql - bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2-*/schema/preferences.pgsql.sql + bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2*/schema/accounts.pgsql.sql + bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2*/schema/preferences.pgsql.sql ## Configuration @@ -74,16 +74,16 @@ The monitoring module is enabled by default. ### Backend configuration -`/etc/icingaweb/resources.ini` contains the database backend information. +`/etc/icingaweb2/resources.ini` contains the database backend information. By default the Icinga 2 DB IDO is used by the monitoring module in -`/etc/icingaweb/modules/monitoring/backends.ini` +`/etc/icingaweb2/modules/monitoring/backends.ini` The external command pipe is required for sending commands and configured for Icinga 2 in -`/etc/icingaweb/modules/monitoring/instances.ini` +`/etc/icingaweb2/modules/monitoring/instances.ini` ### Authentication configuration -The `/etc/icingaweb/authentication.ini` file uses the internal database as +The `/etc/icingaweb2/authentication.ini` file uses the internal database as default. This requires the database being installed properly before allowing users to login via web console. diff --git a/packages/rpm/etc/httpd/conf.d/icingaweb.conf b/packages/rpm/etc/httpd/conf.d/icingaweb.conf index cf2fd82a4..1db9f20f2 100644 --- a/packages/rpm/etc/httpd/conf.d/icingaweb.conf +++ b/packages/rpm/etc/httpd/conf.d/icingaweb.conf @@ -1,6 +1,6 @@ -Alias /icingaweb "/usr/share/icingaweb/public" +Alias /icingaweb "/usr/share/icingaweb2/public" - + Options SymLinksIfOwnerMatch AllowOverride None @@ -17,7 +17,7 @@ Alias /icingaweb "/usr/share/icingaweb/public" Allow from all - SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb + SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb2 EnableSendfile Off diff --git a/packages/rpm/etc/icingaweb/authentication.ini b/packages/rpm/etc/icingaweb2/authentication.ini similarity index 100% rename from packages/rpm/etc/icingaweb/authentication.ini rename to packages/rpm/etc/icingaweb2/authentication.ini diff --git a/packages/rpm/etc/icingaweb/modules/monitoring/backends.ini b/packages/rpm/etc/icingaweb2/modules/monitoring/backends.ini similarity index 100% rename from packages/rpm/etc/icingaweb/modules/monitoring/backends.ini rename to packages/rpm/etc/icingaweb2/modules/monitoring/backends.ini diff --git a/packages/rpm/etc/icingaweb/modules/monitoring/instances.ini b/packages/rpm/etc/icingaweb2/modules/monitoring/instances.ini similarity index 100% rename from packages/rpm/etc/icingaweb/modules/monitoring/instances.ini rename to packages/rpm/etc/icingaweb2/modules/monitoring/instances.ini diff --git a/packages/rpm/etc/icingaweb/resources.ini b/packages/rpm/etc/icingaweb2/resources.ini similarity index 100% rename from packages/rpm/etc/icingaweb/resources.ini rename to packages/rpm/etc/icingaweb2/resources.ini diff --git a/packages/rpm/usr/bin/icingacli b/packages/rpm/usr/bin/icingacli index 03ed43116..d6c4010b6 100755 --- a/packages/rpm/usr/bin/icingacli +++ b/packages/rpm/usr/bin/icingacli @@ -2,5 +2,5 @@ dispatch(); diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 870d89494..b201df7c6 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -17,11 +17,7 @@ td.configTable { min-width: 300px; } -div.config-form-group { - float: right; -} - -.form-group { +.form-element { margin-bottom: 1em; } @@ -101,7 +97,7 @@ form.inline { display: inline; } -button.link-like, input.link-like { +form.link-like input[type="submit"] { color: @colorLinkDefault; font-weight: normal; border: none; @@ -111,7 +107,7 @@ button.link-like, input.link-like { cursor: pointer; } -button.link-like:hover, input.link-like:hover, input.link-like:focus { +form.link-like input[type="submit"]:hover, form.link-like input[type="submit"]:focus { text-decoration: underline; background: none; color: @colorLinkDefault; @@ -144,4 +140,39 @@ form ul.errors li { font-size: 0.8em; } +.description { + display: block; +} +textarea { + height: 4em; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + width: 100%; +} + +input, select, textarea { + display: inline; +} + +.control-group { + display: -webkit-flex; + display: flex; + -webkit-flex-flow: row wrap; + flex-flow: row wrap; +} + +.control-group > div { + width: 100%; + height: 100%; + overflow: auto; +} + +@media (min-width: 480px) { + .control-group > div { + width: auto; + } +} diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 6319e65d1..be8a8703f 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -331,3 +331,21 @@ html { font-weight: bold; color: white; } + +.hbox { + display: inline-block; +} + +.hbox-item { + display: inline-block; + vertical-align: top; + margin-bottom: 0.25em; + margin-left: 1em; + margin-right: 1em; +} + +.hbox-spacer { + display: inline-block; + vertical-align: top; + width: 2em; +} diff --git a/public/css/icinga/tabs.less b/public/css/icinga/tabs.less index ed846fc9d..74e3e0b72 100644 --- a/public/css/icinga/tabs.less +++ b/public/css/icinga/tabs.less @@ -136,3 +136,6 @@ ul.tabs img.icon { margin-top: -4px; } +a.close-tab { + display: none; +} \ No newline at end of file diff --git a/public/css/icinga/widgets.less b/public/css/icinga/widgets.less index f546443c4..9fe42c899 100644 --- a/public/css/icinga/widgets.less +++ b/public/css/icinga/widgets.less @@ -19,7 +19,7 @@ table.historycolorgrid td { margin: 1em; } -table.historycolorgrid td.hover { +table.historycolorgrid td:hover { opacity: 0.5; } diff --git a/public/js/icinga/behavior/form.js b/public/js/icinga/behavior/form.js new file mode 100644 index 000000000..d289183d6 --- /dev/null +++ b/public/js/icinga/behavior/form.js @@ -0,0 +1,104 @@ +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +/** + * Controls behavior of form elements, depending reload and + */ +(function(Icinga, $) { + + "use strict"; + + Icinga.Behaviors = Icinga.Behaviors || {}; + + var Form = function (icinga) { + Icinga.EventListener.call(this, icinga); + this.on('keyup change', 'form input', this.onChange, this); + + // store the modification state of all input fields + this.inputs = {}; + }; + Form.prototype = new Icinga.EventListener(); + + /** + * @param evt + */ + Form.prototype.onChange = function (evt) { + var el = evt.target; + var form = evt.data.self.uniqueFormName($(el).closest('form')[0] || {}); + evt.data.self.inputs[form] = evt.data.self.inputs[form] || {}; + if (el.value !== '') { + evt.data.self.inputs[form][el.name] = true; + } else { + evt.data.self.inputs[form][el.name] = false; + } + }; + + /** + * Try to generate an unique form name using the action + * and the name of the given form element + * + * @param form {HTMLFormElement} The + * @returns {String} The unique name + */ + Form.prototype.uniqueFormName = function(form) + { + return (form.name || 'undefined') + '.' + (form.action || 'undefined'); + }; + + /** + * Mutates the HTML before it is placed in the DOM after a reload + * + * @param content {String} The content to be rendered + * @param $container {jQuery} The target container where the html will be rendered in + * @param action {String} The action-url that caused the reload + * @param autorefresh {Boolean} Whether the rendering is due to an autoRefresh + * + * @returns {string|NULL} The content to be rendered, or NULL, when nothing should be changed + */ + Form.prototype.renderHook = function(content, $container, action, autorefresh) { + var origFocus = document.activeElement; + var containerId = $container.attr('id'); + var icinga = this.icinga; + var self = this.icinga.behaviors.form; + var changed = false; + $container.find('form').each(function () { + var form = self.uniqueFormName(this); + if (autorefresh) { + // check if an element in this container was changed + $(this).find('input').each(function () { + var name = this.name; + if (self.inputs[form] && self.inputs[form][name]) { + icinga.logger.debug( + 'form input: ' + form + '.' + name + ' was changed and aborts reload...' + ); + changed = true; + } + }); + } else { + // user-triggered reload, forget all changes to forms in this container + self.inputs[form] = null; + } + }); + if (changed) { + return null; + } + if ( + // is the focus among the elements to be replaced? + $container.has(origFocus).length && + // is an autorefresh + autorefresh && + + // and has focus + $(origFocus).length && + !$(origFocus).hasClass('autofocus') && + $(origFocus).closest('form').length + ) { + icinga.logger.debug('Not changing content for ' + containerId + ' form has focus'); + return null; + } + return content; + }; + + Icinga.Behaviors.Form = Form; + +}) (Icinga, jQuery); diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index 0adf1b36b..615e5ac08 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -10,14 +10,24 @@ Icinga.Behaviors = Icinga.Behaviors || {}; var Navigation = function (icinga) { - this.icinga = icinga; + Icinga.EventListener.call(this, icinga); + this.on('click', '#menu a', this.linkClicked, this); + this.on('click', '#menu tr[href]', this.linkClicked, this); + this.on('mouseenter', 'li.dropdown', this.dropdownHover, this); + this.on('mouseleave', 'li.dropdown', this.dropdownLeave, this); + this.on('mouseenter', '#menu > ul > li', this.menuTitleHovered, this); + this.on('mouseleave', '#sidebar', this.leaveSidebar, this); + this.on('rendered', this.onRendered); }; + Navigation.prototype = new Icinga.EventListener(); - Navigation.prototype.apply = function(el) { - // restore old menu state + Navigation.prototype.onRendered = function(evt) { + // get original source element of the rendered-event + var el = evt.target; if (activeMenuId) { + // restore old menu state $('[role="navigation"] li.active', el).removeClass('active'); - var $selectedMenu = $('#' + activeMenuId, el).addClass('active'); + var $selectedMenu = $('#' + activeMenuId).addClass('active'); var $outerMenu = $selectedMenu.parent().closest('li'); if ($outerMenu.size()) { $outerMenu.addClass('active'); @@ -27,28 +37,13 @@ var $menus = $('[role="navigation"] li.active', el); if ($menus.size()) { activeMenuId = $menus[0].id; + $menus.find('li.active').first().each(function () { + activeMenuId = this.id; + }); } } }; - Navigation.prototype.bind = function() { - $(document).on('click', '#menu a', { self: this }, this.linkClicked); - $(document).on('click', '#menu tr[href]', { self: this }, this.linkClicked); - $(document).on('mouseenter', 'li.dropdown', this.dropdownHover); - $(document).on('mouseleave', 'li.dropdown', {self: this}, this.dropdownLeave); - $(document).on('mouseenter', '#menu > ul > li', { self: this }, this.menuTitleHovered); - $(document).on('mouseleave', '#sidebar', { self: this }, this.leaveSidebar); - }; - - Navigation.prototype.unbind = function() { - $(document).off('click', '#menu a', this.linkClicked); - $(document).off('click', '#menu tr[href]', this.linkClicked); - $(document).off('mouseenter', 'li.dropdown', this.dropdownHover); - $(document).off('mouseleave', 'li.dropdown', this.dropdownLeave); - $(document).off('mouseenter', '#menu > ul > li', this.menuTitleHovered); - $(document).on('mouseleave', '#sidebar', this.leaveSidebar); - }; - Navigation.prototype.linkClicked = function(event) { var $a = $(this); var href = $a.attr('href'); @@ -82,6 +77,29 @@ $menu.data('icinga-url', menuDataUrl); }; + Navigation.prototype.setActiveByUrl = function(url) + { + this.resetActive(); + this.setActive($('#menu [href="' + url + '"]')); + } + + /** + * Change the active menu element + * + * @param $el {jQuery} A selector pointing to the active element + */ + Navigation.prototype.setActive = function($el) { + + $el.closest('li').addClass('active'); + $el.parents('li').addClass('active'); + activeMenuId = $el.closest('li').attr('id'); + }; + + Navigation.prototype.resetActive = function() { + $('#menu .active').removeClass('active'); + activeMenuId = null; + }; + Navigation.prototype.menuTitleHovered = function(event) { var $li = $(this), delay = 800, diff --git a/public/js/icinga/behavior/sparkline.js b/public/js/icinga/behavior/sparkline.js index 69a16527e..33cc0d34b 100644 --- a/public/js/icinga/behavior/sparkline.js +++ b/public/js/icinga/behavior/sparkline.js @@ -8,10 +8,14 @@ Icinga.Behaviors = Icinga.Behaviors || {}; var Sparkline = function (icinga) { - this.icinga = icinga; + Icinga.EventListener.call(this, icinga); + this.on('rendered', this.onRendered, this); }; + Sparkline.prototype = new Icinga.EventListener(); + + Sparkline.prototype.onRendered = function(evt) { + var el = evt.target; - Sparkline.prototype.apply = function(el) { $('span.sparkline', el).each(function(i, element) { // read custom options var $spark = $(element); @@ -45,12 +49,6 @@ }); }; - Sparkline.prototype.bind = function() { - }; - - Sparkline.prototype.unbind = function() { - }; - Icinga.Behaviors.Sparkline = Sparkline; }) (Icinga, jQuery); diff --git a/public/js/icinga/behavior/tooltip.js b/public/js/icinga/behavior/tooltip.js index 9cf207d7f..de5b66d05 100644 --- a/public/js/icinga/behavior/tooltip.js +++ b/public/js/icinga/behavior/tooltip.js @@ -8,15 +8,23 @@ Icinga.Behaviors = Icinga.Behaviors || {}; var Tooltip = function (icinga) { - this.icinga = icinga; + Icinga.EventListener.call(this, icinga); this.mouseX = 0; this.mouseY = 0; + this.on('mousemove', this.onMousemove, this); + this.on('rendered', this.onRendered, this); + }; + Tooltip.prototype = new Icinga.EventListener(); + + Tooltip.prototype.onMousemove = function(event) { + event.data.self.mouseX = event.pageX; + event.data.self.mouseY = event.pageY; }; - Tooltip.prototype.apply = function(el) { - var self = this, icinga = this.icinga; + Tooltip.prototype.onRendered = function(evt) { + var self = evt.data.self, icinga = evt.data.icinga, el = evt.target; - $('[title]').each(function () { + $('[title]', el).each(function () { var $el = $(this); $el.attr('title', $el.data('title-rich') || $el.attr('title')); }); @@ -49,18 +57,6 @@ }); }; - Tooltip.prototype.bind = function() { - var self = this; - $(document).on('mousemove', function (event) { - self.mouseX = event.pageX; - self.mouseY = event.pageY; - }); - }; - - Tooltip.prototype.unbind = function() { - $(document).off('mousemove'); - }; - // Export Icinga.Behaviors.Tooltip = Tooltip; diff --git a/public/js/icinga/behavior/tristate.js b/public/js/icinga/behavior/tristate.js index c828d6cd7..036c25cea 100644 --- a/public/js/icinga/behavior/tristate.js +++ b/public/js/icinga/behavior/tristate.js @@ -8,21 +8,10 @@ Icinga.Behaviors = Icinga.Behaviors || {}; var Tristate = function (icinga) { - this.icinga = icinga; - }; - - Tristate.prototype.apply = function(el) { - var self = this, icinga = this.icinga; - }; - - Tristate.prototype.bind = function() { - // Toggle all triStateButtons - $(document).on('click', 'div.tristate .tristate-dummy', { self: this }, this.clickTriState); - }; - - Tristate.prototype.unbind = function() { - $(document).off('click', 'div.tristate .tristate-dummy', this.clickTriState); + Icinga.EventListener.call(this, icinga); + this.on('click', 'div.tristate .tristate-dummy', this.clickTriState, this); }; + Tristate.prototype = new Icinga.EventListener(); Tristate.prototype.clickTriState = function (event) { var self = event.data.self; diff --git a/public/js/icinga/eventlistener.js b/public/js/icinga/eventlistener.js new file mode 100644 index 000000000..e1946c6ae --- /dev/null +++ b/public/js/icinga/eventlistener.js @@ -0,0 +1,74 @@ +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +/** + * EventListener contains event handlers and can bind / and unbind them from + * event emitting objects + */ +(function(Icinga, $) { + + "use strict"; + + var EventListener = function (icinga) { + this.icinga = icinga; + this.handlers = []; + }; + + /** + * Add an handler to this EventLister + * + * @param evt {String} The name of the triggering event + * @param cond {String} The filter condition + * @param fn {Function} The event handler to execute + * @param scope {Object} The optional 'this' of the called function + */ + EventListener.prototype.on = function(evt, cond, fn, scope) { + if (typeof cond === 'function') { + scope = fn; + fn = cond; + cond = 'body'; + } + this.icinga.logger.debug('on: ' + evt + '(' + cond + ')'); + this.handlers.push({ evt: evt, cond: cond, fn: fn, scope: scope }); + }; + + /** + * Bind all listeners to the given event emitter + * + * All event handlers will be executed when the associated event is + * triggered on the given Emitter. + * + * @param emitter {String} An event emitter that supports the function + * 'on' to register listeners + */ + EventListener.prototype.bind = function (emitter) { + var self = this; + $.each(this.handlers, function(i, handler) { + self.icinga.logger.debug('bind: ' + handler.evt + '(' + handler.cond + ')'); + emitter.on( + handler.evt, handler.cond, + { + self: handler.scope || emitter, + icinga: self.icinga + }, handler.fn + ); + }); + }; + + /** + * Unbind all listeners from the given event emitter + * + * @param emitter {String} An event emitter that supports the function + * 'off' to un-register listeners. + */ + EventListener.prototype.unbind = function (emitter) { + var self = this; + $.each(this.handlers, function(i, handler) { + self.icinga.logger.debug('unbind: ' + handler.evt + '(' + handler.cond + ')'); + emitter.off(handler.evt, handler.cond, handler.fn); + }); + }; + + Icinga.EventListener = EventListener; + +}) (Icinga, jQuery); diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index a10502a82..5f62a8c8a 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -29,20 +29,17 @@ */ initialize: function () { this.applyGlobalDefaults(); - this.applyHandlers($('#layout')); + $('#layout').trigger('rendered'); + //$('.container').trigger('rendered'); $('.container').each(function(idx, el) { - icinga.events.applyHandlers($(el)); icinga.ui.initializeControls($(el)); }); }, // TODO: What's this? - applyHandlers: function (el) { - $.each(this.icinga.behaviors, function (name, behavior) { - behavior.apply(el); - }); - - var icinga = this.icinga; + applyHandlers: function (evt) { + var el = $(evt.target), self = evt.data.self; + var icinga = self.icinga; $('.dashboard > div', el).each(function(idx, el) { var url = $(el).data('icingaUrl'); @@ -78,12 +75,19 @@ } }); - $('input.autofocus', el).focus(); - + if (document.activeElement === document.body) { + $('input.autofocus', el).focus(); + } var searchField = $('#menu input.search', el); // Remember initial search field value if any if (searchField.length && searchField.val().length) { - this.searchValue = searchField.val(); + self.searchValue = searchField.val(); + } + + if (icinga.ui.isOneColLayout()) { + icinga.ui.disableCloseButtons(); + } else { + icinga.ui.enableCloseButtons(); } }, @@ -92,9 +96,12 @@ */ applyGlobalDefaults: function () { $.each(self.icinga.behaviors, function (name, behavior) { - behavior.bind(); + behavior.bind($(document)); }); + // Apply element-specific behavior whenever the layout is rendered + $(document).on('rendered', { self: this }, this.applyHandlers); + // We catch resize events $(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize); @@ -145,7 +152,7 @@ }, onLoad: function (event) { - $('.container').trigger('rendered'); + //$('.container').trigger('rendered'); }, onUnload: function (event) { @@ -370,6 +377,18 @@ // If link has hash tag... if (href.match(/#/)) { if (href === '#') { + if ($a.hasClass('close-toggle')) { + if (! icinga.ui.isOneColLayout()) { + var $cont = $a.closest('.container').first(); + if ($cont.attr('id') === 'col1') { + icinga.ui.moveToLeft(); + icinga.ui.layout1col(); + } else { + icinga.ui.layout1col(); + } + icinga.history.pushCurrentState(); + } + } return false; } $target = self.getLinkTargetFor($a); @@ -462,7 +481,7 @@ unbindGlobalHandlers: function () { $.each(self.icinga.behaviors, function (name, behavior) { - behavior.unbind(); + behavior.unbind($(document)); }); $(window).off('resize', this.onWindowResize); $(window).off('load', this.onLoad); diff --git a/public/js/icinga/history.js b/public/js/icinga/history.js index a3fcf5deb..9f05def2d 100644 --- a/public/js/icinga/history.js +++ b/public/js/icinga/history.js @@ -90,6 +90,7 @@ } }); + // TODO: update navigation // Did we find any URL? Then push it! if (url !== '') { window.history.pushState({icinga: true}, null, this.cleanupUrl(url)); diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 9d14ebb7f..dca88c394 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -340,12 +340,12 @@ if (! req.autorefresh) { // TODO: Hook for response/url? var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); - var $matches = $.merge($('[href="' + url + '"]'), $forms); - $matches.each(function (idx, el) { if ($(el).closest('#menu').length) { - $('#menu .active').removeClass('active'); + if (req.$target[0].id === 'col1') { + self.icinga.behaviors.navigation.resetActive(); + } } else if ($(el).closest('table.action').length) { $(el).closest('table.action').find('.active').removeClass('active'); } @@ -357,8 +357,9 @@ if ($el.is('form')) { $('input', $el).addClass('active'); } else { - $el.closest('li').addClass('active'); - $el.parents('li').addClass('active'); + if (req.$target[0].id === 'col1') { + self.icinga.behaviors.navigation.setActive($el); + } } // Interrupt .each, only on menu item shall be active return false; @@ -368,7 +369,6 @@ }); } else { // TODO: next container url - // Get first container url? active = $('[href].active', req.$target).attr('href'); } @@ -540,7 +540,6 @@ }).addClass('active'); } } - req.$target.trigger('rendered'); }, /** @@ -663,6 +662,7 @@ // Container update happens here var scrollPos = false; var self = this; + var origFocus = document.activeElement; var containerId = $container.attr('id'); if (typeof containerId !== 'undefined') { if (autorefresh) { @@ -672,13 +672,18 @@ } } - var origFocus = document.activeElement; - if ( - // Do not reload menu when search field has content - (containerId === 'menu' && $(origFocus).length && $(origFocus).val().length) - // TODO: remove once #7146 is solved - || (containerId !== 'menu' && typeof containerId !== 'undefined' && autorefresh && origFocus && $(origFocus).closest('form').length && $container.has($(origFocus)) && $(origFocus).closest('#' + containerId).length && ! $(origFocus).hasClass('autosubmit'))) { - this.icinga.logger.debug('Not changing content for ', containerId, ' form has focus'); + var discard = false; + $.each(self.icinga.behaviors, function(name, behavior) { + if (behavior.renderHook) { + var changed = behavior.renderHook(content, $container, action, autorefresh); + if (!changed) { + discard = true; + } else { + content = changed; + } + } + }); + if (discard) { return; } @@ -718,16 +723,18 @@ } this.icinga.ui.assignUniqueContainerIds(); - if (scrollPos !== false) { - $container.scrollTop(scrollPos); - } if (origFocus) { $(origFocus).focus(); } // TODO: this.icinga.events.refreshContainer(container); + $container.trigger('rendered'); + + if (scrollPos !== false) { + $container.scrollTop(scrollPos); + } var icinga = this.icinga; - icinga.events.applyHandlers($container); + //icinga.events.applyHandlers($container); icinga.ui.initializeControls($container); icinga.ui.fixControls(); diff --git a/public/js/icinga/logger.js b/public/js/icinga/logger.js index 1bdc2d718..07135e007 100644 --- a/public/js/icinga/logger.js +++ b/public/js/icinga/logger.js @@ -37,7 +37,7 @@ /** * Raise or lower current log level * - * Messages blow this threshold will be silently discarded + * Messages below this threshold will be silently discarded */ setLevel: function (level) { if ('undefined' !== typeof this.numericLevel(level)) { diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index ef5e43f70..ac98fb14c 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -136,6 +136,7 @@ var kill = this.cutContainer($('#col1')); this.pasteContainer($('#col1'), col2); this.fixControls(); + this.icinga.behaviors.navigation.setActiveByUrl($('#col1').data('icingaUrl')); }, cutContainer: function ($col) { @@ -199,6 +200,11 @@ self.refreshDebug(); }, + /** + * Returns whether the layout is too small for more than one column + * + * @returns {boolean} True when more than one column is available + */ hasOnlyOneColumn: function () { return this.currentLayout === 'poor' || this.currentLayout === 'minimal'; }, @@ -229,11 +235,21 @@ return false; }, + /** + * Returns whether only one column is displayed + * + * @returns {boolean} True when only one column is displayed + */ + isOneColLayout: function () { + return ! $('#layout').hasClass('twocols'); + }, + layout1col: function () { - if (! $('#layout').hasClass('twocols')) { return; } + if (this.isOneColLayout()) { return; } this.icinga.logger.debug('Switching to single col'); $('#layout').removeClass('twocols'); this.closeContainer($('#col2')); + this.disableCloseButtons(); }, closeContainer: function($c) { @@ -247,10 +263,11 @@ }, layout2col: function () { - if ($('#layout').hasClass('twocols')) { return; } + if (! this.isOneColLayout()) { return; } this.icinga.logger.debug('Switching to double col'); $('#layout').addClass('twocols'); this.fixControls(); + this.enableCloseButtons(); }, getAvailableColumnSpace: function () { @@ -543,25 +560,39 @@ refreshTimeSince: function () { $('.timesince').each(function (idx, el) { - var m = el.innerHTML.match(/^(.*?)(-?\d+)m\s(-?\d+)s/); + + // todo remove after replace timeSince + var mp = el.innerHTML.match(/^(.*?)(-?\d+)d\s(-?\d+)h/); + if (mp !== null) { + return true; + } + + var m = el.innerHTML.match(/^(.*?)(-?\d+)(.+\s)(-?\d+)(.+)/); if (m !== null) { var nm = parseInt(m[2]); - var ns = parseInt(m[3]); + var ns = parseInt(m[4]); if (ns < 59) { ns++; } else { ns = 0; nm++; } - $(el).html(m[1] + nm + 'm ' + ns + 's'); + $(el).html(m[1] + nm + m[3] + ns + m[5]); } }); $('.timeuntil').each(function (idx, el) { - var m = el.innerHTML.match(/^(.*?)(-?\d+)m\s(-?\d+)s/); + + // todo remove after replace timeUntil + var mp = el.innerHTML.match(/^(.*?)(-?\d+)d\s(-?\d+)h/); + if (mp !== null) { + return true; + } + + var m = el.innerHTML.match(/^(.*?)(-?\d+)(.+\s)(-?\d+)(.+)/); if (m !== null) { var nm = parseInt(m[2]); - var ns = parseInt(m[3]); + var ns = parseInt(m[4]); var signed = ''; var sec = 0; @@ -589,7 +620,7 @@ nm = Math.floor(sec/60); ns = sec - nm * 60; - $(el).html(m[1] + signed + nm + 'm ' + ns + 's'); + $(el).html(m[1] + signed + nm + m[3] + ns + m[5]); } }); }, @@ -684,6 +715,14 @@ this.fixControls(parent); }, + disableCloseButtons: function () { + $('a.close-toggle').hide(); + }, + + enableCloseButtons: function () { + $('a.close-toggle').show(); + }, + fixControls: function ($parent) { var self = this; diff --git a/test/php/application/forms/Config/Authentication/BaseBackendFormTest.php b/test/php/application/forms/Config/Authentication/BaseBackendFormTest.php deleted file mode 100644 index ca32a1dae..000000000 --- a/test/php/application/forms/Config/Authentication/BaseBackendFormTest.php +++ /dev/null @@ -1,61 +0,0 @@ -is_valid; - } -} - -class BaseBackendFormTest extends BaseTestCase -{ - public function testIsForceCreationCheckboxBeingAdded() - { - $form = new BackendForm(); - $form->is_valid = false; - - $this->assertFalse($form->isValid(array())); - $this->assertNotNull( - $form->getElement('backend_force_creation'), - 'Checkbox to force a backend\'s creation is not being added though the backend is invalid' - ); - } - - public function testIsForceCreationCheckboxNotBeingAdded() - { - $form = new BackendForm(); - $form->is_valid = true; - - $this->assertTrue($form->isValid(array())); - $this->assertNull( - $form->getElement('backend_force_creation'), - 'Checkbox to force a backend\'s creation is being added though the backend is valid' - ); - } - - public function testIsTheFormValidIfForceCreationTrue() - { - $form = new BackendForm(); - $form->is_valid = false; - - $this->assertTrue( - $form->isValid(array('backend_force_creation' => 1)), - 'BaseBackendForm with invalid backend is not valid though force creation is set' - ); - } -} diff --git a/test/php/application/forms/Config/Authentication/DbBackendFormTest.php b/test/php/application/forms/Config/Authentication/DbBackendFormTest.php index 51bec896f..3364dff60 100644 --- a/test/php/application/forms/Config/Authentication/DbBackendFormTest.php +++ b/test/php/application/forms/Config/Authentication/DbBackendFormTest.php @@ -32,13 +32,12 @@ class DbBackendFormTest extends BaseTestCase ->andReturn(2); $form = new DbBackendForm(); - $form->setBackendName('test'); - $form->setResources(array('test_db_backend' => null)); - $form->create(); - $form->populate(array('backend_test_resource' => 'test_db_backend')); + $form->setTokenDisabled(); + $form->setResources(array('test_db_backend')); + $form->populate(array('resource' => 'test_db_backend')); $this->assertTrue( - $form->isValidAuthenticationBackend(), + $form->isValidAuthenticationBackend($form), 'DbBackendForm claims that a valid authentication backend with users is not valid' ); } @@ -55,13 +54,12 @@ class DbBackendFormTest extends BaseTestCase ->andReturn(0); $form = new DbBackendForm(); - $form->setBackendName('test'); - $form->setResources(array('test_db_backend' => null)); - $form->create(); - $form->populate(array('backend_test_resource' => 'test_db_backend')); + $form->setTokenDisabled(); + $form->setResources(array('test_db_backend')); + $form->populate(array('resource' => 'test_db_backend')); $this->assertFalse( - $form->isValidAuthenticationBackend(), + $form->isValidAuthenticationBackend($form), 'DbBackendForm claims that an invalid authentication backend without users is valid' ); } diff --git a/test/php/application/forms/Config/Authentication/LdapBackendFormTest.php b/test/php/application/forms/Config/Authentication/LdapBackendFormTest.php index 209b7a675..0335d1f82 100644 --- a/test/php/application/forms/Config/Authentication/LdapBackendFormTest.php +++ b/test/php/application/forms/Config/Authentication/LdapBackendFormTest.php @@ -29,16 +29,15 @@ class LdapBackendFormTest extends BaseTestCase { $this->setUpResourceFactoryMock(); Mockery::mock('overload:Icinga\Authentication\Backend\LdapUserBackend') - ->shouldReceive('assertAuthenticationPossible')->andReturn(null); + ->shouldReceive('assertAuthenticationPossible')->andReturnNull(); $form = new LdapBackendForm(); - $form->setBackendName('test'); - $form->setResources(array('test_ldap_backend' => null)); - $form->create(); - $form->populate(array('backend_test_resource' => 'test_ldap_backend')); + $form->setTokenDisabled(); + $form->setResources(array('test_ldap_backend')); + $form->populate(array('resource' => 'test_ldap_backend')); $this->assertTrue( - $form->isValidAuthenticationBackend(), + $form->isValidAuthenticationBackend($form), 'LdapBackendForm claims that a valid authentication backend with users is not valid' ); } @@ -54,13 +53,12 @@ class LdapBackendFormTest extends BaseTestCase ->shouldReceive('assertAuthenticationPossible')->andThrow(new AuthenticationException); $form = new LdapBackendForm(); - $form->setBackendName('test'); - $form->setResources(array('test_ldap_backend' => null)); - $form->create(); - $form->populate(array('backend_test_resource' => 'test_ldap_backend')); + $form->setTokenDisabled(); + $form->setResources(array('test_ldap_backend')); + $form->populate(array('resource' => 'test_ldap_backend')); $this->assertFalse( - $form->isValidAuthenticationBackend(), + $form->isValidAuthenticationBackend($form), 'LdapBackendForm claims that an invalid authentication backend without users is valid' ); } diff --git a/test/php/application/forms/Config/Authentication/ReorderFormTest.php b/test/php/application/forms/Config/Authentication/ReorderFormTest.php deleted file mode 100644 index 808806f1a..000000000 --- a/test/php/application/forms/Config/Authentication/ReorderFormTest.php +++ /dev/null @@ -1,80 +0,0 @@ - $this->order); - } -} - -class ReorderFormTest extends BaseTestCase -{ - public function setUp() - { - parent::setUp(); - $this->viewMock = Mockery::mock('\Zend_View'); - $this->viewMock->shouldReceive('icon')->andReturn(''); - } - - public function testMoveBackendUp() - { - $config = new Zend_Config( - array( - 'test1' => '', - 'test2' => '', - 'test3' => '' - ) - ); - $oldOrder = array_keys($config->toArray()); - - $form = new RequestLessReorderForm(); - $form->setCurrentOrder($oldOrder); - $form->setBackendName('test3'); - $form->setView($this->viewMock); - $form->create(); - - $form->order = $form->getSubForm('btn_reorder_up')->getElement('form_backend_order')->getValue(); - $this->assertSame( - $form->getReorderedConfig($config), - array('test1' => '', 'test3' => '', 'test2' => ''), - 'Moving elements up with ReorderForm does not seem to properly work' - ); - } - - public function testMoveBackendDown() - { - $config = new Zend_Config( - array( - 'test1' => '', - 'test2' => '', - 'test3' => '' - ) - ); - $oldOrder = array_keys($config->toArray()); - - $form = new RequestLessReorderForm(); - $form->setCurrentOrder($oldOrder); - $form->setBackendName('test1'); - $form->setView($this->viewMock); - $form->create(); - - $form->order = $form->getSubForm('btn_reorder_down')->getElement('form_backend_order')->getValue(); - $this->assertSame( - $form->getReorderedConfig($config), - array('test2' => '', 'test1' => '', 'test3' => ''), - 'Moving elements down with ReorderForm does not seem to properly work' - ); - } -} diff --git a/test/php/application/forms/Config/AuthenticationBackendReorderFormTest.php b/test/php/application/forms/Config/AuthenticationBackendReorderFormTest.php new file mode 100644 index 000000000..c79b12fd9 --- /dev/null +++ b/test/php/application/forms/Config/AuthenticationBackendReorderFormTest.php @@ -0,0 +1,61 @@ +config; + return false; + } +} + +class AuthenticationBackendReorderFormProvidingConfigFormWithoutSave extends AuthenticationBackendReorderForm +{ + public function getConfigForm() + { + $form = new AuthenticationBackendConfigFormWithoutSave(); + $form->setIniConfig($this->config); + return $form; + } +} + +class AuthenticationBackendReorderFormTest extends BaseTestCase +{ + public function testMoveBackend() + { + $config = new Config( + array( + 'test1' => '', + 'test2' => '', + 'test3' => '' + ) + ); + + $this->getRequestMock()->shouldReceive('getMethod')->andReturn('POST') + ->shouldReceive('isPost')->andReturn(true) + ->shouldReceive('getPost')->andReturn(array('backend_newpos' => 'test3|1')); + + $form = new AuthenticationBackendReorderFormProvidingConfigFormWithoutSave(); + $form->setIniConfig($config); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->handleRequest(); + + $this->assertEquals( + array('test1', 'test3', 'test2'), + AuthenticationBackendConfigFormWithoutSave::$newConfig->keys(), + 'Moving elements with AuthenticationBackendReorderForm does not seem to properly work' + ); + } +} diff --git a/test/php/application/forms/Config/Resource/DbResourceFormTest.php b/test/php/application/forms/Config/Resource/DbResourceFormTest.php new file mode 100644 index 000000000..1b624c49f --- /dev/null +++ b/test/php/application/forms/Config/Resource/DbResourceFormTest.php @@ -0,0 +1,66 @@ +setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('getConnection')->atMost()->twice()->andReturn(Mockery::self())->getMock() + ); + + $form = new DbResourceForm(); + + $this->assertTrue( + $form->isValidResource($form), + 'ResourceForm claims that a valid db resource is not valid' + ); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testInvalidDbResourceIsNotValid() + { + $this->setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('getConnection')->once()->andThrow('\Exception')->getMock() + ); + + $form = new DbResourceForm(); + + $this->assertFalse( + $form->isValidResource($form), + 'ResourceForm claims that an invalid db resource is valid' + ); + } + + protected function setUpResourceFactoryMock($resourceMock) + { + Mockery::mock('alias:Icinga\Data\ResourceFactory') + ->shouldReceive('createResource') + ->with(Mockery::type('\Zend_Config')) + ->andReturn($resourceMock); + } +} diff --git a/test/php/application/forms/Config/Resource/LdapResourceFormTest.php b/test/php/application/forms/Config/Resource/LdapResourceFormTest.php new file mode 100644 index 000000000..078a11146 --- /dev/null +++ b/test/php/application/forms/Config/Resource/LdapResourceFormTest.php @@ -0,0 +1,66 @@ +setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('connect')->getMock() + ); + + $form = new LdapResourceForm(); + + $this->assertTrue( + $form->isValidResource($form), + 'ResourceForm claims that a valid ldap resource is not valid' + ); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testInvalidLdapResourceIsNotValid() + { + $this->setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('connect')->once()->andThrow('\Exception')->getMock() + ); + + $form = new LdapResourceForm(); + + $this->assertFalse( + $form->isValidResource($form), + 'ResourceForm claims that an invalid ldap resource is valid' + ); + } + + protected function setUpResourceFactoryMock($resourceMock) + { + Mockery::mock('alias:Icinga\Data\ResourceFactory') + ->shouldReceive('createResource') + ->with(Mockery::type('\Zend_Config')) + ->andReturn($resourceMock); + } +} diff --git a/test/php/application/forms/Config/Resource/LivestatusResourceFormTest.php b/test/php/application/forms/Config/Resource/LivestatusResourceFormTest.php new file mode 100644 index 000000000..300adebed --- /dev/null +++ b/test/php/application/forms/Config/Resource/LivestatusResourceFormTest.php @@ -0,0 +1,67 @@ +setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('connect')->andReturn(Mockery::self()) + ->shouldReceive('disconnect')->getMock() + ); + + $form = new LivestatusResourceForm(); + + $this->assertTrue( + $form->isValidResource($form), + 'ResourceForm claims that a valid livestatus resource is not valid' + ); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testInvalidLivestatusResourceIsNotValid() + { + $this->setUpResourceFactoryMock( + Mockery::mock()->shouldReceive('connect')->once()->andThrow('\Exception')->getMock() + ); + + $form = new LivestatusResourceForm(); + + $this->assertFalse( + $form->isValidResource($form), + 'ResourceForm claims that an invalid livestatus resource is valid' + ); + } + + protected function setUpResourceFactoryMock($resourceMock) + { + Mockery::mock('alias:Icinga\Data\ResourceFactory') + ->shouldReceive('createResource') + ->with(Mockery::type('\Zend_Config')) + ->andReturn($resourceMock); + } +} diff --git a/test/php/application/forms/Config/Resource/ResourceFormTestBroken.php b/test/php/application/forms/Config/Resource/ResourceFormTestBroken.php deleted file mode 100644 index 6dba6ce54..000000000 --- a/test/php/application/forms/Config/Resource/ResourceFormTestBroken.php +++ /dev/null @@ -1,268 +0,0 @@ -is_valid; - } -} - -class ResourceFormTest extends BaseTestCase -{ - public function tearDown() - { - parent::tearDown(); - Mockery::close(); // Necessary because some tests run in a separate process - } - - public function testIsForceCreationCheckboxBeingAdded() - { - $form = new TestResourceForm(); - $form->is_valid = false; - - $this->assertFalse($form->isValid(array())); - $this->assertNotNull( - $form->getElement('resource_force_creation'), - 'Checkbox to force the creation of a resource is not being added though the resource is invalid' - ); - } - - public function testIsForceCreationCheckboxNotBeingAdded() - { - $form = new TestResourceForm(); - $form->is_valid = true; - - $this->assertTrue($form->isValid(array())); - $this->assertNull( - $form->getElement('resource_force_creation'), - 'Checkbox to force the creation of a resource is being added though the resource is valid' - ); - } - - public function testIsTheFormValidIfForceCreationTrue() - { - $form = new TestResourceForm(); - $form->is_valid = false; - - $this->assertTrue( - $form->isValid(array('resource_force_creation' => 1)), - 'ResourceForm with invalid resource is not valid though force creation is set' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testValidDbResourceIsValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('getConnection')->atMost()->twice()->andReturn(Mockery::self())->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'db'))); - - $this->assertTrue( - $form->isValidResource(), - 'ResourceForm claims that a valid db resource is not valid' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testInvalidDbResourceIsNotValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('getConnection')->once()->andThrow('\Exception')->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'db'))); - - $this->assertFalse( - $form->isValidResource(), - 'ResourceForm claims that an invalid db resource is valid' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testValidLdapResourceIsValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('connect')->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'ldap'))); - - $this->assertTrue( - $form->isValidResource(), - 'ResourceForm claims that a valid ldap resource is not valid' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testInvalidLdapResourceIsNotValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('connect')->once()->andThrow('\Exception')->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'ldap'))); - - $this->assertFalse( - $form->isValidResource(), - 'ResourceForm claims that an invalid ldap resource is valid' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testValidLivestatusResourceIsValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('connect')->andReturn(Mockery::self()) - ->shouldReceive('disconnect')->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'livestatus'))); - - $this->assertTrue( - $form->isValidResource(), - 'ResourceForm claims that a valid livestatus resource is not valid' - ); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testInvalidLivestatusResourceIsNotValid() - { - $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('connect')->once()->andThrow('\Exception')->getMock() - ); - $form = $this->buildResourceForm(new Zend_Config(array('type' => 'livestatus'))); - - $this->assertFalse( - $form->isValidResource(), - 'ResourceForm claims that an invalid livestatus resource is valid' - ); - } - - public function testValidFileResourceIsValid() - { - $form = $this->buildResourceForm( - new Zend_Config( - array( - 'type' => 'file', - 'filename' => BaseTestCase::$testDir . '/res/status/icinga.status.dat' - ) - ) - ); - - $this->assertTrue( - $form->isValidResource(), - 'ResourceForm claims that a valid file resource is not valid' - ); - } - - public function testInvalidFileResourceIsNotValid() - { - $form = $this->buildResourceForm( - new Zend_Config( - array( - 'type' => 'file', - 'filename' => 'not_existing' - ) - ) - ); - - $this->assertFalse( - $form->isValidResource(), - 'ResourceForm claims that an invalid file resource is valid' - ); - } - - public function testValidStatusdatResourceIsValid() - { - $form = $this->buildResourceForm( - new Zend_Config( - array( - 'type' => 'statusdat', - 'status_file' => BaseTestCase::$testDir . '/res/status/icinga.status.dat', - 'object_file' => BaseTestCase::$testDir . '/res/status/icinga.objects.cache', - ) - ) - ); - - $this->assertTrue( - $form->isValidResource(), - 'ResourceForm claims that a valid statusdat resource is not valid' - ); - } - - public function testInvalidStatusdatResourceIsNotValid() - { - $form = $this->buildResourceForm( - new Zend_Config( - array( - 'type' => 'statusdat', - 'status_file' => 'not_existing', - 'object_file' => 'not_existing' - ) - ) - ); - - $this->assertFalse( - $form->isValidResource(), - 'ResourceForm claims that an invalid statusdat resource is valid' - ); - } - - protected function buildResourceForm($resourceConfig) - { - $form = new ResourceForm(); - $form->setRequest($this->getRequestMock()); - $form->setResource($resourceConfig); - $form->create(); - - return $form; - } - - protected function getRequestMock() - { - return Mockery::mock('\Zend_Controller_Request_Abstract') - ->shouldReceive('getParam') - ->with(Mockery::type('string'), Mockery::type('string')) - ->andReturnUsing(function ($name, $default) { return $default; }) - ->getMock(); - } - - protected function setUpResourceFactoryMock($resourceMock) - { - Mockery::mock('alias:Icinga\Data\ResourceFactory') - ->shouldReceive('createResource') - ->with(Mockery::type('\Zend_Config')) - ->andReturn($resourceMock); - } -} diff --git a/test/php/library/Icinga/Data/Filter/FilterTest.php b/test/php/library/Icinga/Data/Filter/FilterTest.php index dbd640b91..7d6349213 100644 --- a/test/php/library/Icinga/Data/Filter/FilterTest.php +++ b/test/php/library/Icinga/Data/Filter/FilterTest.php @@ -199,6 +199,18 @@ class FilterTest extends BaseTestCase $this->assertNotEquals((string) $c, (string) $d); } + public function testLeadingAndTrailingWhitespacesSanitizing() + { + $columnHasWhitespaces = Filter::where(' host ', 'localhost'); + $expressionHasWhitespaces = Filter::where('host', ' localhost '); + $bothHaveWhitespaces = Filter::fromQueryString(' host = localhost '); + $withArray = Filter::where(' host ', array(' no match ', ' localhost ')); + $this->assertTrue($columnHasWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($expressionHasWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($bothHaveWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($withArray->matches($this->sampleData[0])); + } + private function row($idx) { return $this->sampleData[$idx]; diff --git a/test/php/library/Icinga/Logger/Writer/StreamWriterTest.php b/test/php/library/Icinga/Logger/Writer/StreamWriterTest.php index f20191418..11fec9740 100644 --- a/test/php/library/Icinga/Logger/Writer/StreamWriterTest.php +++ b/test/php/library/Icinga/Logger/Writer/StreamWriterTest.php @@ -9,7 +9,7 @@ use Icinga\Logger\Logger; use Icinga\Test\BaseTestCase; use Icinga\Logger\Writer\FileWriter; -class LoggerTest extends BaseTestCase +class StreamWriterTest extends BaseTestCase { public function setUp() { @@ -27,7 +27,7 @@ class LoggerTest extends BaseTestCase public function testWhetherStreamWriterCreatesMissingFiles() { - new FileWriter(new Zend_Config(array('target' => $this->target))); + new FileWriter(new Zend_Config(array('file' => $this->target))); $this->assertFileExists($this->target, 'StreamWriter does not create missing files on initialization'); } @@ -36,8 +36,8 @@ class LoggerTest extends BaseTestCase */ public function testWhetherStreamWriterWritesMessages() { - $writer = new FileWriter(new Zend_Config(array('target' => $this->target))); - $writer->log(Logger::$ERROR, 'This is a test error'); + $writer = new FileWriter(new Zend_Config(array('file' => $this->target))); + $writer->log(Logger::ERROR, 'This is a test error'); $log = file_get_contents($this->target); $this->assertContains('This is a test error', $log, 'StreamWriter does not write log messages'); } diff --git a/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeTest.php b/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeTest.php deleted file mode 100644 index 7d3fd94f6..000000000 --- a/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeTest.php +++ /dev/null @@ -1,697 +0,0 @@ -markTestSkipped('Cannot find "' . EXTCMD_TEST_BIN . '"'); - } - } - - /** - * Return the path of the test pipe used in these tests - * - * @return string - */ - public function getPipeName() - { - return sys_get_temp_dir() . '/icinga_test_pipe'; - } - - /** - * Return a @see Icinga\Protocal\CommandPipe\CommandPipe instance set up for the local test pipe - * - * @return CommandPipe - */ - private function getLocalTestPipe() - { - $tmpPipe = $this->getPipeName(); - $this->cleanup(); - touch($tmpPipe); - - $cfg = new Zend_Config( - array( - 'path' => $tmpPipe, - 'name' => 'test' - ) - ); - - return new CommandPipe($cfg); - } - - /** - * Return a @see Icinga\Protocal\CommandPipe\CommandPipe instance set up - * for the local test pipe, but with ssh as the transport layer - * - * @return CommandPipe - */ - private function getSSHTestPipe() - { - $tmpPipe = $this->getPipeName(); - $this->cleanup(); - touch($tmpPipe); - - $cfg = new Zend_Config( - array( - 'path' => $tmpPipe, - 'user' => 'vagrant', - 'password' => 'vagrant', - 'host' => 'localhost', - 'port' => 22, - 'name' => 'test' - ) - ); - - return new CommandPipe($cfg); - } - - /** - * Remove the testpipe if it exists - */ - private function cleanup() { - if (file_exists($this->getPipeName())) { - unlink($this->getPipeName()); - } - } - - /** - * Query the extcmd_test script with $command or the command pipe and test whether the result is $exceptedString and - * has a return code of 0. - * - * Note: - * - if no string is given, only the return code is tested - * - if no command is given, the content of the test commandpipe is used - * - * This helps testing whether commandpipe serialization works correctly - * - * @param bool $expectedString The string that is expected to be returned from the extcmd_test binary - * (optional, leave it to just test for the return code) - * @param bool $command The commandstring to send (optional, leave it for using the command pipe content) - */ - private function assertCommandSucceeded($expectedString = false, $command = false) { - $resultCode = null; - $resultArr = array(); - $receivedCmd = exec(EXTCMD_TEST_BIN . ' ' . escapeshellarg( - $command ? $command : file_get_contents($this->getPipeName())), - $resultArr, - $resultCode - ); - $this->assertEquals(0, $resultCode, 'Submit of external command returned error : ' . $receivedCmd); - if ($expectedString) { - $this->assertEquals( - $expectedString, - $receivedCmd, - 'Asserting that the command icinga received matches the command we send' - ); - } - } - - /** - * Test whether a single host acknowledgment is serialized and send correctly - * - * @throws Exception - */ - public function testAcknowledgeSingleHost() - { - $pipe = $this->getLocalTestPipe(); - try { - $ack = new AcknowledgeCommand(new Comment('I can', 'sends teh ack')); - $pipe->sendCommand( - $ack, - array( - (object) array( - 'host_name' => 'hostA' - ) - ) - ); - $this->assertCommandSucceeded('ACKNOWLEDGE_HOST_PROBLEM;hostA;0;0;0;I can;sends teh ack'); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether multiple host and service acknowledgments are serialized and send correctly - * - * @throws Exception - */ - public function testAcknowledgeMultipleObjects() - { - $pipe = $this->getLocalTestPipe(); - try { - $ack = new AcknowledgeCommand(new Comment('I can', 'sends teh ack')); - $pipe->getTransport()->setOpenMode('a'); - $pipe->sendCommand( - $ack, - array( - (object) array( - 'host_name' => 'hostA' - ),(object) array( - 'host_name' => 'hostB' - ),(object) array( - 'host_name' => 'hostC' - ),(object) array( - 'host_name' => 'hostC', - 'service_description' => 'svc' - ) - ) - ); - - $result = explode("\n", file_get_contents($this->getPipeName())); - $this->assertCount(5, $result, 'Asserting the correct number of commands being written to the command pipe'); - - $this->assertCommandSucceeded('ACKNOWLEDGE_HOST_PROBLEM;hostA;0;0;0;I can;sends teh ack', $result[0]); - $this->assertCommandSucceeded('ACKNOWLEDGE_HOST_PROBLEM;hostB;0;0;0;I can;sends teh ack', $result[1]); - $this->assertCommandSucceeded('ACKNOWLEDGE_HOST_PROBLEM;hostC;0;0;0;I can;sends teh ack', $result[2]); - $this->assertCommandSucceeded('ACKNOWLEDGE_SVC_PROBLEM;hostC;svc;0;0;0;I can;sends teh ack', $result[3]); - - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether a single host comment is correctly serialized and send to the command pipe - * - * @throws Exception - */ - public function testAddHostComment() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->sendCommand( - new AddCommentCommand( - new Comment( - 'Autor', - 'Comment' - ) - ), - array( - (object) array( - 'host_name' => 'hostA' - ) - ) - ); - $this->assertCommandSucceeded('ADD_HOST_COMMENT;hostA;0;Autor;Comment'); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether removing all hostcomments is correctly serialized and send to the command pipe - * - * @throws Exception - */ - public function testRemoveAllHostComment() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->removeComment( - array( - (object) array( - 'host_name' => 'test' - ) - ) - ); - $this->assertCommandSucceeded('DEL_ALL_HOST_COMMENTS;test'); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether removing a single host comment is correctly serialized and send to the command pipe - * - * @throws Exception - */ - public function testRemoveSpecificComment() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->removeComment( - array( - (object) array( - 'comment_id' => 34, - 'host_name' => 'test' - ) - ) - ); - $this->assertCommandSucceeded('DEL_HOST_COMMENT;34'); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether a multiple reschedules for services and hosts are correctly serialized and send to the commandpipe - * - * @throws Exception - */ - public function testScheduleChecks() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->getTransport()->setOpenMode('a'); // append so we have multiple results - $command = new ScheduleCheckCommand(5000); - $pipe->sendCommand( - $command, - array( - (object) array( - 'host_name' => 'test' - ), - (object) array( - 'host_name' => 'test', - 'service_description' => 'svc1' - ) - ) - ); - $command->setForced(true); - $pipe->sendCommand( - $command, - array( - (object) array( - 'host_name' => 'test' - ), - (object) array( - 'host_name' => 'test', - 'service_description' => 'svc1' - ) - ) - ); - $command->excludeHost(); - $pipe->sendCommand( - $command, - array( - (object) array( - 'host_name' => 'test' - ) - ) - ); - - $result = explode("\n", file_get_contents($this->getPipeName())); - $this->assertCount(6, $result, 'Asserting a correct number of commands being written to the commandpipe'); - - $this->assertCommandSucceeded('SCHEDULE_HOST_CHECK;test;5000', $result[0]); - $this->assertCommandSucceeded('SCHEDULE_SVC_CHECK;test;svc1;5000', $result[1]); - $this->assertCommandSucceeded('SCHEDULE_FORCED_HOST_CHECK;test;5000', $result[2]); - $this->assertCommandSucceeded('SCHEDULE_FORCED_SVC_CHECK;test;svc1;5000', $result[3]); - $this->assertCommandSucceeded('SCHEDULE_FORCED_HOST_SVC_CHECKS;test;5000', $result[4]); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether modifying monitoringflags of a host and service is correctly serialized and send to the command pipe - * - * @throws Exception - */ - public function testObjectStateModifications() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->getTransport()->setOpenMode('a'); - $pipe->setMonitoringProperties( - array( - (object) array( - 'host_name' => 'Testhost' - ), - (object) array( - 'host_name' => 'host', - 'service_description' => 'svc' - ) - ), new MONFLAG( - array( - MONFLAG::ACTIVE => MONFLAG::STATE_DISABLE, - MONFLAG::PASSIVE => MONFLAG::STATE_ENABLE, - MONFLAG::NOTIFICATIONS => MONFLAG::STATE_DISABLE, - MONFLAG::EVENTHANDLER => MONFLAG::STATE_ENABLE, - MONFLAG::FLAPPING => MONFLAG::STATE_DISABLE, - MONFLAG::FRESHNESS => MONFLAG::STATE_ENABLE, - ) - ) - ); - - $result = explode("\n", file_get_contents($this->getPipeName())); - array_pop($result); // remove empty last line - $this->assertCount(12, $result, 'Asserting a correct number of commands being written to the commandpipe'); - foreach ($result as $command) { - $this->assertCommandSucceeded(false, $command); - } - - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether enabling and disabling global notifications are send correctly to the pipe - * - * @throws Exception - */ - public function testGlobalNotificationTrigger() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->enableGlobalNotifications(); - $this->assertCommandSucceeded('ENABLE_NOTIFICATIONS;'); - $pipe->disableGlobalNotifications(); - $this->assertCommandSucceeded('DISABLE_NOTIFICATIONS;'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether host and servicedowntimes are correctly scheduled - * - * @throws Exception - */ - public function testScheduleDowntime() - { - $pipe = $this->getLocalTestPipe(); - try { - $downtime = new ScheduleDowntimeCommand(25, 26, new Comment('me', 'test')); - $pipe->sendCommand( - $downtime, - array( - (object) array( - 'host_name' => 'Testhost' - ) - ) - ); - $this->assertCommandSucceeded('SCHEDULE_HOST_DOWNTIME;Testhost;25;26;1;0;0;me;test'); - - $pipe->sendCommand( - $downtime, - array( - (object) array( - 'host_name' => 'Testhost', - 'service_description' => 'svc' - ) - ) - ); - $this->assertCommandSucceeded('SCHEDULE_SVC_DOWNTIME;Testhost;svc;25;26;1;0;0;me;test'); - - $downtime->excludeHost(); - $pipe->sendCommand( - $downtime, - array( - (object) array( - 'host_name' => 'Testhost' - ) - ) - ); - $this->assertCommandSucceeded('SCHEDULE_HOST_SVC_DOWNTIME;Testhost;25;26;1;0;0;me;test'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether propagated host downtimes are correctly scheduled - * - * @throws Exception - */ - public function testSchedulePropagatedDowntime() - { - $pipe = $this->getLocalTestPipe(); - try { - $downtime = new ScheduleDowntimeCommand(25, 26, new Comment('me', 'test')); - $downtime->includeChildren(); - $pipe->sendCommand( - $downtime, - array( - (object) array( - 'host_name' => 'Testhost' - ) - ) - ); - $this->assertCommandSucceeded('SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME;Testhost;25;26;1;0;0;me;test'); - - $downtime->includeChildren(true, true); - $pipe->sendCommand( - $downtime, - array( - (object) array( - 'host_name' => 'Testhost' - ) - ) - ); - $this->assertCommandSucceeded( - 'SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME;Testhost;25;26;1;0;0;me;test' - ); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether the removal of downtimes is correctly serialized and send to the commandpipe for hosts and services - * - * @throws Exception - */ - public function testRemoveDowntime() - { - $pipe = $this->getLocalTestPipe(); - try { - $pipe->getTransport()->setOpenMode('a'); - $pipe->removeDowntime( - array( - (object) array( - 'host_name' => 'Testhost' - ), - (object) array( - 'host_name' => 'host', - 'service_description' => 'svc' - ), - (object) array( - 'host_name' => 'host', - 'service_description' => 'svc', - 'downtime_id' => 123 - ) - ) - ); - $result = explode("\n", file_get_contents($this->getPipeName())); - array_pop($result); // remove empty last line - $this->assertCount(3, $result, 'Asserting a correct number of commands being written to the commandpipe'); - $this->assertCommandSucceeded('DEL_DOWNTIME_BY_HOST_NAME;Testhost', $result[0]); - $this->assertCommandSucceeded('DEL_DOWNTIME_BY_HOST_NAME;host;svc', $result[1]); - $this->assertCommandSucceeded('DEL_SVC_DOWNTIME;123', $result[2]); - - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether custom servicenotifications are correctly send to the commandpipe without options - * - * @throws Exception - */ - public function testSendCustomServiceNotification() - { - $pipe = $this->getLocalTestPipe(); - try { - $notification = new CustomNotificationCommand(new Comment('Author', 'Comment')); - $pipe->sendCommand( - $notification, - array( - (object) array( - 'host_name' => 'Host', - 'service_description' => 'Service' - ) - ) - ); - $this->assertCommandSucceeded('SEND_CUSTOM_SVC_NOTIFICATION;Host;Service;0;Author;Comment'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether custom hostnotifications are correctly send to the commandpipe with a varlist of options - * - * @throws Exception - */ - public function testSendCustomHostNotificationWithOptions() - { - $pipe = $this->getLocalTestPipe(); - try { - $notification = new CustomNotificationCommand(new Comment('Author', 'Comment'), true, true); - $pipe->sendCommand( - $notification, - array( - (object) array( - 'host_name' => 'Host', - 'service_description' => 'Service' - ) - ) - ); - $this->assertCommandSucceeded('SEND_CUSTOM_SVC_NOTIFICATION;Host;Service;3;Author;Comment'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether commands to delay notifications are being sent to the commandpipe - * - * @throws Exception - */ - public function testDelayNotification() - { - $pipe = $this->getLocalTestPipe(); - try { - $delay = new DelayNotificationCommand(300); - $pipe->sendCommand( - $delay, - array( - (object) array( - 'host_name' => 'Host', - 'service_description' => 'Service' - ) - ) - ); - $this->assertCommandSucceeded('DELAY_SVC_NOTIFICATION;Host;Service;300'); - - $pipe->sendCommand( - $delay, - array( - (object) array( - 'host_name' => 'Host' - ) - ) - ); - $this->assertCommandSucceeded('DELAY_HOST_NOTIFICATION;Host;300'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test whether commands to submit passive check results are being sent to the commandpipe - * - * @throws Exception - */ - public function testSubmitPassiveCheckresult() - { - $pipe = $this->getLocalTestPipe(); - try { - $result = new SubmitPassiveCheckresultCommand(0, 'foo', 'bar'); - $pipe->sendCommand( - $result, - array( - (object) array( - 'host_name' => 'Host', - 'service_description' => 'Service' - ) - ) - ); - $this->assertCommandSucceeded('PROCESS_SERVICE_CHECK_RESULT;Host;Service;0;foo|bar'); - - $result->setOutput('foobar'); - $result->setPerformanceData(''); - $pipe->sendCommand( - $result, - array( - (object) array( - 'host_name' => 'Host' - ) - ) - ); - $this->assertCommandSucceeded('PROCESS_HOST_CHECK_RESULT;Host;0;foobar'); - } catch (Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } - - /** - * Test sending of commands via SSH (currently disabled) - * - * @throws Exception - */ - public function testSSHCommands() - { - $this->markTestSkipped('This test assumes running in a vagrant VM with key-auth'); - - $pipe = $this->getSSHTestPipe(); - try { - $ack = new AcknowledgeCommand(new Comment('I can', 'sends teh ack')); - $pipe->sendCommand( - $ack, - array( - (object) array( - 'host_name' => 'hostA' - ) - ) - ); - $this->assertCommandSucceeded('ACKNOWLEDGE_HOST_PROBLEM;hostA;0;0;0;I can;sends teh ack'); - } catch(Exception $e) { - $this->cleanup(); - throw $e; - } - $this->cleanup(); - } -} diff --git a/test/php/library/Icinga/User/PreferencesTest.php b/test/php/library/Icinga/User/PreferencesTest.php index da5c318fa..540ea1b14 100644 --- a/test/php/library/Icinga/User/PreferencesTest.php +++ b/test/php/library/Icinga/User/PreferencesTest.php @@ -44,4 +44,18 @@ class PreferfencesTest extends BaseTestCase $this->assertEquals(2, count($prefs)); } + + public function testWhetherGetValueReturnsExpectedValue() + { + $prefs = new Preferences(array( + 'test' => array ( + 'key1' => '1', + 'key2' => '2', + ) + )); + + $result = $prefs->getValue('test', 'key2'); + + $this->assertEquals('2', $result, 'Preferences::getValue() do not return an expected value'); + } } diff --git a/test/php/library/Icinga/UserTest.php b/test/php/library/Icinga/UserTest.php index ebb39293e..d12666b04 100644 --- a/test/php/library/Icinga/UserTest.php +++ b/test/php/library/Icinga/UserTest.php @@ -60,4 +60,19 @@ class UserTest extends BaseTestCase $user = new User('unittest'); $user->setEmail('mySampleEmail at someDomain dot org'); } + + public function testPermissions() + { + $user = new User('test'); + $user->setPermissions(array( + 'test', + 'test/some/specific', + 'test/more/*' + )); + $this->assertTrue($user->can('test')); + $this->assertTrue($user->can('test/some/specific')); + $this->assertTrue($user->can('test/more/everything')); + $this->assertFalse($user->can('not/test')); + $this->assertFalse($user->can('test/some/not/so/specific')); + } } diff --git a/test/php/library/Icinga/Util/TranslatorTest.php b/test/php/library/Icinga/Util/TranslatorTest.php index 9e341d545..4d74cc8dc 100644 --- a/test/php/library/Icinga/Util/TranslatorTest.php +++ b/test/php/library/Icinga/Util/TranslatorTest.php @@ -213,4 +213,76 @@ class TranslatorTest extends BaseTestCase 'Translator::getPreferredLocaleCode does not return the default locale if no match could be found' ); } + + /** + * @depends testWhetherSetupLocaleSetsUpTheGivenLocale + */ + public function testWhetherTranslatePluralReturnsTheSingularForm() + { + Translator::setupLocale('de_DE'); + + $result = Translator::translatePlural('test service', 'test services', 1, 'icingatest'); + + $expected = 'test dienst'; + + $this->assertEquals( + $expected, + $result, + 'Translator::translatePlural() could not return the translated singular form' + ); + } + + /** + * @depends testWhetherSetupLocaleSetsUpTheGivenLocale + */ + public function testWhetherTranslatePluralReturnsThePluralForm() + { + Translator::setupLocale('de_DE'); + + $result = Translator::translatePlural('test service', 'test services', 2, 'icingatest'); + + $expected = 'test dienste'; + + $this->assertEquals( + $expected, + $result, + 'Translator::translatePlural() could not return the translated plural form' + ); + } + + /** + * @depends testWhetherSetupLocaleSetsUpTheGivenLocale + */ + public function testWhetherTranslateReturnsTheContextForm() + { + Translator::setupLocale('de_DE'); + + $result = Translator::translate('context service', 'icingatest', 'test2'); + + $expected = 'context dienst test2'; + + $this->assertEquals( + $expected, + $result, + 'Translator::translate() could not return the translated context form' + ); + } + + /** + * @depends testWhetherSetupLocaleSetsUpTheGivenLocale + */ + public function testWhetherTranslatePluralReturnsTheContextForm() + { + Translator::setupLocale('de_DE'); + + $result = Translator::translatePlural('context service', 'context services', 3, 'icingatest', 'test-context'); + + $expected = 'context plural dienste'; + + $this->assertEquals( + $expected, + $result, + 'Translator::translatePlural() could not return the translated context form' + ); + } } diff --git a/test/php/library/Icinga/Web/Form/Element/DateTimePickerTest.php b/test/php/library/Icinga/Web/Form/Element/DateTimePickerTest.php index 7610803b2..802d610ab 100644 --- a/test/php/library/Icinga/Web/Form/Element/DateTimePickerTest.php +++ b/test/php/library/Icinga/Web/Form/Element/DateTimePickerTest.php @@ -4,102 +4,44 @@ namespace Tests\Icinga\Web\Form\Element; +use DateTime; use Icinga\Test\BaseTestCase; use Icinga\Web\Form\Element\DateTimePicker; class DateTimePickerTest extends BaseTestCase { - /** - * Test that DateTimePicker::isValid() returns false if the input is not valid in terms of being a date/time string - * or a timestamp - */ - public function testValidateInvalidInput() + public function testLocalDateAndTimeInput() { - $dt = new DateTimePicker( - 'foo', - array( - 'patterns' => array( - 'd/m/Y g:i A', - 'd.m.Y H:i:s' - ) - ) + $dateTime = new DateTimePicker( + 'name' ); - - $this->assertFalse( - $dt->isValid('08/27/2013 12:40 PM'), - 'Wrong placed month/day must not be valid input' + $now = new DateTime(); + $this->assertTrue( + $dateTime->isValid($now->format('Y-m-d\TH:i:s')), + 'A string representing a local date and time (with no timezone information) must be considered valid input' ); - $this->assertFalse( - $dt->isValid('bar'), - 'Arbitrary strings must not be valid input' - ); - $this->assertFalse( - $dt->isValid('12:40 AM'), - 'Time strings must not be valid input' - ); - $this->assertFalse( - $dt->isValid('27/08/2013'), - 'Date strings must not be valid input' - ); - $this->assertFalse( - $dt->isValid('13736a16223'), - 'Invalid Unix timestamps must not be valid input' + $this->assertTrue( + $dateTime->getValue() instanceof DateTime, + 'DateTimePicker::getValue() must return an instance of DateTime if its input is valid' ); } - /** - * Test that DateTimePicker::isValid() returns true if the input is valid in terms of being a date/time string - * or a timestamp - */ - public function testValidateValidInput() + public function testRFC3339Input() { - $dt = new DateTimePicker( - 'foo', + $dateTime = new DateTimePicker( + 'name', array( - 'patterns' => array( - 'd/m/Y g:i A', - 'd.m.Y H:i:s' - ) + 'local' => false ) ); - + $now = new DateTime(); $this->assertTrue( - $dt->isValid('27/08/2013 12:40 PM'), - 'Using a valid date/time string must not fail' + $dateTime->isValid($now->format(DateTime::RFC3339)), + 'A string representing a global date and time (with timezone information) must be considered valid input' ); $this->assertTrue( - $dt->isValid('12.07.2013 08:03:43'), - 'Using a valid date/time string must not fail' - ); - $this->assertTrue( - $dt->isValid(1373616223), - 'Using valid Unix timestamps must not fail' - ); - $this->assertTrue( - $dt->isValid('1373616223'), - 'Using strings as Unix timestamps must not fail' - ); - } - - /** - * Test that DateTimePicker::getValue() returns a timestamp after a successful call to isValid - */ - public function testGetValueReturnsUnixTimestampAfterSuccessfulIsValidCall() - { - $dt = new DateTimePicker( - 'foo', - array( - 'patterns' => array( - 'd.m.Y H:i:s' - ) - ) - ); - $dt->isValid('12.07.2013 08:03:43'); - - $this->assertEquals( - 1373616223, - $dt->getValue(), - 'getValue did not return the correct Unix timestamp according to the given date/time string' + $dateTime->getValue() instanceof DateTime, + 'DateTimePicker::getValue() must return an instance of DateTime if its input is valid' ); } } diff --git a/test/php/library/Icinga/Web/FormTest.php b/test/php/library/Icinga/Web/FormTest.php index 4e6ec5d92..a2fc9c8b1 100644 --- a/test/php/library/Icinga/Web/FormTest.php +++ b/test/php/library/Icinga/Web/FormTest.php @@ -4,58 +4,285 @@ namespace Tests\Icinga\Web; +use Mockery; use Icinga\Web\Form; +use Icinga\Web\Request; use Icinga\Test\BaseTestCase; +class SuccessfulForm extends Form +{ + public function onSuccess(Request $request) + { + return true; + } +} + class FormTest extends BaseTestCase { - public function testWhetherAddElementDoesNotAddSpecificDecorators() + public function tearDown() + { + Mockery::close(); // Necessary as some tests are running isolated + } + + public function testWhetherASubmitButtonIsAddedWithASubmitLabelBeingSet() { $form = new Form(); - $form->addElement('text', 'someText'); - $element = $form->getElement('someText'); + $form->setTokenDisabled(); + $form->setSubmitLabel('test'); + $form->create(); - $this->assertFalse( - $element->getDecorator('HtmlTag'), - 'Form::addElement does not remove the HtmlTag-Decorator' - ); - $this->assertFalse( - $element->getDecorator('Label'), - 'Form::addElement does not remove the Label-Decorator' - ); - $this->assertFalse( - $element->getDecorator('DtDdWrapper'), - 'Form::addElement does not remove the DtDdWrapper-Decorator' + $this->assertInstanceOf( + '\Zend_Form_Element', + $form->getElement('btn_submit'), + 'Form::create() does not add a submit button in case a submit label is set' ); } - public function testWhetherAddElementDoesNotAddAnyOptionalDecoratorsToHiddenElements() + public function testWhetherNoSubmitButtonIsAddedWithoutASubmitLabelBeingSet() { $form = new Form(); - $form->addElement('hidden', 'somethingHidden'); - $element = $form->getElement('somethingHidden'); + $form->setTokenDisabled(); + $form->create(); + + $this->assertNull( + $form->getElement('btn_submit'), + 'Form::create() adds a submit button in case no submit label is set' + ); + } + + /** + * @depends testWhetherASubmitButtonIsAddedWithASubmitLabelBeingSet + */ + public function testWhetherIsSubmittedReturnsTrueWithASubmitLabelBeingSet() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->setSubmitLabel('test'); + $form->populate(array('btn_submit' => true)); + + $this->assertTrue( + $form->isSubmitted(), + 'Form::isSubmitted() does not return true in case a submit label is set' + ); + } + + /** + * @depends testWhetherNoSubmitButtonIsAddedWithoutASubmitLabelBeingSet + */ + public function testWhetherIsSubmittedReturnsFalseWithoutASubmitLabelBeingSet() + { + $form = new Form(); + + $this->assertFalse( + $form->isSubmitted(), + 'Form::isSubmitted() does not return false in case no submit label is set' + ); + } + + public function testWhetherTheCurrentLocationIsUsedAsDefaultRedirection() + { + $this->getRequestMock()->shouldReceive('getPathInfo')->andReturn('default/route'); + $this->getResponseMock()->shouldReceive('redirectAndExit')->atLeast()->once() + ->with(Mockery::on(function ($url) { return $url->getRelativeUrl() === 'default/route'; })); + + $form = new SuccessfulForm(); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->handleRequest(); + } + + public function testWhetherAnExplicitlySetRedirectUrlIsUsedForRedirection() + { + $this->getResponseMock()->shouldReceive('redirectAndExit')->atLeast()->once()->with('special/route'); + + $form = new SuccessfulForm(); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->setRedirectUrl('special/route'); + $form->handleRequest(); + } + + /** + * @runInSeparateProcess + */ + public function testWhetherACsrfCounterMeasureIsBeingAdded() + { + Mockery::mock('alias:Icinga\Web\Session')->shouldReceive('getSession->getId')->andReturn('1234567890'); + + $form = new Form(); + $form->create(); + + $this->assertInstanceOf( + '\Zend_Form_Element', + $form->getElement($form->getTokenElementName()), + 'Form::create() does not add a csrf counter measure element' + ); + } + + public function testWhetherACsrfCounterMeasureIsNotBeingAdded() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->create(); + + $this->assertNull( + $form->getElement($form->getTokenElementName()), + 'Form::create() adds a csrf counter measure element in case it\'s disabled' + ); + } + + public function testWhetherAUniqueFormIdIsBeingAdded() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->create(); + + $this->assertInstanceOf( + '\Zend_Form_Element', + $form->getElement($form->getUidElementName()), + 'Form::create() does not add a form identification element' + ); + } + + public function testWhetherAUniqueFormIdIsNotBeingAdded() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->create(); + + $this->assertNull( + $form->getElement($form->getUidElementName()), + 'Form::create() adds a form identification element in case it\'s disabled' + ); + } + + /** + * @depends testWhetherAUniqueFormIdIsBeingAdded + */ + public function testWhetherAFormIsSentWithAUniqueFormIdBeingAdded() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->create(); + + $this->assertTrue( + $form->wasSent( + array( + $form->getUidElementName() => $form->getElement($form->getUidElementName())->getValue() + ) + ), + 'Form::wasSent() does not return true in case a the form identification value is being sent' + ); + } + + /** + * @depends testWhetherAUniqueFormIdIsNotBeingAdded + */ + public function testWhetherAFormIsNotSentWithoutAUniqueFormIdBeingAdded() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->create(); + + $this->assertFalse( + $form->wasSent(array()), + 'Form::wasSent() does not return false in case no form identification element was added' + ); + } + + public function testWhetherADefaultActionIsBeingSetOnFormCreation() + { + $this->getRequestMock()->shouldReceive('getPathInfo')->andReturn('some/route'); + + $form = new Form(); + $form->setTokenDisabled(); + $form->create(); + + $this->assertEquals( + '/some/route', + $form->getAction(), + 'Form::create() does not set a default action if none was set explicitly' + ); + } + + /** + * @depends testWhetherAUniqueFormIdIsBeingAdded + * @depends testWhetherASubmitButtonIsAddedWithASubmitLabelBeingSet + */ + public function testWhetherItIsPossibleToRecreateAForm() + { + $form = new Form(); + $form->setTokenDisabled(); + $form->setSubmitLabel('test'); + $form->create(); // sets the flag $this->created to true + $form->clearElements(); // should reset the flag.. + $form->create(); // ..so that we can recreate the form $this->assertCount( - 1, - $element->getDecorators(), - 'Form::addElement adds more decorators than necessary to hidden elements' - ); - $this->assertInstanceOf( - '\Zend_Form_Decorator_ViewHelper', - $element->getDecorator('ViewHelper'), - 'Form::addElement does not add the ViewHelper-Decorator to hidden elements' + 2, + $form->getElements(), + 'Form::clearElements() does not fully reset the form' ); } - public function testWhetherLoadDefaultDecoratorsDoesNotAddTheHtmlTagDecorator() + public function testWhetherGetNameReturnsTheEscapedClassNameByDefault() { $form = new Form(); - $form->loadDefaultDecorators(); - $this->assertArrayNotHasKey( - 'HtmlTag', - $form->getDecorators(), - 'Form::loadDefaultDecorators adds the HtmlTag-Decorator' + $this->assertEquals( + $form->filterName(get_class($form)), + $form->getName(), + 'Form::getName() does not return the escaped class name in case no name was explicitly set' + ); + } + + public function testWhetherGetRequestDataOnlyReturnsFormRelevantData() + { + $form = new Form(); + $form->setMethod('POST'); + + $expectedResult = array('expected_key' => 'expected_value'); + $request = $this->getRequestMock(); + $request->shouldReceive('getMethod')->andReturn('POST') + ->shouldReceive('isPost')->andReturn(true) + ->shouldReceive('getPost')->andReturn($expectedResult); + + $this->assertEquals( + $expectedResult, + $form->getRequestData($request), + 'Form::getRequestData() does not (only) return form relevant data' + ); + } + + /** + * @expectedException LogicException + */ + public function testWhetherTheOnSuccessOptionMustBeCallable() + { + new Form(array('onSuccess' => '_invalid_')); + } + + /** + * @depends testWhetherACsrfCounterMeasureIsNotBeingAdded + * @depends testWhetherAUniqueFormIdIsNotBeingAdded + * @depends testWhetherNoSubmitButtonIsAddedWithoutASubmitLabelBeingSet + */ + public function testWhetherAClosureCanBePassedAsOnSuccessCallback() + { + $request = new Request(); + $form = new Form(array( + 'onSuccess' => function ($req) { $req->setParam('test', 'tset'); return false; } + )); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->handleRequest($request); + + $this->assertEquals( + 'tset', + $request->getParam('test'), + 'Form does not utilize the onSuccess callback set with form options on instantiation' ); } } diff --git a/test/php/library/Icinga/Web/HookTest.php b/test/php/library/Icinga/Web/HookTest.php index 9e82ad90d..16300f5ca 100644 --- a/test/php/library/Icinga/Web/HookTest.php +++ b/test/php/library/Icinga/Web/HookTest.php @@ -2,23 +2,38 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} +namespace Icinga\Web\Hook; + +use Icinga\Web\Hook; + +class TestHook extends Hook {} + namespace Tests\Icinga\Web; -use Mockery; -use Exception; -use Icinga\Web\Hook; use Icinga\Test\BaseTestCase; +use Icinga\Web\Hook; +use Icinga\Web\Hook\TestHook; +use Exception; -class ErrorProneHookImplementation +class NoHook {} +class MyHook extends TestHook {} +class AnotherHook extends TestHook {} +class FailingHook extends TestHook { public function __construct() { - throw new Exception(); + throw new Exception("I'm failing"); } } class HookTest extends BaseTestCase { + protected $invalidHook = '\\Tests\\Icinga\\Web\\NoHook'; + protected $validHook = '\\Tests\\Icinga\\Web\\MyHook'; + protected $anotherHook = '\\Tests\\Icinga\\Web\\AnotherHook'; + protected $failingHook = '\\Tests\\Icinga\\Web\\FailingHook'; + protected $testBaseClass = '\\Icinga\\Web\\Hook\\TestHook'; + public function setUp() { parent::setUp(); @@ -31,83 +46,28 @@ class HookTest extends BaseTestCase Hook::clean(); } - public function testWhetherHasReturnsTrueIfGivenAKnownHook() + public function testKnowsWhichHooksAreRegistered() { - Hook::registerClass('TestHook', __FUNCTION__, get_class(Mockery::mock(Hook::$BASE_NS . 'TestHook'))); - - $this->assertTrue(Hook::has('TestHook'), 'Hook::has does not return true if given a known hook'); + Hook::register('test', __FUNCTION__, $this->validHook); + $this->assertTrue(Hook::has('test')); + $this->assertFalse(Hook::has('no_such_hook')); } - public function testWhetherHasReturnsFalseIfGivenAnUnknownHook() + public function testCorrectlyHandlesMultipleInstances() { - $this->assertFalse(Hook::has('not_existing'), 'Hook::has does not return false if given an unknown hook'); - } - - public function testWhetherHooksCanBeRegisteredWithRegisterClass() - { - Hook::registerClass('TestHook', __FUNCTION__, get_class(Mockery::mock(Hook::$BASE_NS . 'TestHook'))); - - $this->assertTrue(Hook::has('TestHook'), 'Hook::registerClass does not properly register a given hook'); - } - - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterClass - */ - public function testWhetherMultipleHooksOfTheSameTypeCanBeRegisteredWithRegisterClass() - { - $firstHook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - $secondHook = Mockery::mock('overload:' . get_class($firstHook)); - - Hook::registerClass('TestHook', 'one', get_class($firstHook)); - Hook::registerClass('TestHook', 'two', get_class($secondHook)); + Hook::register('test', 'one', $this->validHook); + Hook::register('test', 'two', $this->anotherHook); $this->assertInstanceOf( - get_class($secondHook), - Hook::createInstance('TestHook', 'two'), - 'Hook::registerClass is not able to register different hooks of the same type' + $this->anotherHook, + Hook::createInstance('test', 'two') + ); + $this->assertInstanceOf( + $this->validHook, + Hook::createInstance('test', 'one') ); } - /** - * @expectedException Icinga\Exception\ProgrammingError - */ - public function testWhetherOnlyClassesCanBeRegisteredAsHookWithRegisterClass() - { - Hook::registerClass('TestHook', __FUNCTION__, 'nope'); - } - - public function testWhetherHooksCanBeRegisteredWithRegisterObject() - { - Hook::registerObject('TestHook', __FUNCTION__, Mockery::mock(Hook::$BASE_NS . 'TestHook')); - - $this->assertTrue(Hook::has('TestHook'), 'Hook::registerObject does not properly register a given hook'); - } - - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterObject - */ - public function testWhetherMultipleHooksOfTheSameTypeCanBeRegisteredWithRegisterObject() - { - $firstHook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - $secondHook = Mockery::mock('overload:' . get_class($firstHook)); - - Hook::registerObject('TestHook', 'one', $firstHook); - Hook::registerObject('TestHook', 'two', $secondHook); - $this->assertInstanceOf( - get_class($secondHook), - Hook::createInstance('TestHook', 'two'), - 'Hook::registerObject is not able to register different hooks of the same type' - ); - } - - /** - * @expectedException Icinga\Exception\ProgrammingError - */ - public function testWhetherOnlyObjectsCanBeRegisteredAsHookWithRegisterObject() - { - Hook::registerObject('TestHook', __FUNCTION__, 'nope'); - } - - public function testWhetherCreateInstanceReturnsNullIfGivenAnUnknownHookName() + public function testReturnsNullForInvalidHooks() { $this->assertNull( Hook::createInstance('not_existing', __FUNCTION__), @@ -115,98 +75,41 @@ class HookTest extends BaseTestCase ); } - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterClass - */ - public function testWhetherCreateInstanceInitializesHooksInheritingFromAPredefinedAbstractHook() + public function testReturnsNullForFailingHook() { - $baseHook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - Hook::registerClass( - 'TestHook', - __FUNCTION__, - get_class(Mockery::mock('overload:' . get_class($baseHook))) - ); + Hook::register('test', __FUNCTION__, $this->failingHook); + $this->assertNull(Hook::createInstance('test', __FUNCTION__)); + } + public function testChecksWhetherCreatedInstancesInheritBaseClasses() + { + Hook::register('test', __FUNCTION__, $this->validHook); $this->assertInstanceOf( - get_class($baseHook), - Hook::createInstance('TestHook', __FUNCTION__), - 'Hook::createInstance does not initialize hooks inheriting from a predefined abstract hook' + $this->testBaseClass, + Hook::createInstance('test', __FUNCTION__) ); } - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterClass - */ - public function testWhetherCreateInstanceDoesNotInitializeMultipleHooksForASpecificIdentifier() - { - Hook::registerClass('TestHook', __FUNCTION__, get_class(Mockery::mock(Hook::$BASE_NS . 'TestHook'))); - $secondHook = Hook::createInstance('TestHook', __FUNCTION__); - $thirdHook = Hook::createInstance('TestHook', __FUNCTION__); - - $this->assertSame( - $secondHook, - $thirdHook, - 'Hook::createInstance initializes multiple hooks for a specific identifier' - ); - } - - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterClass - */ - public function testWhetherCreateInstanceReturnsNullIfHookCannotBeInitialized() - { - Hook::registerClass('TestHook', __FUNCTION__, 'Tests\Icinga\Web\ErrorProneHookImplementation'); - - $this->assertNull(Hook::createInstance('TestHook', __FUNCTION__)); - } - /** * @expectedException Icinga\Exception\ProgrammingError - * @depends testWhetherHooksCanBeRegisteredWithRegisterClass */ - public function testWhetherCreateInstanceThrowsAnErrorIfGivenAHookNotInheritingFromAPredefinedAbstractHook() + public function testThrowsErrorsForInstancesNotInheritingBaseClasses() { - Mockery::mock(Hook::$BASE_NS . 'TestHook'); - Hook::registerClass('TestHook', __FUNCTION__, get_class(Mockery::mock('TestHook'))); - - Hook::createInstance('TestHook', __FUNCTION__); + Hook::register('test', __FUNCTION__, $this->invalidHook); + Hook::createInstance('test', __FUNCTION__); } - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterObject - */ - public function testWhetherAllReturnsAllRegisteredHooks() + public function testCreatesIdenticalInstancesOnlyOnce() { - $hook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - Hook::registerObject('TestHook', 'one', $hook); - Hook::registerObject('TestHook', 'two', $hook); - Hook::registerObject('TestHook', 'three', $hook); + Hook::register('test', __FUNCTION__, $this->validHook); + $first = Hook::createInstance('test', __FUNCTION__); + $second = Hook::createInstance('test', __FUNCTION__); - $this->assertCount(3, Hook::all('TestHook'), 'Hook::all does not return all registered hooks'); + $this->assertSame($first, $second); } - public function testWhetherAllReturnsNothingIfGivenAnUnknownHookName() + public function testReturnsAnEmptyArrayWithNoRegisteredHook() { - $this->assertEmpty( - Hook::all('not_existing'), - 'Hook::all does not return an empty array if given an unknown hook' - ); - } - - /** - * @depends testWhetherHooksCanBeRegisteredWithRegisterObject - */ - public function testWhetherFirstReturnsTheFirstRegisteredHook() - { - $firstHook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - $secondHook = Mockery::mock(Hook::$BASE_NS . 'TestHook'); - Hook::registerObject('TestHook', 'first', $firstHook); - Hook::registerObject('TestHook', 'second', $secondHook); - - $this->assertSame( - $firstHook, - Hook::first('TestHook'), - 'Hook::first does not return the first registered hook' - ); + $this->assertEquals(array(), Hook::all('not_existing')); } } diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index 5da50c3f8..68206beee 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -12,11 +12,9 @@ class UrlTest extends BaseTestCase { public function testWhetherFromRequestWorksWithoutARequest() { - $request = Mockery::mock('Icinga\Web\Request'); - $request->shouldReceive('getPathInfo')->andReturn('my/test/url.html') - ->shouldReceive('getBaseUrl')->andReturn('/path/to') + $this->getRequestMock()->shouldReceive('getBaseUrl')->andReturn('/path/to') + ->shouldReceive('getPathInfo')->andReturn('my/test/url.html') ->shouldReceive('getQuery')->andReturn(array('param1' => 'value1', 'param2' => 'value2')); - $this->setupIcingaMock($request); $url = Url::fromRequest(); $this->assertEquals( diff --git a/test/php/library/Icinga/Web/View/DateTimeRendererTest.php b/test/php/library/Icinga/Web/View/DateTimeRendererTest.php new file mode 100644 index 000000000..e45d3fdfe --- /dev/null +++ b/test/php/library/Icinga/Web/View/DateTimeRendererTest.php @@ -0,0 +1,102 @@ +assertInstanceOf( + 'Icinga\Web\View\DateTimeRenderer', + $dt, + 'Dashboard::create() could not create DateTimeRenderer' + ); + } + + /** + * @depends testWhetherCreateCreatesDateTimeRenderer + */ + public function testWhetherIsDateTimeReturnsRightType() + { + $dateTime = new DateTime('+1 day'); + $dt = DateTimeRenderer::create($dateTime); + + $this->assertTrue( + $dt->isDateTime(), + 'Dashboard::isDateTime() returns wrong type' + ); + } + + /** + * @depends testWhetherCreateCreatesDateTimeRenderer + */ + public function testWhetherIsTimeReturnsRightType() + { + $dateTime = new DateTime('+1 hour'); + $dt = DateTimeRenderer::create($dateTime); + + $this->assertTrue( + $dt->isTime(), + 'Dashboard::isTime() returns wrong type' + ); + } + + /** + * @depends testWhetherCreateCreatesDateTimeRenderer + */ + public function testWhetherIsTimespanReturnsRightType() + { + $dateTime = new DateTime('+1 minute'); + $dt = DateTimeRenderer::create($dateTime); + + $this->assertTrue( + $dt->isTimespan(), + 'Dashboard::isTimespan() returns wrong type' + ); + } + + /** + * @depends testWhetherCreateCreatesDateTimeRenderer + */ + public function testWhetherNormalizeReturnsNormalizedDateTime() + { + $dateTime = time(); + $dt = DateTimeRenderer::normalize($dateTime); + + $this->assertInstanceOf( + 'DateTime', + $dt, + 'Dashboard::normalize() returns wrong instance' + ); + } + + /** + * @depends testWhetherCreateCreatesDateTimeRenderer + */ + public function testWhetherRenderReturnsRightText() + { + $dateTime = new DateTime('+1 minute'); + $dt = DateTimeRenderer::create($dateTime); + + $text = $dt->render( + '#1 The service is down since %s', + '#2 The service is down since %s o\'clock.', + '#3 The service is down for %s.' + ); + + $this->assertRegExp( + '/#3/', + $text, + 'Dashboard::render() returns wrong text' + ); + } +} diff --git a/test/php/library/Icinga/Web/Widget/DashboardTest.php b/test/php/library/Icinga/Web/Widget/DashboardTest.php index 9114a8f90..be11c42fe 100644 --- a/test/php/library/Icinga/Web/Widget/DashboardTest.php +++ b/test/php/library/Icinga/Web/Widget/DashboardTest.php @@ -47,7 +47,7 @@ class DashboardTest extends BaseTestCase Mockery::close(); // Necessary because some tests run in a separate process } - protected function setupIcingaMock(\Zend_Controller_Request_Abstract $request) + public function setUp() { $moduleMock = Mockery::mock('Icinga\Application\Modules\Module'); $moduleMock->shouldReceive('getPaneItems')->andReturn(array( @@ -59,14 +59,8 @@ class DashboardTest extends BaseTestCase 'test-module' => $moduleMock )); - $bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing(); - $bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing( - function () use ($request) { return $request; } - )->shouldReceive('getApplicationDir')->andReturn(self::$appDir); - + $bootstrapMock = $this->setupIcingaMock(); $bootstrapMock->shouldReceive('getModuleManager')->andReturn($moduleManagerMock); - - Icinga::setApp($bootstrapMock, true); } public function testWhetherCreatePaneCreatesAPane() diff --git a/test/php/library/Icinga/Web/Widget/SearchDashboardTest.php b/test/php/library/Icinga/Web/Widget/SearchDashboardTest.php index d42862ff6..d9323c00a 100644 --- a/test/php/library/Icinga/Web/Widget/SearchDashboardTest.php +++ b/test/php/library/Icinga/Web/Widget/SearchDashboardTest.php @@ -5,19 +5,12 @@ namespace Tests\Icinga\Web; use Mockery; -use Icinga\Application\Icinga; -use Icinga\Web\Widget\SearchDashboard; use Icinga\Test\BaseTestCase; +use Icinga\Web\Widget\SearchDashboard; class SearchDashboardTest extends BaseTestCase { - public function tearDown() - { - parent::tearDown(); - Mockery::close(); - } - - protected function setupIcingaMock(\Zend_Controller_Request_Abstract $request) + public function setUp() { $moduleMock = Mockery::mock('Icinga\Application\Modules\Module'); $searchUrl = (object) array( @@ -33,14 +26,8 @@ class SearchDashboardTest extends BaseTestCase 'test-module' => $moduleMock )); - $bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing(); - $bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing( - function () use ($request) { return $request; } - )->shouldReceive('getApplicationDir')->andReturn(self::$appDir); - + $bootstrapMock = $this->setupIcingaMock(); $bootstrapMock->shouldReceive('getModuleManager')->andReturn($moduleManagerMock); - - Icinga::setApp($bootstrapMock, true); } /** diff --git a/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.mo b/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.mo index 487e49023..621eab2bf 100644 Binary files a/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.mo and b/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.mo differ diff --git a/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.po b/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.po index 2bf77ccae..3215ac4f7 100644 --- a/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.po +++ b/test/php/res/locale/de_DE/LC_MESSAGES/icingatest.po @@ -1,2 +1,42 @@ +msgid "" +msgstr "" +"Project-Id-Version: Icinga Web 2 Test (0.0.1)\n" +"Report-Msgid-Bugs-To: dev@icinga.org\n" +"POT-Creation-Date: 2014-09-16 13:29+0200\n" +"PO-Revision-Date: 2014-09-16 16:08+0100\n" +"Last-Translator: Alexander Fuhr \n" +"Language: de_DE\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.5.4\n" + msgid "Lorem ipsum dolor sit amet" -msgstr "Lorem ipsum dolor sit amet!" \ No newline at end of file +msgstr "Lorem ipsum dolor sit amet!" + +msgid "test service" +msgid_plural "test services" +msgstr[0] "test dienst" +msgstr[1] "test dienste" + +msgctxt "test" +msgid "context service" +msgstr "context dienst test" + +msgctxt "test2" +msgid "context service" +msgstr "context dienst test2" + +msgctxt "test-contextu" +msgid "context service" +msgid_plural "context services" +msgstr[0] "context plural dienstu" +msgstr[1] "context plural diensteu" + +msgctxt "test-context" +msgid "context service" +msgid_plural "context services" +msgstr[0] "context plural dienst" +msgstr[1] "context plural dienste"