Merge branch 'master' into feature/rename-monitoring-instances-to-command-transports-and-allow-to-link-them-with-a-monitoring-instance-9651

Conflicts:
	modules/monitoring/application/controllers/ConfigController.php
This commit is contained in:
Johannes Meyer 2015-08-31 13:05:10 +02:00
commit 1c51735629
99 changed files with 1791 additions and 1544 deletions

View File

@ -1,10 +1,10 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers; namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Version; use Icinga\Application\Version;
use Icinga\Web\Controller\ActionController;
class AboutController extends ActionController class AboutController extends ActionController
{ {

View File

@ -1,12 +1,11 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers; namespace Icinga\Controllers;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Forms\Authentication\LoginForm; use Icinga\Forms\Authentication\LoginForm;
use Icinga\Web\Controller; use Icinga\Web\Controller;
use Icinga\Web\Cookie;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
@ -39,8 +38,11 @@ class AuthenticationController extends Controller
} }
if (! $requiresSetup) { if (! $requiresSetup) {
if (! $this->getRequest()->hasCookieSupport()) { if (! $this->getRequest()->hasCookieSupport()) {
echo $this->translate("Cookies must be enabled to run this application.\n"); $this
$this->getResponse()->setHttpResponseCode(403)->sendHeaders(); ->getResponse()
->setBody("Cookies must be enabled to run this application.\n")
->setHttpResponseCode(403)
->sendResponse();
exit(); exit();
} }
$form->handleRequest(); $form->handleRequest();

View File

@ -1,21 +1,24 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use InvalidArgumentException;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Modules\Module; use Icinga\Application\Modules\Module;
use Icinga\Data\ResourceFactory; use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserBackendConfigForm;
use Icinga\Forms\Config\UserBackendReorderForm;
use Icinga\Forms\Config\GeneralConfigForm; use Icinga\Forms\Config\GeneralConfigForm;
use Icinga\Forms\Config\ResourceConfigForm; use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Forms\Config\UserBackendConfigForm;
use Icinga\Forms\Config\UserBackendReorderForm;
use Icinga\Forms\ConfirmRemovalForm; use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Security\SecurityException; use Icinga\Security\SecurityException;
use Icinga\Web\Controller; use Icinga\Web\Controller;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget; use Icinga\Web\Widget;
/** /**

View File

@ -1,12 +1,16 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Zend_Controller_Action_Exception;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Forms\ConfirmRemovalForm; use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Dashboard\DashletForm; use Icinga\Forms\Dashboard\DashletForm;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Form; use Icinga\Web\Form;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Dashboard; use Icinga\Web\Widget\Dashboard;
use Icinga\Web\Widget\Tabextension\DashboardSettings; use Icinga\Web\Widget\Tabextension\DashboardSettings;

View File

@ -1,6 +1,9 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Zend_Controller_Plugin_ErrorHandler;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Exception\Http\HttpMethodNotAllowedException; use Icinga\Exception\Http\HttpMethodNotAllowedException;

View File

@ -1,92 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Filter\Filter;
use Icinga\Application\Logger;
/**
* Application wide interface for filtering
*/
class FilterController extends ActionController
{
/**
* The current filter registry
*
* @var Filter
*/
private $registry;
private $moduleRegistry;
/**
* Entry point for filtering, uses the filter_domain and filter_module request parameter
* to determine which filter registry should be used
*/
public function indexAction()
{
$this->registry = new Filter();
$query = $this->getRequest()->getParam('query', '');
$target = $this->getRequest()->getParam('filter_domain', '');
if ($this->getRequest()->getHeader('accept') == 'application/json') {
$this->getResponse()->setHeader('Content-Type', 'application/json');
$this->setupQueries(
$target,
$this->getParam('filter_module', '')
);
$this->_helper->json($this->parse($query, $target));
} else {
$this->setupQueries(
$target,
$this->getParam('filter_module')
);
$urlTarget = $this->parse($query, $target);
$this->redirect($urlTarget['urlParam']);
}
}
/**
* Set up the query handler for the given domain and module
*
* @param string $domain The domain to use
* @param string $module The module to use
*/
private function setupQueries($domain, $module = 'default')
{
$class = '\\Icinga\\Module\\' . ucfirst($module) . '\\Filter\\Registry';
$factory = strtolower($domain) . 'Filter';
$this->moduleRegistry = $class;
$this->registry->addDomain($class::$factory());
}
/**
* Parse the given query text and returns the json as expected by the semantic search box
*
* @param String $text The query to parse
* @return array The result structure to be returned in json format
*/
private function parse($text, $target)
{
try {
$queryTree = $this->registry->createQueryTreeForFilter($text);
$registry = $this->moduleRegistry;
return array(
'state' => 'success',
'proposals' => $this->registry->getProposalsForQuery($text),
'urlParam' => $registry::getUrlForTarget($target, $queryTree),
'valid' => count($this->registry->getIgnoredQueryParts()) === 0
);
} catch (\Exception $exc) {
Logger::error($exc);
$this->getResponse()->setHttpResponseCode(500);
return array(
'state' => 'error',
'message' => 'Search service is currently not available'
);
}
}
}

View File

@ -1,10 +1,13 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource; use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Data\Reducible;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Reducible;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserGroup\AddMemberForm; use Icinga\Forms\Config\UserGroup\AddMemberForm;
use Icinga\Forms\Config\UserGroup\UserGroupForm; use Icinga\Forms\Config\UserGroup\UserGroupForm;

View File

@ -1,10 +1,9 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers; namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController; use Icinga\Web\Controller\ActionController;
use Icinga\Application\Benchmark;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
@ -18,6 +17,7 @@ class IndexController extends ActionController
public function preDispatch() public function preDispatch()
{ {
if ($this->getRequest()->getActionName() !== 'welcome') { if ($this->getRequest()->getActionName() !== 'welcome') {
// @TODO(el): Avoid landing page redirects: https://dev.icinga.org/issues/9656
$this->redirectNow(Url::fromRequest()->setPath('dashboard')); $this->redirectNow(Url::fromRequest()->setPath('dashboard'));
} }
} }

View File

@ -1,10 +1,12 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\MenuRenderer; namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController; use Icinga\Web\Controller\ActionController;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Icinga\Web\Menu; use Icinga\Web\Menu;
use Icinga\Web\MenuRenderer;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**

View File

@ -1,6 +1,8 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Data\ConfigObject; use Icinga\Data\ConfigObject;
@ -11,8 +13,6 @@ use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat; use Icinga\Web\Widget\Tabextension\OutputFormat;
/** /**
* Class ListController
*
* Application wide controller for various listing actions * Application wide controller for various listing actions
*/ */
class ListController extends Controller class ListController extends Controller

View File

@ -1,16 +1,20 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config;
use Icinga\Data\ConfigObject;
use Icinga\Forms\PreferenceForm;
use Icinga\User\Preferences\PreferencesStore;
use Icinga\Web\Controller\BasePreferenceController; use Icinga\Web\Controller\BasePreferenceController;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tab;
use Icinga\Application\Config;
use Icinga\Forms\PreferenceForm;
use Icinga\Data\ConfigObject;
use Icinga\User\Preferences\PreferencesStore;
/** /**
* Application wide preference controller for user preferences * Application wide preference controller for user preferences
*
* @TODO(el): Rename to PreferencesController: https://dev.icinga.org/issues/10014
*/ */
class PreferenceController extends BasePreferenceController class PreferenceController extends BasePreferenceController
{ {

View File

@ -1,12 +1,21 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Exception\AlreadyExistsException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm; use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Security\RoleForm; use Icinga\Forms\Security\RoleForm;
use Icinga\Web\Controller\AuthBackendController; use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Notification; use Icinga\Web\Notification;
/**
* Manage user permissions and restrictions based on roles
*
* @TODO(el): Rename to RolesController: https://dev.icinga.org/issues/10015
*/
class RoleController extends AuthBackendController class RoleController extends AuthBackendController
{ {
/** /**
@ -31,7 +40,7 @@ class RoleController extends AuthBackendController
$values = $role->getValues(); $values = $role->getValues();
try { try {
$role->add($name, $values); $role->add($name, $values);
} catch (InvalidArgumentException $e) { } catch (AlreadyExistsException $e) {
$role->addError($e->getMessage()); $role->addError($e->getMessage());
return false; return false;
} }
@ -54,19 +63,11 @@ class RoleController extends AuthBackendController
/** /**
* Update a role * Update a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/ */
public function editAction() public function editAction()
{ {
$this->assertPermission('config/authentication/roles/edit'); $this->assertPermission('config/authentication/roles/edit');
$name = $this->_request->getParam('role'); $name = $this->params->getRequired('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
sprintf($this->translate('Required parameter \'%s\' missing'), 'role'),
400
);
}
$role = new RoleForm(); $role = new RoleForm();
$role->setTitle(sprintf($this->translate('Update Role %s'), $name)); $role->setTitle(sprintf($this->translate('Update Role %s'), $name));
$role->setSubmitLabel($this->translate('Update Role')); $role->setSubmitLabel($this->translate('Update Role'));
@ -74,11 +75,8 @@ class RoleController extends AuthBackendController
$role $role
->setIniConfig(Config::app('roles', true)) ->setIniConfig(Config::app('roles', true))
->load($name); ->load($name);
} catch (InvalidArgumentException $e) { } catch (NotFoundError $e) {
throw new Zend_Controller_Action_Exception( $this->httpNotFound($e->getMessage());
$e->getMessage(),
400
);
} }
$role $role
->setOnSuccess(function (RoleForm $role) use ($name) { ->setOnSuccess(function (RoleForm $role) use ($name) {
@ -87,7 +85,7 @@ class RoleController extends AuthBackendController
$values = $role->getValues(); $values = $role->getValues();
try { try {
$role->update($name, $values, $oldName); $role->update($name, $values, $oldName);
} catch (InvalidArgumentException $e) { } catch (NotFoundError $e) {
$role->addError($e->getMessage()); $role->addError($e->getMessage());
return false; return false;
} }
@ -105,35 +103,24 @@ class RoleController extends AuthBackendController
/** /**
* Remove a role * Remove a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/ */
public function removeAction() public function removeAction()
{ {
$this->assertPermission('config/authentication/roles/remove'); $this->assertPermission('config/authentication/roles/remove');
$name = $this->_request->getParam('role'); $name = $this->params->getRequired('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
sprintf($this->translate('Required parameter \'%s\' missing'), 'role'),
400
);
}
$role = new RoleForm(); $role = new RoleForm();
try { try {
$role $role
->setIniConfig(Config::app('roles', true)) ->setIniConfig(Config::app('roles', true))
->load($name); ->load($name);
} catch (InvalidArgumentException $e) { } catch (NotFoundError $e) {
throw new Zend_Controller_Action_Exception( $this->httpNotFound($e->getMessage());
$e->getMessage(),
400
);
} }
$confirmation = new ConfirmRemovalForm(array( $confirmation = new ConfirmRemovalForm(array(
'onSuccess' => function (ConfirmRemovalForm $confirmation) use ($name, $role) { 'onSuccess' => function (ConfirmRemovalForm $confirmation) use ($name, $role) {
try { try {
$role->remove($name); $role->remove($name);
} catch (InvalidArgumentException $e) { } catch (NotFoundError $e) {
Notification::error($e->getMessage()); Notification::error($e->getMessage());
return false; return false;
} }
@ -162,15 +149,15 @@ class RoleController extends AuthBackendController
$tabs->add( $tabs->add(
'role/list', 'role/list',
array( array(
'title' => $this->translate( 'baseTarget' => '_main',
'label' => $this->translate('Roles'),
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2' 'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
), ),
'label' => $this->translate('Roles'), 'url' => 'role/list'
'url' => 'role/list',
'baseTarget' => '_main'
) )
); );
return $tabs; return $tabs;
} }
} }

View File

@ -1,6 +1,8 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController; use Icinga\Web\Controller\ActionController;
use Icinga\Web\Widget; use Icinga\Web\Widget;
use Icinga\Web\Widget\SearchDashboard; use Icinga\Web\Widget\SearchDashboard;

View File

@ -1,16 +1,18 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController; namespace Icinga\Controllers;
use Icinga\Web\Controller;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Web\FileCache; use Icinga\Web\FileCache;
use Zend_Controller_Action_Exception as ActionException; use Icinga\Web\LessCompiler;
/** /**
* Delivery static content to clients * Delivery static content to clients
*/ */
class StaticController extends ActionController class StaticController extends Controller
{ {
/** /**
* Static routes don't require authentication * Static routes don't require authentication
@ -57,17 +59,15 @@ class StaticController extends ActionController
*/ */
public function imgAction() public function imgAction()
{ {
// TODO(el): I think this action only retrieves images from modules
$module = $this->_getParam('module_name'); $module = $this->_getParam('module_name');
$file = $this->_getParam('file'); $file = $this->_getParam('file');
$basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir(); $basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir();
$filePath = realpath($basedir . '/public/img/' . $file); $filePath = realpath($basedir . '/public/img/' . $file);
if (! $filePath || strpos($filePath, $basedir) !== 0) { if ($filePath === false) {
throw new ActionException(sprintf( $this->httpNotFound('%s does not exist', $filePath);
'%s does not exist',
$filePath
), 404);
} }
if (preg_match('/\.([a-z]+)$/i', $file, $m)) { if (preg_match('/\.([a-z]+)$/i', $file, $m)) {
$extension = $m[1]; $extension = $m[1];
@ -80,10 +80,7 @@ class StaticController extends ActionController
header(sprintf('ETag: "%x-%x-%x"', $s['ino'], $s['size'], (float) str_pad($s['mtime'], 16, '0'))); header(sprintf('ETag: "%x-%x-%x"', $s['ino'], $s['size'], (float) str_pad($s['mtime'], 16, '0')));
header('Cache-Control: public, max-age=3600'); header('Cache-Control: public, max-age=3600');
header('Pragma: cache'); header('Pragma: cache');
header('Last-Modified: ' . gmdate( header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $s['mtime']) . ' GMT');
'D, d M Y H:i:s',
$s['mtime']
) . ' GMT');
readfile($filePath); readfile($filePath);
} }
@ -100,7 +97,7 @@ class StaticController extends ActionController
$basedir = Icinga::app()->getApplicationDir('../public/js/icinga/components/'); $basedir = Icinga::app()->getApplicationDir('../public/js/icinga/components/');
$filePath = $basedir . $file; $filePath = $basedir . $file;
} else { } else {
if (!Icinga::app()->getModuleManager()->hasEnabled($module)) { if (! Icinga::app()->getModuleManager()->hasEnabled($module)) {
Logger::error( Logger::error(
'Non-existing frontend component "' . $module . '/' . $file 'Non-existing frontend component "' . $module . '/' . $file
. '" was requested. The module "' . $module . '" does not exist or is not active.' . '" was requested. The module "' . $module . '" does not exist or is not active.'
@ -112,7 +109,7 @@ class StaticController extends ActionController
$filePath = $basedir . '/public/js/' . $file; $filePath = $basedir . '/public/js/' . $file;
} }
if (!file_exists($filePath)) { if (! file_exists($filePath)) {
Logger::error( Logger::error(
'Non-existing frontend component "' . $module . '/' . $file 'Non-existing frontend component "' . $module . '/' . $file
. '" was requested, which would resolve to the the path: ' . $filePath . '" was requested, which would resolve to the the path: ' . $filePath
@ -122,41 +119,41 @@ class StaticController extends ActionController
} }
$response = $this->getResponse(); $response = $this->getResponse();
$response->setHeader('Content-Type', 'text/javascript'); $response->setHeader('Content-Type', 'text/javascript');
$this->setCacheHeader(3600); $this->setCacheHeader();
$response->setHeader( $response->setHeader(
'Last-Modified', 'Last-Modified',
gmdate( gmdate('D, d M Y H:i:s', filemtime($filePath)) . ' GMT'
'D, d M Y H:i:s',
filemtime($filePath)
) . ' GMT'
); );
readfile($filePath); readfile($filePath);
} }
/** /**
* Set cache header for this response * Set cache header for the response
* *
* @param integer $maxAge The maximum age to set * @param int $maxAge The maximum age to set
*/ */
private function setCacheHeader($maxAge) private function setCacheHeader($maxAge = 3600)
{ {
$this->_response->setHeader('Cache-Control', 'max-age=3600', true); $maxAge = (int) $maxAge;
$this->_response->setHeader('Pragma', 'cache', true); $this
$this->_response->setHeader( ->getResponse()
'Expires', ->setHeader('Cache-Control', sprintf('max-age=%d', $maxAge), true)
gmdate( ->setHeader('Pragma', 'cache', true)
'D, d M Y H:i:s', ->setHeader(
time()+3600 'Expires',
) . ' GMT', gmdate('D, d M Y H:i:s', time() + $maxAge) . ' GMT',
true true
); );
} }
/**
* Send application's and modules' CSS
*/
public function stylesheetAction() public function stylesheetAction()
{ {
$lessCompiler = new \Icinga\Web\LessCompiler(); $lessCompiler = new LessCompiler();
$moduleManager = Icinga::app()->getModuleManager(); $moduleManager = Icinga::app()->getModuleManager();
$publicDir = realpath(dirname($_SERVER['SCRIPT_FILENAME'])); $publicDir = realpath(dirname($_SERVER['SCRIPT_FILENAME']));
@ -172,7 +169,7 @@ class StaticController extends ActionController
} }
} }
$this->_response->setHeader('Content-Type', 'text/css'); $this->getResponse()->setHeader('Content-Type', 'text/css');
$this->setCacheHeader(3600); $this->setCacheHeader(3600);
$lessCompiler->printStack(); $lessCompiler->printStack();

View File

@ -1,12 +1,15 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\User\CreateMembershipForm; use Icinga\Forms\Config\User\CreateMembershipForm;
use Icinga\Forms\Config\User\UserForm; use Icinga\Forms\Config\User\UserForm;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\User; use Icinga\User;
use Icinga\Web\Controller\AuthBackendController; use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Form; use Icinga\Web\Form;

View File

@ -1,13 +1,15 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Config\UserGroup\UserGroupBackendForm; use Icinga\Forms\Config\UserGroup\UserGroupBackendForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Web\Controller; use Icinga\Web\Controller;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Url;
/** /**
* Controller to configure user group backends * Controller to configure user group backends

View File

@ -350,8 +350,21 @@ class ResourceConfigForm extends ConfigForm
'decorators' => array('ViewHelper') 'decorators' => array('ViewHelper')
) )
); );
$this->setAttrib('data-progress-element', 'resource-progress');
$this->addElement(
'note',
'resource-progress',
array(
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => 'resource-progress'))
)
)
);
$this->addDisplayGroup( $this->addDisplayGroup(
array('btn_submit', 'resource_validation'), array('btn_submit', 'resource_validation', 'resource-progress'),
'submit_validation', 'submit_validation',
array( array(
'decorators' => array( 'decorators' => array(

View File

@ -3,10 +3,11 @@
namespace Icinga\Forms\Security; namespace Icinga\Forms\Security;
use InvalidArgumentException;
use LogicException; use LogicException;
use Zend_Form_Element; use Zend_Form_Element;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Exception\AlreadyExistsException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm; use Icinga\Forms\ConfigForm;
use Icinga\Util\String; use Icinga\Util\String;
@ -168,6 +169,7 @@ class RoleForm extends ConfigForm
* @return $this * @return $this
* *
* @throws LogicException If the config is not set * @throws LogicException If the config is not set
* @throws NotFoundError If the given role does not exist
* @see ConfigForm::setConfig() For setting the config. * @see ConfigForm::setConfig() For setting the config.
*/ */
public function load($name) public function load($name)
@ -176,10 +178,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t load role \'%s\'. Config is not set', $name)); throw new LogicException(sprintf('Can\'t load role \'%s\'. Config is not set', $name));
} }
if (! $this->config->hasSection($name)) { if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf( throw new NotFoundError(
$this->translate('Can\'t load role \'%s\'. Role does not exist'), $this->translate('Can\'t load role \'%s\'. Role does not exist'),
$name $name
)); );
} }
$role = $this->config->getSection($name)->toArray(); $role = $this->config->getSection($name)->toArray();
$role['permissions'] = ! empty($role['permissions']) $role['permissions'] = ! empty($role['permissions'])
@ -202,14 +204,14 @@ class RoleForm extends ConfigForm
/** /**
* Add a role * Add a role
* *
* @param string $name The name of the role * @param string $name The name of the role
* @param array $values * @param array $values
* *
* @return $this * @return $this
* *
* @throws LogicException If the config is not set * @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role to add already exists * @throws AlreadyExistsException If the role to add already exists
* @see ConfigForm::setConfig() For setting the config. * @see ConfigForm::setConfig() For setting the config.
*/ */
public function add($name, array $values) public function add($name, array $values)
{ {
@ -217,10 +219,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t add role \'%s\'. Config is not set', $name)); throw new LogicException(sprintf('Can\'t add role \'%s\'. Config is not set', $name));
} }
if ($this->config->hasSection($name)) { if ($this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf( throw new AlreadyExistsException(
$this->translate('Can\'t add role \'%s\'. Role already exists'), $this->translate('Can\'t add role \'%s\'. Role already exists'),
$name $name
)); );
} }
$this->config->setSection($name, $values); $this->config->setSection($name, $values);
return $this; return $this;
@ -229,13 +231,13 @@ class RoleForm extends ConfigForm
/** /**
* Remove a role * Remove a role
* *
* @param string $name The name of the role * @param string $name The name of the role
* *
* @return $this * @return $this
* *
* @throws LogicException If the config is not set * @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role does not exist * @throws NotFoundError If the role does not exist
* @see ConfigForm::setConfig() For setting the config. * @see ConfigForm::setConfig() For setting the config.
*/ */
public function remove($name) public function remove($name)
{ {
@ -243,10 +245,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t remove role \'%s\'. Config is not set', $name)); throw new LogicException(sprintf('Can\'t remove role \'%s\'. Config is not set', $name));
} }
if (! $this->config->hasSection($name)) { if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf( throw new NotFoundError(
$this->translate('Can\'t remove role \'%s\'. Role does not exist'), $this->translate('Can\'t remove role \'%s\'. Role does not exist'),
$name $name
)); );
} }
$this->config->removeSection($name); $this->config->removeSection($name);
return $this; return $this;
@ -255,15 +257,15 @@ class RoleForm extends ConfigForm
/** /**
* Update a role * Update a role
* *
* @param string $name The possibly new name of the role * @param string $name The possibly new name of the role
* @param array $values * @param array $values
* @param string $oldName The name of the role to update * @param string $oldName The name of the role to update
* *
* @return $this * @return $this
* *
* @throws LogicException If the config is not set * @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role to update does not exist * @throws NotFoundError If the role to update does not exist
* @see ConfigForm::setConfig() For setting the config. * @see ConfigForm::setConfig() For setting the config.
*/ */
public function update($name, array $values, $oldName) public function update($name, array $values, $oldName)
{ {
@ -276,10 +278,10 @@ class RoleForm extends ConfigForm
$this->add($name, $values); $this->add($name, $values);
} else { } else {
if (! $this->config->hasSection($name)) { if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf( throw new NotFoundError(
$this->translate('Can\'t update role \'%s\'. Role does not exist'), $this->translate('Can\'t update role \'%s\'. Role does not exist'),
$name $name
)); );
} }
$this->config->setSection($name, $values); $this->config->setSection($name, $values);
} }

View File

@ -78,9 +78,9 @@ abstract class ApplicationBootstrap
protected $configDir; protected $configDir;
/** /**
* Icinga auto loader * Icinga class loader
* *
* @var Loader * @var ClassLoader
*/ */
private $loader; private $loader;
@ -183,7 +183,7 @@ abstract class ApplicationBootstrap
/** /**
* Getter for class loader * Getter for class loader
* *
* @return Loader * @return ClassLoader
*/ */
public function getLoader() public function getLoader()
{ {
@ -339,15 +339,15 @@ abstract class ApplicationBootstrap
} }
/** /**
* Setup Icinga auto loader * Setup Icinga class loader
* *
* @return $this * @return $this
*/ */
public function setupAutoloader() public function setupAutoloader()
{ {
require $this->libDir . '/Icinga/Application/Loader.php'; require $this->libDir . '/Icinga/Application/ClassLoader.php';
$this->loader = new Loader(); $this->loader = new ClassLoader();
$this->loader->registerNamespace('Icinga', $this->libDir. '/Icinga'); $this->loader->registerNamespace('Icinga', $this->libDir. '/Icinga');
$this->loader->register(); $this->loader->register();

View File

@ -0,0 +1,113 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application;
/**
* PSR-4 class loader
*/
class ClassLoader
{
/**
* Namespace separator
*/
const NAMESPACE_SEPARATOR = '\\';
/**
* Namespaces
*
* @var array
*/
private $namespaces = array();
/**
* Register a base directory for a namespace prefix
*
* @param string $namespace
* @param string $directory
*
* @return $this
*/
public function registerNamespace($namespace, $directory)
{
$this->namespaces[$namespace] = $directory;
return $this;
}
/**
* Test whether a namespace exists
*
* @param string $namespace
*
* @return bool
*/
public function hasNamespace($namespace)
{
return array_key_exists($namespace, $this->namespaces);
}
/**
* Get the source file of the given class or interface
*
* @param string $class Name of the class or interface
*
* @return string|null
*/
public function getSourceFile($class)
{
foreach ($this->namespaces as $namespace => $dir) {
if ($class === strstr($class, $namespace)) {
$classPath = str_replace(
self::NAMESPACE_SEPARATOR,
DIRECTORY_SEPARATOR,
substr($class, strlen($namespace))
) . '.php';
if (file_exists($file = $dir . $classPath)) {
return $file;
}
}
}
return null;
}
/**
* Load the given class or interface
*
* @param string $class Name of the class or interface
*
* @return bool Whether the class or interface has been loaded
*/
public function loadClass($class)
{
if ($file = $this->getSourceFile($class)) {
require $file;
return true;
}
return false;
}
/**
* Register {@link loadClass()} as an autoloader
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Unregister {@link loadClass()} as an autoloader
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Unregister this as an autoloader
*/
public function __destruct()
{
$this->unregister();
}
}

View File

@ -1,138 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application;
use Icinga\Exception\ProgrammingError;
class Loader
{
/**
* Namespace separator
*/
const NAMESPACE_SEPARATOR = '\\';
/**
* List of namespaces
*
* @var array
*/
private $namespaces = array();
/**
* Detach spl autoload method from stack
*/
public function __destruct()
{
$this->unRegister();
}
/**
* Register new namespace for directory
*
* @param string $namespace
* @param string $directory
*
* @throws ProgrammingError
*/
public function registerNamespace($namespace, $directory)
{
if (!is_dir($directory)) {
throw new ProgrammingError(
'Directory "%s" for namespace "%s" does not exist',
$directory,
$namespace
);
}
$this->namespaces[$namespace] = $directory;
}
/**
* Test if a namespace exists
*
* @param string $namespace
*
* @return bool
*/
public function hasNamespace($namespace)
{
return array_key_exists($namespace, $this->namespaces);
}
/**
* Class loader
*
* Ignores all but classes in registered namespaces.
*
* @param string $class
*
* @return boolean
*/
public function loadClass($class)
{
$namespace = $this->getNamespaceForClass($class);
if ($namespace) {
$file = $this->namespaces[$namespace] . preg_replace('/^' . preg_quote($namespace) . '/', '', $class);
$file = str_replace(self::NAMESPACE_SEPARATOR, '/', $file) . '.php';
if (@file_exists($file)) {
require_once $file;
return true;
}
}
return false;
}
/**
* Test if we have a registered namespaces for this class
*
* Return is the longest match in the array found
*
* @param string $className
*
* @return bool|string
*/
private function getNamespaceForClass($className)
{
$testNamespace = '';
$testLength = 0;
foreach (array_keys($this->namespaces) as $namespace) {
$stub = preg_replace(
'/^' . preg_quote($namespace) . '(' . preg_quote(self::NAMESPACE_SEPARATOR) . '|$)/', '', $className
);
$length = strlen($className) - strlen($stub);
if ($length > $testLength) {
$testLength = $length;
$testNamespace = $namespace;
}
}
if ($testLength > 0) {
return $testNamespace;
}
return false;
}
/**
* Effectively registers the autoloader the PHP/SPL way
*/
public function register()
{
// Think about to add class pathes to php include path
// this could be faster (tg)
spl_autoload_register(array(&$this, 'loadClass'));
}
/**
* Detach autoloader from spl registration
*/
public function unRegister()
{
spl_autoload_unregister(array(&$this, 'loadClass'));
}
}

View File

@ -4,23 +4,24 @@
namespace Icinga\Application\Modules; namespace Icinga\Application\Modules;
use Exception; use Exception;
use Zend_Controller_Router_Route;
use Zend_Controller_Router_Route_Abstract; use Zend_Controller_Router_Route_Abstract;
use Zend_Controller_Router_Route as Route; use Zend_Controller_Router_Route_Regex;
use Zend_Controller_Router_Route_Regex as RegexRoute;
use Icinga\Application\ApplicationBootstrap; use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Data\ConfigObject; use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Util\Translator; use Icinga\Util\Translator;
use Icinga\Web\Controller\Dispatcher;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Icinga\Web\Menu; use Icinga\Web\Menu;
use Icinga\Web\Widget; use Icinga\Web\Widget;
use Icinga\Web\Widget\Dashboard\Pane; use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Exception\ProgrammingError;
use Icinga\Exception\IcingaException;
/** /**
* Module handling * Module handling
@ -188,7 +189,7 @@ class Module
/** /**
* A set of menu elements * A set of menu elements
* *
* @var array * @var Menu[]
*/ */
protected $menuItems = array(); protected $menuItems = array();
@ -770,6 +771,7 @@ class Module
{ {
$this->launchConfigScript(); $this->launchConfigScript();
$tabs = Widget::create('tabs'); $tabs = Widget::create('tabs');
/** @var \Icinga\Web\Widget\Tabs $tabs */
$tabs->add('info', array( $tabs->add('info', array(
'url' => 'config/module', 'url' => 'config/module',
'urlParams' => array('name' => $this->getName()), 'urlParams' => array('name' => $this->getName()),
@ -934,7 +936,7 @@ class Module
} }
/** /**
* Register module namespaces on the autoloader * Register module namespaces on our class loader
* *
* @return $this * @return $this
*/ */
@ -944,16 +946,17 @@ class Module
return $this; return $this;
} }
$loader = $this->app->getLoader();
$moduleName = ucfirst($this->getName()); $moduleName = ucfirst($this->getName());
$moduleLibraryDir = $this->getLibDir(). '/'. $moduleName; $moduleLibraryDir = $this->getLibDir(). '/'. $moduleName;
if (is_dir($moduleLibraryDir)) { if (is_dir($moduleLibraryDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName, $moduleLibraryDir); $loader->registerNamespace('Icinga\\Module\\' . $moduleName, $moduleLibraryDir);
} }
$moduleFormDir = $this->getFormDir(); $moduleFormDir = $this->getFormDir();
if (is_dir($moduleFormDir)) { if (is_dir($moduleFormDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName. '\\Forms', $moduleFormDir); $loader->registerNamespace('Icinga\\Module\\' . $moduleName. '\\Forms', $moduleFormDir);
} }
$this->registeredAutoloader = true; $this->registeredAutoloader = true;
@ -1015,19 +1018,23 @@ class Module
*/ */
protected function registerWebIntegration() protected function registerWebIntegration()
{ {
if (!$this->app->isWeb()) { if (! $this->app->isWeb()) {
return $this; return $this;
} }
$moduleControllerDir = $this->getControllerDir();
if (file_exists($this->controllerdir) && is_dir($this->controllerdir)) { if (is_dir($moduleControllerDir)) {
$this->app->getfrontController()->addControllerDirectory( $this->app->getfrontController()->addControllerDirectory(
$this->controllerdir, $moduleControllerDir,
$this->name $this->getName()
);
$this->app->getLoader()->registerNamespace(
'Icinga\\Module\\' . ucfirst($this->getName()) . '\\' . Dispatcher::CONTROLLER_NAMESPACE,
$moduleControllerDir
); );
} }
$this
$this->registerLocales() ->registerLocales()
->registerRoutes(); ->registerRoutes();
return $this; return $this;
} }
@ -1039,27 +1046,30 @@ class Module
protected function registerRoutes() protected function registerRoutes()
{ {
$router = $this->app->getFrontController()->getRouter(); $router = $this->app->getFrontController()->getRouter();
/** @var \Zend_Controller_Router_Rewrite $router */
foreach ($this->routes as $name => $route) { foreach ($this->routes as $name => $route) {
$router->addRoute($name, $route); $router->addRoute($name, $route);
} }
$router->addRoute( $router->addRoute(
$this->name . '_jsprovider', $this->name . '_jsprovider',
new Route( new Zend_Controller_Router_Route(
'js/' . $this->name . '/:file', 'js/' . $this->name . '/:file',
array( array(
'action' => 'javascript',
'controller' => 'static', 'controller' => 'static',
'action' =>'javascript', 'module' => 'default',
'module_name' => $this->name 'module_name' => $this->name
) )
) )
); );
$router->addRoute( $router->addRoute(
$this->name . '_img', $this->name . '_img',
new RegexRoute( new Zend_Controller_Router_Route_Regex(
'img/' . $this->name . '/(.+)', 'img/' . $this->name . '/(.+)',
array( array(
'controller' => 'static',
'action' => 'img', 'action' => 'img',
'controller' => 'static',
'module' => 'default',
'module_name' => $this->name 'module_name' => $this->name
), ),
array( array(

View File

@ -3,12 +3,15 @@
namespace Icinga\Application; namespace Icinga\Application;
/**
* Retrieve the version of Icinga Web 2
*/
class Version class Version
{ {
/** /**
* Get the version of this instance of Icinga Web 2 * Get the version of this instance of Icinga Web 2
* *
* @return array|bool array on success, false otherwise * @return array|false array on success, false otherwise
*/ */
public static function get() public static function get()
{ {

View File

@ -15,6 +15,7 @@ use Icinga\Authentication\Auth;
use Icinga\User; use Icinga\User;
use Icinga\Util\TimezoneDetect; use Icinga\Util\TimezoneDetect;
use Icinga\Util\Translator; use Icinga\Util\Translator;
use Icinga\Web\Controller\Dispatcher;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Session; use Icinga\Web\Session;
use Icinga\Web\Session\Session as BaseSession; use Icinga\Web\Session\Session as BaseSession;
@ -88,7 +89,7 @@ class Web extends EmbeddedWeb
->setupLogger() ->setupLogger()
->setupInternationalization() ->setupInternationalization()
->setupZendMvc() ->setupZendMvc()
->setupFormNamespace() ->setupNamespaces()
->setupModuleManager() ->setupModuleManager()
->setupUserBackendFactory() ->setupUserBackendFactory()
->loadSetupModuleIfNecessary() ->loadSetupModuleIfNecessary()
@ -210,6 +211,7 @@ class Web extends EmbeddedWeb
private function setupFrontController() private function setupFrontController()
{ {
$this->frontController = Zend_Controller_Front::getInstance(); $this->frontController = Zend_Controller_Front::getInstance();
$this->frontController->setDispatcher(new Dispatcher());
$this->frontController->setRequest($this->getRequest()); $this->frontController->setRequest($this->getRequest());
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers')); $this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
@ -306,16 +308,22 @@ class Web extends EmbeddedWeb
} }
/** /**
* Setup an autoloader namespace for Icinga\Forms * Setup class loader namespaces for Icinga\Controllers and Icinga\Forms
* *
* @return $this * @return $this
*/ */
private function setupFormNamespace() private function setupNamespaces()
{ {
$this->getLoader()->registerNamespace( $this
'Icinga\\Forms', ->getLoader()
$this->getApplicationDir('forms') ->registerNamespace(
); 'Icinga\\' . Dispatcher::CONTROLLER_NAMESPACE,
$this->getApplicationDir('controllers')
)
->registerNamespace(
'Icinga\\Forms',
$this->getApplicationDir('forms')
);
return $this; return $this;
} }
} }

View File

@ -3,11 +3,9 @@
namespace Icinga\Application; namespace Icinga\Application;
use Icinga\Application\EmbeddedWeb;
use Icinga\Application\Web;
use Icinga\Web\StyleSheet;
use Icinga\Web\JavaScript;
use Icinga\Chart\Inline\PieChart; use Icinga\Chart\Inline\PieChart;
use Icinga\Web\JavaScript;
use Icinga\Web\StyleSheet;
error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);

View File

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

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception;
/**
* Exception thrown if something to add already exists
*/
class AlreadyExistsException extends IcingaException
{
}

View File

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

View File

@ -8,26 +8,24 @@ use Icinga\Application\Icinga;
use Icinga\Web\Widget\Tabs; use Icinga\Web\Widget\Tabs;
/** /**
* Static helper class that collects tabs provided by the 'createProvidedTabs' method * Static helper class that collects tabs provided by the 'createProvidedTabs' method of controllers
* of controllers.
*/ */
class ControllerTabCollector class ControllerTabCollector
{ {
/** /**
* Scan all controllers with the provided name * Scan all controllers with given name in the application and (loaded) module folders and collects their provided
* in the application and (loaded) module folders and collects their provided tabs * tabs
* *
* @param string $controller The name of the controllers to use for tab collection * @param string $controllerName The name of the controllers to use for tab collection
* *
* @return Tabs A @see Tabs instance containing the application tabs first * @return Tabs A {@link Tabs} instance containing the application tabs first followed by the
* followed by the tabs provided from the modules * tabs provided from the modules
*/ */
public static function collectControllerTabs($controller) public static function collectControllerTabs($controllerName)
{ {
require_once(Icinga::app()->getApplicationDir('/controllers/'.$controller.'.php')); $controller = '\Icinga\\' . Dispatcher::CONTROLLER_NAMESPACE . '\\' . $controllerName;
$applicationTabs = $controller::createProvidedTabs(); $applicationTabs = $controller::createProvidedTabs();
$moduleTabs = self::collectModuleTabs($controller); $moduleTabs = self::collectModuleTabs($controllerName);
$tabs = new Tabs(); $tabs = new Tabs();
foreach ($applicationTabs as $name => $tab) { foreach ($applicationTabs as $name => $tab) {
@ -35,7 +33,7 @@ class ControllerTabCollector
} }
foreach ($moduleTabs as $name => $tab) { foreach ($moduleTabs as $name => $tab) {
// don't overwrite application tabs if the module wants to // Don't overwrite application tabs if the module wants to
if ($tabs->has($name)) { if ($tabs->has($name)) {
continue; continue;
} }
@ -66,29 +64,30 @@ class ControllerTabCollector
/** /**
* Collects the tabs from the createProvidedTabs() method in the configuration controller * Collects the tabs from the createProvidedTabs() method in the configuration controller
* *
* If the module doesn't have the given controller or createProvidedTabs method in the controller * If the module doesn't have the given controller or createProvidedTabs method in the controller an empty array
* an empty array will be returned * will be returned
* *
* @param string $controller The name of the controller that provides tabs via createProvidedTabs * @param string $controllerName The name of the controller that provides tabs via createProvidedTabs
* @param Module $module The module instance that provides the controller * @param Module $module The module instance that provides the controller
* *
* @return array * @return array
*/ */
private static function createModuleConfigurationTabs($controller, Module $module) private static function createModuleConfigurationTabs($controllerName, Module $module)
{ {
// TODO(el): Only works for controllers w/o namepsace: https://dev.icinga.org/issues/4149
$controllerDir = $module->getControllerDir(); $controllerDir = $module->getControllerDir();
$name = $module->getName(); $name = $module->getName();
$controllerDir = $controllerDir . '/' . $controller . '.php'; $controllerDir = $controllerDir . '/' . $controllerName . '.php';
$controllerName = ucfirst($name) . '_' . $controller; $controllerName = ucfirst($name) . '_' . $controllerName;
if (is_readable($controllerDir)) { if (is_readable($controllerDir)) {
require_once(realpath($controllerDir)); require_once(realpath($controllerDir));
if (!method_exists($controllerName, "createProvidedTabs")) { if (! method_exists($controllerName, 'createProvidedTabs')) {
return array(); return array();
} }
$tab = $controllerName::createProvidedTabs(); $tab = $controllerName::createProvidedTabs();
if (!is_array($tab)) { if (! is_array($tab)) {
$tab = array($name => $tab); $tab = array($name => $tab);
} }
return $tab; return $tab;

View File

@ -0,0 +1,92 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Controller;
use Exception;
use Zend_Controller_Action;
use Zend_Controller_Action_Interface;
use Zend_Controller_Dispatcher_Exception;
use Zend_Controller_Dispatcher_Standard;
use Zend_Controller_Request_Abstract;
use Zend_Controller_Response_Abstract;
/**
* Dispatcher supporting Zend-style and namespaced controllers
*
* Does not support a namespaced default controller in combination w/ the Zend parameter useDefaultControllerAlways.
*/
class Dispatcher extends Zend_Controller_Dispatcher_Standard
{
/**
* Controller namespace
*
* @var string
*/
const CONTROLLER_NAMESPACE = 'Controllers';
/**
* Dispatch request to a controller and action
*
* @param Zend_Controller_Request_Abstract $request
* @param Zend_Controller_Response_Abstract $response
*
* @throws Zend_Controller_Dispatcher_Exception If the controller is not an instance of
* Zend_Controller_Action_Interface
* @throws Exception If dispatching the request fails
*/
public function dispatch(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response)
{
$this->setResponse($response);
$controllerName = $request->getControllerName();
if (! $controllerName) {
parent::dispatch($request, $response);
return;
}
$controllerName = ucfirst($controllerName) . 'Controller';
$moduleName = $request->getModuleName();
if ($moduleName === null || $moduleName === $this->_defaultModule) {
$controllerClass = 'Icinga\\' . self::CONTROLLER_NAMESPACE . '\\' . $controllerName;
} else {
$controllerClass = 'Icinga\\Module\\' . ucfirst($moduleName) . '\\' . self::CONTROLLER_NAMESPACE . '\\'
. $controllerName;
}
if (! class_exists($controllerClass)) {
parent::dispatch($request, $response);
return;
}
$controller = new $controllerClass($request, $response, $this->getParams());
if (! $controller instanceof Zend_Controller_Action
&& ! $controller instanceof Zend_Controller_Action_Interface
) {
throw new Zend_Controller_Dispatcher_Exception(
'Controller "' . $controllerClass . '" is not an instance of Zend_Controller_Action_Interface'
);
}
$action = $this->getActionMethod($request);
$request->setDispatched(true);
// Buffer output by default
$disableOb = $this->getParam('disableOutputBuffering');
$obLevel = ob_get_level();
if (empty($disableOb)) {
ob_start();
}
try {
$controller->dispatch($action);
} catch (Exception $e) {
// Clean output buffer on error
$curObLevel = ob_get_level();
if ($curObLevel > $obLevel) {
do {
ob_get_clean();
$curObLevel = ob_get_level();
} while ($curObLevel > $obLevel);
}
throw $e;
}
if (empty($disableOb)) {
$content = ob_get_clean();
$response->appendBody($content);
}
}
}

View File

@ -1012,6 +1012,8 @@ class Form extends Zend_Form
* Populate the elements with the given values * Populate the elements with the given values
* *
* @param array $defaults The values to populate the elements with * @param array $defaults The values to populate the elements with
*
* @return $this
*/ */
public function populate(array $defaults) public function populate(array $defaults)
{ {

View File

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

View File

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

View File

@ -1,17 +1,19 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Doc\Controllers;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Module\Doc\DocController; use Icinga\Module\Doc\DocController;
class Doc_IcingawebController extends DocController class IcingawebController extends DocController
{ {
/** /**
* Get the path to Icinga Web 2's documentation * Get the path to Icinga Web 2's documentation
* *
* @return string * @return string
* *
* @throws Zend_Controller_Action_Exception If Icinga Web 2's documentation is not available * @throws \Icinga\Exception\Http\HttpNotFoundException If Icinga Web 2's documentation is not available
*/ */
protected function getPath() protected function getPath()
{ {

View File

@ -1,10 +1,20 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Doc\Controllers;
use Icinga\Module\Doc\DocController; use Icinga\Module\Doc\DocController;
class Doc_IndexController extends DocController /**
* Documentation module index
*/
class IndexController extends DocController
{ {
/**
* Documentation module landing page
*
* Lists documentation links
*/
public function indexAction() public function indexAction()
{ {
} }

View File

@ -1,12 +1,13 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Doc\Controllers;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Module\Doc\DocController; use Icinga\Module\Doc\DocController;
use Icinga\Module\Doc\Exception\DocException; use Icinga\Module\Doc\Exception\DocException;
use Icinga\File\Ini\Parser;
class Doc_ModuleController extends DocController class ModuleController extends DocController
{ {
/** /**
* Get the path to a module documentation * Get the path to a module documentation

View File

@ -1,15 +1,17 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Doc\Controllers;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Module\Doc\DocController; use Icinga\Module\Doc\DocController;
use Icinga\Module\Doc\DocParser; use Icinga\Module\Doc\DocParser;
use Icinga\Module\Doc\Exception\DocException; use Icinga\Module\Doc\Exception\DocException;
use Icinga\Module\Doc\Renderer\DocSearchRenderer;
use Icinga\Module\Doc\Search\DocSearch; use Icinga\Module\Doc\Search\DocSearch;
use Icinga\Module\Doc\Search\DocSearchIterator; use Icinga\Module\Doc\Search\DocSearchIterator;
use Icinga\Module\Doc\Renderer\DocSearchRenderer;
class Doc_SearchController extends DocController class SearchController extends DocController
{ {
/** /**
* Render search * Render search
@ -77,9 +79,7 @@ class Doc_SearchController extends DocController
/** /**
* Get the path to Icinga Web 2's documentation * Get the path to Icinga Web 2's documentation
* *
* @return string * @return string
*
* @throws Zend_Controller_Action_Exception If Icinga Web 2's documentation is not available
*/ */
protected function getWebPath() protected function getWebPath()
{ {

View File

@ -1,11 +1,13 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Doc\Controllers;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Web\Controller; use Icinga\Web\Controller;
use Icinga\Web\Widget; use Icinga\Web\Widget;
class Doc_StyleController extends Controller class StyleController extends Controller
{ {
public function guideAction() public function guideAction()
{ {

View File

@ -1,15 +1,23 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use stdClass;
use DateInterval;
use DatePeriod;
use DateTime;
use Zend_Controller_Action_Exception;
use Icinga\Chart\GridChart; use Icinga\Chart\GridChart;
use Icinga\Chart\Unit\LinearUnit; use Icinga\Chart\Unit\LinearUnit;
use Icinga\Chart\Unit\StaticAxis; use Icinga\Chart\Unit\StaticAxis;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Web\Widget\SelectBox; use Icinga\Module\Monitoring\Web\Widget\SelectBox;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
class Monitoring_AlertsummaryController extends Controller class AlertsummaryController extends Controller
{ {
/** /**
* @var array * @var array
@ -95,7 +103,7 @@ class Monitoring_AlertsummaryController extends Controller
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'notification_start_time', 'notification_start_time',
'>=', '>=',
$this->getBeginDate($interval)->format('Y-m-d H:i:s') $this->getBeginDate($interval)->format('Y-m-d H:i:s')
@ -144,7 +152,7 @@ class Monitoring_AlertsummaryController extends Controller
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'notification_start_time', 'notification_start_time',
'>=', '>=',
$beginDate->format('Y-m-d H:i:s') $beginDate->format('Y-m-d H:i:s')
@ -212,7 +220,7 @@ class Monitoring_AlertsummaryController extends Controller
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'notification_start_time', 'notification_start_time',
'>=', '>=',
$this->getBeginDate($interval)->format('Y-m-d H:i:s') $this->getBeginDate($interval)->format('Y-m-d H:i:s')
@ -263,7 +271,7 @@ class Monitoring_AlertsummaryController extends Controller
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'timestamp', 'timestamp',
'>=', '>=',
$this->getBeginDate($interval)->getTimestamp() $this->getBeginDate($interval)->getTimestamp()
@ -271,7 +279,7 @@ class Monitoring_AlertsummaryController extends Controller
); );
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'state', 'state',
'>', '>',
0 0
@ -329,7 +337,7 @@ class Monitoring_AlertsummaryController extends Controller
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$query->addFilter( $query->addFilter(
new Icinga\Data\Filter\FilterExpression( new FilterExpression(
'notification_start_time', 'notification_start_time',
'>=', '>=',
$this->getBeginDate($interval)->format('Y-m-d H:i:s') $this->getBeginDate($interval)->format('Y-m-d H:i:s')

View File

@ -1,20 +1,18 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Controller; namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Chart\GridChart; use Icinga\Chart\GridChart;
use Icinga\Chart\PieChart; use Icinga\Chart\PieChart;
use Icinga\Chart\Unit\StaticAxis;
use Icinga\Chart\Unit\LogarithmicUnit; use Icinga\Chart\Unit\LogarithmicUnit;
use Icinga\Chart\Unit\LinearUnit; use Icinga\Chart\Unit\StaticAxis;
use Icinga\Module\Monitoring\Controller;
/** /**
* Class Monitoring_CommandController * @TODO(el): Fix and reuse the controller or remove it: https://dev.icinga.org/issues/10019
*
* Interface to send commands and display forms
*/ */
class ChartController extends Controller
class Monitoring_ChartController extends Controller
{ {
private function drawLogChart1() private function drawLogChart1()
{ {

View File

@ -1,6 +1,8 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Web\Url; use Icinga\Web\Url;
@ -9,12 +11,12 @@ use Icinga\Web\Widget\Tabextension\DashboardAction;
/** /**
* Display detailed information about a comment * Display detailed information about a comment
*/ */
class Monitoring_CommentController extends Controller class CommentController extends Controller
{ {
/** /**
* The fetched comment * The fetched comment
* *
* @var stdClass * @var object
*/ */
protected $comment; protected $comment;
@ -41,19 +43,16 @@ class Monitoring_CommentController extends Controller
))->where('comment_internal_id', $commentId); ))->where('comment_internal_id', $commentId);
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->comment = $query->getQuery()->fetchRow(); if (false === $this->comment = $query->fetchRow()) {
if ($this->comment === false) {
$this->httpNotFound($this->translate('Comment not found')); $this->httpNotFound($this->translate('Comment not found'));
} }
$this->getTabs()->add( $this->getTabs()->add(
'comment', 'comment',
array( array(
'title' => $this->translate( 'icon' => 'comment',
'Display detailed information about a comment.'
),
'icon' => 'comment',
'label' => $this->translate('Comment'), 'label' => $this->translate('Comment'),
'title' => $this->translate('Display detailed information about a comment.'),
'url' =>'monitoring/comments/show' 'url' =>'monitoring/comments/show'
) )
)->activate('comment')->extend(new DashboardAction()); )->activate('comment')->extend(new DashboardAction());
@ -64,37 +63,19 @@ class Monitoring_CommentController extends Controller
*/ */
public function showAction() public function showAction()
{ {
$listCommentsLink = Url::fromPath('monitoring/list/comments')
->setQueryString('comment_type=(comment|ack)');
$this->view->comment = $this->comment; $this->view->comment = $this->comment;
if ($this->hasPermission('monitoring/command/comment/delete')) { if ($this->hasPermission('monitoring/command/comment/delete')) {
$this->view->delCommentForm = $this->createDelCommentForm(); $listUrl = Url::fromPath('monitoring/list/comments')->setQueryString('comment_type=(comment|ack)');
$this->view->delCommentForm->populate( $form = new DeleteCommentCommandForm();
array( $form
'redirect' => $listCommentsLink, ->populate(array(
'comment_id' => $this->comment->id, 'comment_id' => $this->comment->id,
'comment_is_service' => isset($this->comment->service_description) 'comment_is_service' => isset($this->comment->service_description),
) 'redirect' => $listUrl
); ))
->handleRequest();
$this->view->delCommentForm = $form;
} }
} }
/**
* Create a command form to delete a single comment
*
* @return DeleteCommentsCommandForm
*/
private function createDelCommentForm()
{
$this->assertPermission('monitoring/command/comment/delete');
$delCommentForm = new DeleteCommentCommandForm();
$delCommentForm->setAction(
Url::fromPath('monitoring/comment/show')
->setParam('comment_id', $this->comment->id)
);
$delCommentForm->handleRequest();
return $delCommentForm;
}
} }

View File

@ -1,34 +1,41 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Data\Filter\Filter;
/** /**
* Display detailed information about a comment * Display detailed information about comments
*/ */
class Monitoring_CommentsController extends Controller class CommentsController extends Controller
{ {
/** /**
* The fetched comments * The comments view
* *
* @var array * @var \Icinga\Module\Monitoring\DataView\Comment
*/ */
protected $comments; protected $comments;
/** /**
* Fetch all comments matching the current filter and add tabs * Filter from request
* *
* @throws Zend_Controller_Action_Exception * @var Filter
*/
protected $filter;
/**
* Fetch all comments matching the current filter and add tabs
*/ */
public function init() public function init()
{ {
$this->filter = Filter::fromQueryString(str_replace( $this->filter = Filter::fromQueryString(str_replace(
'comment_id', 'comment_id',
'comment_internal_id', 'comment_internal_id',
(string)$this->params (string) $this->params
)); ));
$query = $this->backend->select()->from('comment', array( $query = $this->backend->select()->from('comment', array(
'id' => 'comment_internal_id', 'id' => 'comment_internal_id',
@ -46,19 +53,16 @@ class Monitoring_CommentsController extends Controller
))->addFilter($this->filter); ))->addFilter($this->filter);
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->comments = $query->getQuery()->fetchAll(); $this->comments = $query;
if (false === $this->comments) {
throw new Zend_Controller_Action_Exception($this->translate('Comment not found'));
}
$this->getTabs()->add( $this->getTabs()->add(
'comments', 'comments',
array( array(
'icon' => 'comment',
'label' => $this->translate('Comments') . sprintf(' (%d)', $query->count()),
'title' => $this->translate( 'title' => $this->translate(
'Display detailed information about multiple comments.' 'Display detailed information about multiple comments.'
), ),
'icon' => 'comment',
'label' => $this->translate('Comments') . sprintf(' (%d)', count($this->comments)),
'url' =>'monitoring/comments/show' 'url' =>'monitoring/comments/show'
) )
)->activate('comments'); )->activate('comments');
@ -71,9 +75,9 @@ class Monitoring_CommentsController extends Controller
{ {
$this->view->comments = $this->comments; $this->view->comments = $this->comments;
$this->view->listAllLink = Url::fromPath('monitoring/list/comments') $this->view->listAllLink = Url::fromPath('monitoring/list/comments')
->setQueryString($this->filter->toQueryString()); ->setQueryString($this->filter->toQueryString());
$this->view->removeAllLink = Url::fromPath('monitoring/comments/delete-all') $this->view->removeAllLink = Url::fromPath('monitoring/comments/delete-all')
->setParams($this->params); ->setParams($this->params);
} }
/** /**
@ -89,14 +93,14 @@ class Monitoring_CommentsController extends Controller
$delCommentForm->setTitle($this->view->translate('Remove all Comments')); $delCommentForm->setTitle($this->view->translate('Remove all Comments'));
$delCommentForm->addDescription(sprintf( $delCommentForm->addDescription(sprintf(
$this->translate('Confirm removal of %d comments.'), $this->translate('Confirm removal of %d comments.'),
count($this->comments) $this->comments->count()
)); ));
$delCommentForm->setComments($this->comments) $delCommentForm->setComments($this->comments->fetchAll())
->setRedirectUrl($listCommentsLink) ->setRedirectUrl($listCommentsLink)
->handleRequest(); ->handleRequest();
$this->view->delCommentForm = $delCommentForm; $this->view->delCommentForm = $delCommentForm;
$this->view->comments = $this->comments; $this->view->comments = $this->comments;
$this->view->listAllLink = Url::fromPath('monitoring/list/comments') $this->view->listAllLink = Url::fromPath('monitoring/list/comments')
->setQueryString($this->filter->toQueryString()); ->setQueryString($this->filter->toQueryString());
} }
} }

View File

@ -1,8 +1,12 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Exception;
use Icinga\Data\ResourceFactory; use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm; use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
@ -13,7 +17,7 @@ use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm;
/** /**
* Configuration controller for editing monitoring resources * Configuration controller for editing monitoring resources
*/ */
class Monitoring_ConfigController extends Controller class ConfigController extends Controller
{ {
/** /**
* Display a list of available backends and command transports * Display a list of available backends and command transports

View File

@ -1,33 +1,27 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand; use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction; use Icinga\Web\Widget\Tabextension\DashboardAction;
/** /**
* Display detailed information about a downtime * Display detailed information about a downtime
*/ */
class Monitoring_DowntimeController extends Controller class DowntimeController extends Controller
{ {
/** /**
* The fetched downtime * The fetched downtime
* *
* @var stdClass * @var object
*/ */
protected $downtime; protected $downtime;
/**
* If the downtime is a service or not
*
* @var boolean
*/
protected $isService;
/** /**
* Fetch the downtime matching the given id and add tabs * Fetch the downtime matching the given id and add tabs
*/ */
@ -58,29 +52,20 @@ class Monitoring_DowntimeController extends Controller
))->where('downtime_internal_id', $downtimeId); ))->where('downtime_internal_id', $downtimeId);
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->downtime = $query->getQuery()->fetchRow(); if (false === $this->downtime = $query->fetchRow()) {
if ($this->downtime === false) {
$this->httpNotFound($this->translate('Downtime not found')); $this->httpNotFound($this->translate('Downtime not found'));
} }
if (isset($this->downtime->service_description)) { $this->getTabs()->add(
$this->isService = true; 'downtime',
} else { array(
$this->isService = false;
}
$this->getTabs() 'icon' => 'plug',
->add( 'label' => $this->translate('Downtime'),
'downtime', 'title' => $this->translate('Display detailed information about a downtime.'),
array( 'url' =>'monitoring/downtimes/show'
'title' => $this->translate( )
'Display detailed information about a downtime.' )->activate('downtime')->extend(new DashboardAction());
),
'icon' => 'plug',
'label' => $this->translate('Downtime'),
'url' =>'monitoring/downtimes/show'
)
)->activate('downtime')->extend(new DashboardAction());
} }
/** /**
@ -88,52 +73,27 @@ class Monitoring_DowntimeController extends Controller
*/ */
public function showAction() public function showAction()
{ {
$isService = isset($this->downtime->service_description);
$this->view->downtime = $this->downtime; $this->view->downtime = $this->downtime;
$this->view->isService = $this->isService; $this->view->isService = $isService;
$this->view->stateName = isset($this->downtime->service_description) ?
Service::getStateText($this->downtime->service_state) :
Host::getStateText($this->downtime->host_state);
$this->view->listAllLink = Url::fromPath('monitoring/list/downtimes'); $this->view->listAllLink = Url::fromPath('monitoring/list/downtimes');
$this->view->showHostLink = Url::fromPath('monitoring/host/show') $this->view->showHostLink = Url::fromPath('monitoring/host/show')->setParam('host', $this->downtime->host_name);
->setParam('host', $this->downtime->host_name);
$this->view->showServiceLink = Url::fromPath('monitoring/service/show') $this->view->showServiceLink = Url::fromPath('monitoring/service/show')
->setParam('host', $this->downtime->host_name) ->setParam('host', $this->downtime->host_name)
->setParam('service', $this->downtime->service_description); ->setParam('service', $this->downtime->service_description);
$this->view->stateName = $isService ? Service::getStateText($this->downtime->service_state)
: Host::getStateText($this->downtime->host_state);
if ($this->hasPermission('monitoring/command/downtime/delete')) { if ($this->hasPermission('monitoring/command/downtime/delete')) {
$this->view->delDowntimeForm = $this->createDelDowntimeForm(); $form = new DeleteDowntimeCommandForm();
$this->view->delDowntimeForm->populate( $form
array( ->populate(array(
'redirect' => Url::fromPath('monitoring/list/downtimes'), 'downtime_id' => $this->downtime->id,
'downtime_id' => $this->downtime->id, 'downtime_is_service' => $isService,
'downtime_is_service' => $this->isService 'redirect' => Url::fromPath('monitoring/list/downtimes'),
) ))
); ->handleRequest();
$this->view->delDowntimeForm = $form;
} }
} }
/**
* Receive DeleteDowntimeCommandForm post from other controller
*/
public function removeAction()
{
$this->assertHttpMethod('POST');
$this->createDelDowntimeForm();
}
/**
* Create a command form to delete a single comment
*
* @return DeleteDowntimeCommandForm
*/
private function createDelDowntimeForm()
{
$this->assertPermission('monitoring/command/downtime/delete');
$delDowntimeForm = new DeleteDowntimeCommandForm();
$delDowntimeForm->setAction(
Url::fromPath('monitoring/downtime/show')
->setParam('downtime_id', $this->downtime->id)
);
$delDowntimeForm->handleRequest();
return $delDowntimeForm;
}
} }

View File

@ -1,27 +1,27 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
* Display detailed information about a downtime * Display detailed information about downtimes
*/ */
class Monitoring_DowntimesController extends Controller class DowntimesController extends Controller
{ {
/** /**
* The fetched downtimes * The downtimes view
* *
* @var array * @var \Icinga\Module\Monitoring\DataView\Downtime
*/ */
protected $downtimes; protected $downtimes;
/** /**
* A filter matching all current downtimes * Filter from request
* *
* @var Filter * @var Filter
*/ */
@ -29,15 +29,13 @@ class Monitoring_DowntimesController extends Controller
/** /**
* Fetch all downtimes matching the current filter and add tabs * Fetch all downtimes matching the current filter and add tabs
*
* @throws Zend_Controller_Action_Exception
*/ */
public function init() public function init()
{ {
$this->filter = Filter::fromQueryString(str_replace( $this->filter = Filter::fromQueryString(str_replace(
'downtime_id', 'downtime_id',
'downtime_internal_id', 'downtime_internal_id',
(string)$this->params (string) $this->params
)); ));
$query = $this->backend->select()->from('downtime', array( $query = $this->backend->select()->from('downtime', array(
'id' => 'downtime_internal_id', 'id' => 'downtime_internal_id',
@ -62,38 +60,17 @@ class Monitoring_DowntimesController extends Controller
))->addFilter($this->filter); ))->addFilter($this->filter);
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->downtimes = $query->getQuery()->fetchAll(); $this->downtimes = $query;
if (false === $this->downtimes) {
throw new Zend_Controller_Action_Exception(
$this->translate('Downtime not found')
);
}
$this->getTabs()->add( $this->getTabs()->add(
'downtimes', 'downtimes',
array( array(
'title' => $this->translate(
'Display detailed information about multiple downtimes.'
),
'icon' => 'plug', 'icon' => 'plug',
'label' => $this->translate('Downtimes') . sprintf(' (%d)', count($this->downtimes)), 'label' => $this->translate('Downtimes') . sprintf(' (%d)', $query->count()),
'title' => $this->translate('Display detailed information about multiple downtimes.'),
'url' =>'monitoring/downtimes/show' 'url' =>'monitoring/downtimes/show'
) )
)->activate('downtimes'); )->activate('downtimes');
foreach ($this->downtimes as $downtime) {
if (isset($downtime->service_description)) {
$downtime->isService = true;
} else {
$downtime->isService = false;
}
if ($downtime->isService) {
$downtime->stateText = Service::getStateText($downtime->service_state);
} else {
$downtime->stateText = Host::getStateText($downtime->host_state);
}
}
} }
/** /**
@ -103,9 +80,8 @@ class Monitoring_DowntimesController extends Controller
{ {
$this->view->downtimes = $this->downtimes; $this->view->downtimes = $this->downtimes;
$this->view->listAllLink = Url::fromPath('monitoring/list/downtimes') $this->view->listAllLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString($this->filter->toQueryString()); ->setQueryString($this->filter->toQueryString());
$this->view->removeAllLink = Url::fromPath('monitoring/downtimes/delete-all') $this->view->removeAllLink = Url::fromPath('monitoring/downtimes/delete-all')->setParams($this->params);
->setParams($this->params);
} }
/** /**
@ -121,10 +97,10 @@ class Monitoring_DowntimesController extends Controller
$delDowntimeForm->setTitle($this->view->translate('Remove all Downtimes')); $delDowntimeForm->setTitle($this->view->translate('Remove all Downtimes'));
$delDowntimeForm->addDescription(sprintf( $delDowntimeForm->addDescription(sprintf(
$this->translate('Confirm removal of %d downtimes.'), $this->translate('Confirm removal of %d downtimes.'),
count($this->downtimes) $this->downtimes->count()
)); ));
$delDowntimeForm->setRedirectUrl(Url::fromPath('monitoring/list/downtimes')); $delDowntimeForm->setRedirectUrl(Url::fromPath('monitoring/list/downtimes'));
$delDowntimeForm->setDowntimes($this->downtimes)->handleRequest(); $delDowntimeForm->setDowntimes($this->downtimes->fetchAll())->handleRequest();
$this->view->delDowntimeForm = $delDowntimeForm; $this->view->delDowntimeForm = $delDowntimeForm;
} }
} }

View File

@ -1,6 +1,8 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
@ -11,11 +13,11 @@ use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController; use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
use Icinga\Web\Hook; use Icinga\Web\Hook;
class Monitoring_HostController extends MonitoredObjectController class HostController extends MonitoredObjectController
{ {
/** /**
* (non-PHPDoc) * {@inheritdoc}
* @see MonitoredObjectController::$commandRedirectUrl For the property documentation.
*/ */
protected $commandRedirectUrl = 'monitoring/host/show'; protected $commandRedirectUrl = 'monitoring/host/show';
@ -25,9 +27,7 @@ class Monitoring_HostController extends MonitoredObjectController
public function init() public function init()
{ {
$host = new Host($this->backend, $this->params->getRequired('host')); $host = new Host($this->backend, $this->params->getRequired('host'));
$this->applyRestriction('monitoring/filter/objects', $host); $this->applyRestriction('monitoring/filter/objects', $host);
if ($host->fetch() === false) { if ($host->fetch() === false) {
$this->httpNotFound($this->translate('Host not found')); $this->httpNotFound($this->translate('Host not found'));
} }

View File

@ -1,23 +1,25 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterEqual; use Icinga\Data\Filter\FilterEqual;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Object\HostList; use Icinga\Module\Monitoring\Object\HostList;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction; use Icinga\Web\Widget\Tabextension\DashboardAction;
class Monitoring_HostsController extends Controller class HostsController extends Controller
{ {
/** /**
* @var HostList * @var HostList
@ -166,18 +168,6 @@ class Monitoring_HostsController extends Controller
$this->handleCommandForm($form); $this->handleCommandForm($form);
} }
/**
* Delete a comment
*/
public function deleteCommentAction()
{
$this->assertPermission('monitoring/command/comment/delete');
$form = new DeleteCommentCommandForm();
$form->setTitle($this->translate('Delete Host Comments'));
$this->handleCommandForm($form);
}
/** /**
* Acknowledge host problems * Acknowledge host problems
*/ */

View File

@ -1,19 +1,22 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Controller; namespace Icinga\Module\Monitoring\Controllers;
use Zend_Form;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Backend; use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\DataView\DataView;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\StatehistoryForm;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction; use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat; use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabs; use Icinga\Web\Widget\Tabs;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Forms\StatehistoryForm;
use Icinga\Module\Monitoring\DataView\DataView;
class Monitoring_ListController extends Controller class ListController extends Controller
{ {
/** /**
* @see ActionController::init * @see ActionController::init
@ -556,29 +559,36 @@ class Monitoring_ListController extends Controller
{ {
$this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid')); $this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid'));
$this->setAutorefreshInterval(15); $this->setAutorefreshInterval(15);
$problems = (bool) $this->params->shift('problems', 0);
$query = $this->backend->select()->from('servicestatus', array( $query = $this->backend->select()->from('servicestatus', array(
'host_display_name',
'host_name', 'host_name',
'service_description', 'service_description',
'service_state', 'service_display_name',
'service_handled',
'service_output', 'service_output',
'service_handled' 'service_state'
)); ));
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query); $this->filterQuery($query);
$filter = (bool) $this->params->shift('problems', false) ? Filter::where('service_problem', 1) : null;
$pivot = $query
->pivot(
'service_description',
'host_name',
$filter,
$filter ? clone $filter : null
)
->setXAxisHeader('service_display_name')
->setYAxisHeader('host_display_name');
$this->setupSortControl(array( $this->setupSortControl(array(
'host_name' => $this->translate('Hostname'), 'host_display_name' => $this->translate('Hostname'),
'service_description' => $this->translate('Service description') 'service_display_name' => $this->translate('Service Name')
), $query); ), $pivot);
$pivot = $query->pivot(
'service_description',
'host_name',
$problems ? Filter::where('service_problem', 1) : null,
$problems ? Filter::where('service_problem', 1) : null
);
$this->view->pivot = $pivot;
$this->view->horizontalPaginator = $pivot->paginateXAxis(); $this->view->horizontalPaginator = $pivot->paginateXAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis(); $this->view->verticalPaginator = $pivot->paginateYAxis();
list($pivotData, $pivotHeader) = $pivot->toArray();
$this->view->pivotData = $pivotData;
$this->view->pivotHeader = $pivotHeader;
} }
/** /**

View File

@ -1,15 +1,17 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Widget\Tabextension\DashboardAction; namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Instance\DisableNotificationsExpireCommandForm; use Icinga\Module\Monitoring\Forms\Command\Instance\DisableNotificationsExpireCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm; use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm;
use Icinga\Web\Widget\Tabextension\DashboardAction;
/** /**
* Display process and performance information of the monitoring host and program-wide commands * Display process and performance information of the monitoring host and program-wide commands
*/ */
class Monitoring_ProcessController extends Controller class ProcessController extends Controller
{ {
/** /**
* Add tabs * Add tabs
@ -122,20 +124,4 @@ class Monitoring_ProcessController extends Controller
$this->view->form = $form; $this->view->form = $form;
} }
} }
/**
* @todo should be dropped later
*/
public function performanceAction()
{
$this->getTabs()->activate('performance');
$this->setAutorefreshInterval(10);
$this->view->runtimevariables = (object) $this->backend->select()
->from('runtimevariables', array('varname', 'varvalue'))
->getQuery()->fetchPairs();
$this->view->checkperformance = $this->backend->select()
->from('runtimesummary')
->getQuery()->fetchAll();
}
} }

View File

@ -1,6 +1,8 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
@ -11,11 +13,10 @@ use Icinga\Module\Monitoring\Object\Service;
use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController; use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
use Icinga\Web\Hook; use Icinga\Web\Hook;
class Monitoring_ServiceController extends MonitoredObjectController class ServiceController extends MonitoredObjectController
{ {
/** /**
* (non-PHPDoc) * {@inheritdoc}
* @see MonitoredObjectController::$commandRedirectUrl For the property documentation.
*/ */
protected $commandRedirectUrl = 'monitoring/service/show'; protected $commandRedirectUrl = 'monitoring/service/show';

View File

@ -1,23 +1,24 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm; use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Object\ServiceList; use Icinga\Module\Monitoring\Object\ServiceList;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction; use Icinga\Web\Widget\Tabextension\DashboardAction;
class Monitoring_ServicesController extends Controller class ServicesController extends Controller
{ {
/** /**
* @var ServiceList * @var ServiceList
@ -180,20 +181,6 @@ class Monitoring_ServicesController extends Controller
$this->handleCommandForm($form); $this->handleCommandForm($form);
} }
/**
* Delete a comment
*/
public function deleteCommentAction()
{
$this->assertPermission('monitoring/command/comment/delete');
$form = new DeleteCommentCommandForm();
$form->setTitle($this->translate('Delete Service Comments'));
$this->handleCommandForm($form);
}
/** /**
* Acknowledge service problems * Acknowledge service problems
*/ */

View File

@ -1,60 +1,27 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Url; namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Backend; use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Web\Url;
/** /**
* Class Monitoring_ShowController * Class Monitoring_ShowController
* *
* Actions for show context * Actions for show context
*/ */
class Monitoring_ShowController extends Controller class ShowController extends Controller
{ {
/** /**
* @var Backend * @var Backend
*/ */
protected $backend; protected $backend;
/**
* @deprecated
*/
public function serviceAction()
{
$this->redirectNow(Url::fromRequest()->setPath('monitoring/service/show'));
}
/**
* @deprecated
*/
public function hostAction()
{
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/show'));
}
/**
* @deprecated
*/
public function historyAction()
{
if ($this->params->has('service')) {
$this->redirectNow(Url::fromRequest()->setPath('monitoring/service/history'));
}
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/history'));
}
public function contactAction() public function contactAction()
{ {
$contactName = $this->getParam('contact_name'); $contactName = $this->params->getRequired('contact_name');
if (! $contactName) {
throw new Zend_Controller_Action_Exception(
$this->translate('The parameter `contact_name\' is required'),
404
);
}
$query = $this->backend->select()->from('contact', array( $query = $this->backend->select()->from('contact', array(
'contact_name', 'contact_name',

View File

@ -1,11 +1,13 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Controller as MonitoringController; namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Url;
class Monitoring_TacticalController extends MonitoringController use Icinga\Module\Monitoring\Controller;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
class TacticalController extends Controller
{ {
public function indexAction() public function indexAction()
{ {

View File

@ -1,15 +1,19 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Url; namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Util\Format;
use DateInterval;
use DateTime;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Timeline\TimeLine; use Icinga\Module\Monitoring\Timeline\TimeLine;
use Icinga\Module\Monitoring\Timeline\TimeRange; use Icinga\Module\Monitoring\Timeline\TimeRange;
use Icinga\Module\Monitoring\Web\Widget\SelectBox; use Icinga\Module\Monitoring\Web\Widget\SelectBox;
use Icinga\Util\Format;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction; use Icinga\Web\Widget\Tabextension\DashboardAction;
class Monitoring_TimelineController extends Controller class TimelineController extends Controller
{ {
public function indexAction() public function indexAction()
{ {

View File

@ -1,25 +1,19 @@
<div class="controls"> <div class="controls">
<?php if (! $this->compact): ?> <?php if (! $this->compact): ?>
<?= $this->tabs; ?> <?= $this->tabs ?>
<?php endif ?> <?php endif ?>
<?= $this->render('partials/comment/comments-header.phtml') ?>
<div data-base-target='_next'>
<?= $this->render('partials/comment/comments-header.phtml'); ?>
</div>
</div> </div>
<div class="content multi-commands"> <div class="content multi-commands">
<h3><?= $this->icon('reschedule') ?> <?= $this->translate('Commands') ?> </h3> <h3><?= $this->icon('reschedule') ?><?= $this->translate('Commands') ?></h3>
<?= $this->qlink( <?= $this->qlink(
sprintf( sprintf($this->translate('Remove %d comments'), $comments->count()),
$this->translate('Remove %d comments'),
count($comments)
),
$removeAllLink, $removeAllLink,
null, null,
array( array(
'icon' => 'trash', 'icon' => 'trash',
'title' => $this->translate('Remove all selected comments.') 'title' => $this->translate('Remove all selected comments')
) )
) ?> ) ?>
</div> </div>

View File

@ -1,23 +1,19 @@
<div class="controls"> <div class="controls">
<?php if (! $this->compact): ?>
<?php if (! $this->compact): ?> <?= $this->tabs ?>
<?= $this->tabs; ?> <?php endif ?>
<?php endif ?> <?= $this->render('partials/downtime/downtimes-header.phtml') ?>
<?= $this->render('partials/downtime/downtimes-header.phtml'); ?>
</p>
</div> </div>
<div class="content multi-commands"> <div class="content multi-commands">
<h3><?= $this->translate('Commands') ?> </h3> <h3><?= $this->icon('reschedule') ?><?= $this->translate('Commands') ?></h3>
<?= $this->qlink( <?= $this->qlink(
sprintf( sprintf($this->translate('Remove all %d scheduled downtimes'), $downtimes->count()),
$this->translate('Remove all %d scheduled downtimes'),
count($downtimes)
),
$removeAllLink, $removeAllLink,
null, null,
array( array(
'icon' => 'trash' 'icon' => 'trash',
'title' => $this->translate('Remove all selected downtimes')
) )
) ?> ) ?>
</div> </div>

View File

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

View File

@ -1,27 +1,28 @@
<?php <?php
switch ($comment->type) { switch ($comment->type) {
case 'flapping': case 'flapping':
$icon = 'flapping'; $icon = 'flapping';
$title = $this->translate('Flapping'); $title = $this->translate('Flapping');
$tooltip = $this->translate('Comment was caused by a flapping host or service.'); $tooltip = $this->translate('Comment was caused by a flapping host or service');
break; break;
case 'comment': case 'comment':
$icon = 'user'; $icon = 'user';
$title = $this->translate('User Comment'); $title = $this->translate('User Comment');
$tooltip = $this->translate('Comment was created by an user.'); $tooltip = $this->translate('Comment was created by an user');
break; break;
case 'downtime': case 'downtime':
$icon = 'plug'; $icon = 'plug';
$title = $this->translate('Downtime'); $title = $this->translate('Downtime');
$tooltip = $this->translate('Comment was caused by a downtime.'); $tooltip = $this->translate('Comment was caused by a downtime');
break; break;
case 'ack': case 'ack':
$icon = 'ok'; $icon = 'ok';
$title = $this->translate('Acknowledgement'); $title = $this->translate('Acknowledgement');
$tooltip = $this->translate('Comment was caused by an acknowledgement.'); $tooltip = $this->translate('Comment was caused by an acknowledgement');
break; break;
} }
?> ?>
<strong><?= $this->escape($title); ?></strong><br> <strong><?= $this->escape($title) ?></strong>
<br>
<?= $this->icon($icon, $tooltip) ?> <?= $this->icon($icon, $tooltip) ?>
<?= $this->timeAgo($comment->timestamp, $this->compact); ?> <?= $this->timeAgo($comment->timestamp, $this->compact) ?>

View File

@ -1,17 +1,17 @@
<?php if ($comment->objecttype === 'service'): ?> <?php if ($comment->objecttype === 'service'): ?>
<?= $this->icon('service', $this->translate('Service')); ?> <?= $this->icon('service', $this->translate('Service')) ?>
<?= sprintf( <?= sprintf(
'%s: %s', '%s: %s',
$comment->host_display_name, $comment->host_display_name,
$comment->service_display_name $comment->service_display_name
) ?> ) ?>
<?php else: ?> <?php else: ?>
<?= $this->icon('host', $this->translate('Host')); ?> <?= $this->icon('host', $this->translate('Host')) ?>
<?= $this->link()->host($comment->host_name, $comment->host_display_name); ?> <?= $this->link()->host($comment->host_name, $comment->host_display_name) ?>
<?php endif ?> <?php endif ?>
<br> <br>
<?= $this->icon('comment', $this->translate('Comment')); ?> <?= isset($comment->author) <?= $this->icon('comment', $this->translate('Comment')) ?>
? '[' . $this->escape($comment->author) . '] ' <?php if (isset($comment->author)): ?>
: ''; [<?= $this->escape($comment->author) ?>]
?><?= $this->escape($comment->comment); ?> <?php endif ?>
<?= $this->escape($comment->comment) ?>

View File

@ -1,32 +1,32 @@
<table class="action"> <table class="action" data-base-target="_next">
<?php $i = 0; foreach ($comments as $comment): <tbody>
if (++ $i > 5) { <?php
continue; foreach ($comments as $i => $comment):
if ($i > 5) {
break;
} }
$this->comment = $comment;
?> ?>
<tr class="state invalid"> <tr class="state invalid">
<td class="state" style="width: 12em;"> <td class="state" style="width: 12em;">
<?= $this->render('partials/comment/comment-description.phtml'); ?> <?= $this->partial('partials/comment/comment-description.phtml', array('comment' => $comment)) ?>
</td> </td>
<td> <td>
<?= $this->render('partials/comment/comment-detail.phtml'); ?> <?= $this->partial('partials/comment/comment-detail.phtml', array('comment' => $comment)) ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach ?>
</tbody>
</table> </table>
<?php if ($comments->count() > 5): ?>
<p> <p>
<?php if ($i > 5): ?>
<?= $this->qlink( <?= $this->qlink(
sprintf($this->translate('show all %d comments'), $i), sprintf($this->translate('List all %d comments'), $comments->count()),
$listAllLink, $listAllLink,
null, null,
array( array(
'icon' => $i > 5 ? 'down-open' : '', 'data-base-target' => '_next',
'data-base-target' => "_next" 'icon' => 'down-open'
) )
) ?> ) ?>
<?php endif ?>
</p> </p>
<?php endif ?>

View File

@ -1,95 +1,111 @@
<table class="action" data-base-target="_next"> <?php
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
?>
<table class="action" data-base-target="_next">
<tbody> <tbody>
<?php $i = 0; foreach ($downtimes as $downtime): <?php
if (++ $i > 5) { foreach ($this->downtimes as $i => $downtime):
if ($i > 5) {
break; break;
} ?> }
<tr class="state <?= $downtime->stateText ?>"> if ($downtime->objecttype === 'service') {
<td class="state"> $isService = true;
<?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?> $stateText = Service::getStateText($downtime->service_state);
<strong><?= $this->translate('Ends'); ?></strong> } else {
<br> $isService = false;
<?= $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact) ?> $stateText = Host::getStateText($downtime->host_state);
<?php else: ?> }
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong> ?>
<br> <tr class="state <?= $stateText . ($downtime->is_in_effect ? ' handled' : '') ?>">
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?> <td class="state">
<?php endif; ?> <?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?>
</td> <strong><?= $this->translate('Ends') ?></strong>
<td class="name oneline"> <br>
<?php if ($downtime->isService): ?> <?= $this->timeUntil(
<?= $this->icon('service', $this->translate('Service')) ?> $downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact
<?= $this->link()->service( ) ?>
$downtime->service_description, <?php else: ?>
$downtime->service_display_name, <strong>
$downtime->host_name, <?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts') ?>
$downtime->host_display_name </strong>
); ?> <br>
<?php else: ?> <?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
<?= $this->icon('host', $this->translate('Host')) ?> <?php endif ?>
<?= $this->link()->host($downtime->host_name, $downtime->host_display_name); ?> </td>
<?php endif; ?> <td class="name oneline">
<?php if ($isService): ?>
<?= $this->icon('service', $this->translate('Service')) ?>
<?= $this->link()->service(
$downtime->service_description,
$downtime->service_display_name,
$downtime->host_name,
$downtime->host_display_name
) ?>
<?php else: ?>
<?= $this->icon('host', $this->translate('Host')) ?>
<?= $this->link()->host($downtime->host_name, $downtime->host_display_name) ?>
<?php endif ?>
<br> <br>
<?php if ($downtime->is_flexible): ?> <?php if ($downtime->is_flexible): ?>
<?php if ($downtime->is_in_effect): ?> <?php if ($downtime->is_in_effect): ?>
<?= sprintf( <?= sprintf(
$downtime->isService $isService
? $this->translate('This flexible service downtime was started on %s at %s and lasts for %s until %s at %s.') ? $this->translate('This flexible service downtime was started on %s at %s and lasts for %s until %s at %s.')
: $this->translate('This flexible host downtime was started on %s at %s and lasts for %s until %s at %s.'), : $this->translate('This flexible host downtime was started on %s at %s and lasts for %s until %s at %s.'),
$this->formatDate($downtime->start), $this->formatDate($downtime->start),
$this->formatTime($downtime->start), $this->formatTime($downtime->start),
$this->formatDuration($downtime->duration), $this->formatDuration($downtime->duration),
$this->formatDate($downtime->end), $this->formatDate($downtime->end),
$this->formatTime($downtime->end) $this->formatTime($downtime->end)
); ?> ) ?>
<?php else: ?> <?php else: ?>
<?= sprintf( <?= sprintf(
$downtime->isService $isService
? $this->translate('This flexible service downtime has been scheduled to start between %s - %s and to last for %s.') ? $this->translate('This flexible service downtime has been scheduled to start between %s - %s and to last for %s.')
: $this->translate('This flexible host downtime has been scheduled to start between %s - %s and to last for %s.'), : $this->translate('This flexible host downtime has been scheduled to start between %s - %s and to last for %s.'),
$this->formatDateTime($downtime->scheduled_start), $this->formatDateTime($downtime->scheduled_start),
$this->formatDateTime($downtime->scheduled_end), $this->formatDateTime($downtime->scheduled_end),
$this->formatDuration($downtime->duration) $this->formatDuration($downtime->duration)
); ?> ) ?>
<?php endif ?> <?php endif ?>
<?php else: ?> <?php else: ?>
<?php if ($downtime->is_in_effect): ?> <?php if ($downtime->is_in_effect): ?>
<?= sprintf( <?= sprintf(
$downtime->isService $isService
? $this->translate('This fixed service downtime was started on %s at %s and expires on %s at %s.') ? $this->translate('This fixed service downtime was started on %s at %s and expires on %s at %s.')
: $this->translate('This fixed host downtime was started on %s at %s and expires on %s at %s.'), : $this->translate('This fixed host downtime was started on %s at %s and expires on %s at %s.'),
$this->formatDate($downtime->start), $this->formatDate($downtime->start),
$this->formatTime($downtime->start), $this->formatTime($downtime->start),
$this->formatDate($downtime->end), $this->formatDate($downtime->end),
$this->formatTime($downtime->end) $this->formatTime($downtime->end)
); ?> ) ?>
<?php else: ?> <?php else: ?>
<?= sprintf( <?= sprintf(
$downtime->isService $isService
? $this->translate('This fixed service downtime has been scheduled to start on %s at %s and to end on %s at %s.') ? $this->translate('This fixed service downtime has been scheduled to start on %s at %s and to end on %s at %s.')
: $this->translate('This fixed host downtime has been scheduled to start on %s at %s and to end on %s at %s.'), : $this->translate('This fixed host downtime has been scheduled to start on %s at %s and to end on %s at %s.'),
$this->formatDate($downtime->scheduled_start), $this->formatDate($downtime->scheduled_start),
$this->formatTime($downtime->scheduled_start), $this->formatTime($downtime->scheduled_start),
$this->formatDate($downtime->scheduled_end), $this->formatDate($downtime->scheduled_end),
$this->formatTime($downtime->scheduled_end) $this->formatTime($downtime->scheduled_end)
); ?> ) ?>
<?php endif ?> <?php endif ?>
<?php endif ?> <?php endif ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach ?>
</tbody> </tbody>
</table> </table>
<?php if ($downtimes->count() > 5): ?>
<?php if (count($downtimes) > 5): ?>
<p> <p>
<?= $this->qlink( <?= $this->qlink(
sprintf($this->translate('List all %d downtimes'), $i), sprintf($this->translate('List all %d downtimes'), $downtimes->count()),
$listAllLink, $listAllLink,
null, null,
array( array(
'icon' => 'down-open', 'data-base-target' => '_next',
'data-base-target' => "_next" 'icon' => 'down-open'
) )
) ?> ) ?>
</p> </p>

View File

@ -1,110 +0,0 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
</div>
<?php endif;
$rv = $this->runtimeVariables()->create($this->runtimevariables);
$cp = $this->checkPerformance()->create($this->checkperformance);
?>
<div class="content processinfo">
<h4>Object summaries</h4>
<table class="table-bordered table">
<thead>
<tr>
<td style="width: 300px;">&nbsp;</td>
<td># overall / scheduled</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<strong>Hosts</strong>
</td>
<td>
<?= $rv->total_hosts; ?>
/ <?= $rv->total_scheduled_hosts; ?>
</td>
</tr>
<tr>
<td>
<strong>Services</strong>
</td>
<td>
<?= $rv->total_services; ?>
/ <?= $rv->total_scheduled_services; ?>
</td>
</tr>
<tr>
<td>
<strong>Average services per host</strong>
</td>
<td>
<?= sprintf('%.2f', $rv->average_services_per_host); ?>
/ <?= sprintf('%.2f', $rv->average_scheduled_services_per_host); ?>
</td>
</tr>
</tbody>
</table>
<h4>Active checks</h4>
<table class="table-bordered table">
<thead>
<tr>
<td style="width: 300px;">&nbsp;</td>
<td>#</td>
<td>Latency</td>
<td>Execution time</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<strong>Host Checks</strong>
</td>
<td><?= $cp->host_active_count; ?></td>
<td><?= sprintf('%.3f', $cp->host_active_latency_avg); ?>s</td>
<td><?= sprintf('%.3f', $cp->host_active_execution_avg); ?>s</td>
</tr>
<tr>
<td>
<strong>Service Checks</strong>
</td>
<td><?= $cp->service_active_count; ?></td>
<td><?= sprintf('%.3f', $cp->service_active_latency_avg); ?>s</td>
<td><?= sprintf('%.3f', $cp->service_active_execution_avg); ?>s</td>
</tr>
</tbody>
</table>
<h4>Passive checks</h4>
<table class="table-bordered table">
<thead>
<tr>
<td style="width: 300px;">&nbsp;</td>
<td>#</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<strong>Host Checks</strong>
</td>
<td><?= $cp->host_passive_count; ?></td>
</tr>
<tr>
<td>
<strong>Service Checks</strong>
</td>
<td><?= $cp->service_passive_count; ?></td>
</tr>
</tbody>
</table>
</div>

View File

@ -240,3 +240,9 @@ $dashboard->add(
$this->translate('Host Problems'), $this->translate('Host Problems'),
'monitoring/list/hosts?host_problem=1&sort=host_severity' 'monitoring/list/hosts?host_problem=1&sort=host_severity'
); );
/*
* CSS
*/
$this->provideCssFile('colors.less');
$this->provideCssFile('service-grid.less');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,6 +75,20 @@ class EventhistoryQuery extends IdoQuery
$this->joinedVirtualTables['eventhistory'] = true; $this->joinedVirtualTables['eventhistory'] = true;
} }
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

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

View File

@ -257,62 +257,65 @@ class HoststatusQuery extends IdoQuery
*/ */
public function getGroup() public function getGroup()
{ {
$group = array(); $group = parent::getGroup() ?: array();
if ($this->hasJoinedVirtualTable('hostgroups') || $this->hasJoinedVirtualTable('services')) { if (! is_array($group)) {
$group = array('h.host_id', 'ho.object_id'); $group = array($group);
if ($this->hasJoinedVirtualTable('hoststatus')) { }
$group[] = 'hs.hoststatus_id'; $groupedTables = array();
} if ($this->hasJoinedVirtualTable('servicegroups')) {
$serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
if ($this->hasJoinedVirtualTable('serviceproblemsummary')) { $selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
$group[] = 'sps.unhandled_services_count'; if (! empty($selectedServiceGroupColumns)) {
} $group[] = 'ho.object_id';
$group[] = 'h.host_id';
if ($this->hasJoinedVirtualTable('hostgroups')) { $group[] = 'sgo.object_id';
$selected = false; $group[] = 'sg.servicegroup_id';
foreach ($this->columns as $alias => $column) { $groupedTables['hosts'] = true;
if ($column instanceof Zend_Db_Expr) { $groupedTables['servicegroups'] = true;
continue; }
} }
if ($this->hasJoinedVirtualTable('hostgroups')) {
$table = $this->aliasToTableName( $hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias) $selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
); if (! empty($selectedHostGroupColumns)) {
if ($table === 'hostgroups') { if (! isset($groupedTables['hosts'])) {
$selected = true; $group[] = 'ho.object_id';
break; $group[] = 'h.host_id';
} $groupedTables['hosts'] = true;
} }
$group[] = 'hgo.object_id';
if ($selected) { $group[] = 'hg.hostgroup_id';
$group[] = 'hg.hostgroup_id'; $groupedTables['hostgroups'] = true;
$group[] = 'hgo.object_id'; }
} }
} if (! empty($groupedTables)) {
foreach ($this->columns as $alias => $column) {
if ($this->hasJoinedVirtualTable('servicegroups')) { if ($column instanceof Zend_Db_Expr || $column === '(NULL)') {
$selected = false; continue;
foreach ($this->columns as $alias => $column) { }
if ($column instanceof Zend_Db_Expr) { $tableName = $this->aliasToTableName(
continue; $this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
} );
if (isset($groupedTables[$tableName])) {
$table = $this->aliasToTableName( continue;
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias) }
); switch ($tableName) {
if ($table === 'servicegroups') { case 'hoststatus':
$selected = true; $group[] = 'hs.hoststatus_id';
break; break;
} case 'serviceproblemsummary':
} $group[] = 'sps.unhandled_services_count';
break;
if ($selected) { case 'services':
$group[] = 'sg.servicegroup_id'; $group[] = 'so.object_id';
$group[] = 'sgo.object_id'; $group[] = 's.service_id';
} break;
default:
continue 2;
}
$groupedTables[$tableName] = true;
} }
} }
return $group; return $group;
} }

View File

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

View File

@ -461,17 +461,23 @@ abstract class IdoQuery extends DbQuery
if ($filter->getExpression() === '*') { if ($filter->getExpression() === '*') {
return; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing return; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
} }
$alias = $filter->getColumn();
$col = $filter->getColumn(); $this->requireColumn($alias);
$this->requireColumn($col); if ($this->isCustomvar($alias)) {
$column = $this->getCustomvarColumnName($alias);
if ($this->isCustomvar($col)) {
$col = $this->getCustomvarColumnName($col);
} else { } else {
$col = $this->aliasToColumnName($col); $column = $this->aliasToColumnName($alias);
} }
if (isset($this->columnsWithoutCollation[$alias])) {
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
$filter->setColumn($col); }
}
$filter->setColumn($column);
} else { } else {
foreach ($filter->filters() as $filter) { foreach ($filter->filters() as $filter) {
$this->requireFilterColumns($filter); $this->requireFilterColumns($filter);
@ -489,48 +495,11 @@ abstract class IdoQuery extends DbQuery
return parent::addFilter($filter); return parent::addFilter($filter);
} }
/**
* Recurse the given filter and ensure that any string conversion is case-insensitive
*
* @param Filter $filter
*/
protected function lowerColumnsWithoutCollation(Filter $filter)
{
if ($filter instanceof FilterExpression) {
if (
in_array($filter->getColumn(), $this->columnsWithoutCollation)
&& strpos($filter->getColumn(), 'LOWER') !== 0
) {
$filter->setColumn('LOWER(' . $filter->getColumn() . ')');
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
}
}
} else {
foreach ($filter->filters() as $chainedFilter) {
$this->lowerColumnsWithoutCollation($chainedFilter);
}
}
}
protected function applyFilterSql($select)
{
if (! empty($this->columnsWithoutCollation)) {
$this->lowerColumnsWithoutCollation($this->filter);
}
parent::applyFilterSql($select);
}
public function where($condition, $value = null) public function where($condition, $value = null)
{ {
if ($value === '*') { if ($value === '*') {
return $this; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing return $this; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
} }
$this->requireColumn($condition); $this->requireColumn($condition);
$col = $this->getMappedField($condition); $col = $this->getMappedField($condition);
if ($col === null) { if ($col === null) {
@ -578,7 +547,6 @@ abstract class IdoQuery extends DbQuery
if (! empty($this->columnsWithoutCollation)) { if (! empty($this->columnsWithoutCollation)) {
return in_array($column, $this->columnsWithoutCollation) || strpos($column, 'LOWER') !== 0; return in_array($column, $this->columnsWithoutCollation) || strpos($column, 'LOWER') !== 0;
} }
return preg_match('/ COLLATE .+$/', $column) === 1; return preg_match('/ COLLATE .+$/', $column) === 1;
} }
@ -603,27 +571,27 @@ abstract class IdoQuery extends DbQuery
} }
/** /**
* Apply postgresql specific query initialization * Apply PostgreSQL specific query initialization
*/ */
private function initializeForPostgres() private function initializeForPostgres()
{ {
$this->customVarsJoinTemplate = $this->customVarsJoinTemplate =
'%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s'; '%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s';
foreach ($this->columnMap as $table => & $columns) { foreach ($this->columnMap as $table => &$columns) {
foreach ($columns as $key => & $value) { foreach ($columns as $alias => &$column) {
$value = preg_replace('/ COLLATE .+$/', '', $value, -1, $count); if (false !== $pos = strpos($column, ' COLLATE')) {
if ($count > 0) { $column = 'LOWER(' . substr($column, 0, $pos) . ')';
$this->columnsWithoutCollation[] = $this->getMappedField($key); $this->columnsWithoutCollation[$alias] = true;
} }
$value = preg_replace( $column = preg_replace(
'/inet_aton\(([[:word:].]+)\)/i', '/inet_aton\(([[:word:].]+)\)/i',
'(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)', '(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)',
$value $column
); );
$value = preg_replace( $column = preg_replace(
'/UNIX_TIMESTAMP(\((?>[^()]|(?-1))*\))/i', '/UNIX_TIMESTAMP(\((?>[^()]|(?-1))*\))/i',
'CASE WHEN ($1 < \'1970-01-03 00:00:00+00\'::timestamp with time zone) THEN 0 ELSE UNIX_TIMESTAMP($1) END', 'CASE WHEN ($1 < \'1970-01-03 00:00:00+00\'::timestamp with time zone) THEN 0 ELSE UNIX_TIMESTAMP($1) END',
$value $column
); );
} }
} }

View File

@ -20,7 +20,7 @@ class InstanceQuery extends IdoQuery
*/ */
protected function joinBaseTables() protected function joinBaseTables()
{ {
$this->select()->from(array('i' => $this->prefix . 'instances')); $this->select()->from(array('i' => $this->prefix . 'instances'), array());
$this->joinedVirtualTables['instances'] = true; $this->joinedVirtualTables['instances'] = true;
} }
} }

View File

@ -129,6 +129,20 @@ class NotificationQuery extends IdoQuery
$this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL); $this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL);
} }
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

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

View File

@ -381,68 +381,60 @@ class ServicestatusQuery extends IdoQuery
if (! is_array($group)) { if (! is_array($group)) {
$group = array($group); $group = array($group);
} }
$groupedTables = array();
if ($this->hasJoinedVirtualTable('hostgroups') || $this->hasJoinedVirtualTable('servicegroups')) { if ($this->hasJoinedVirtualTable('servicegroups')) {
$group[] = 's.service_id'; $serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
$group[] = 'so.object_id'; $selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
if (! empty($selectedServiceGroupColumns)) {
if ($this->hasJoinedVirtualTable('hosts')) { $group[] = 'so.object_id';
$group[] = 'h.host_id'; $group[] = 's.service_id';
} $group[] = 'sgo.object_id';
$group[] = 'sg.servicegroup_id';
if ($this->hasJoinedVirtualTable('hoststatus')) { $groupedTables['services'] = true;
$group[] = 'hs.hoststatus_id'; $groupedTables['servicegroups'] = true;
} }
}
if ($this->hasJoinedVirtualTable('servicestatus')) { if ($this->hasJoinedVirtualTable('hostgroups')) {
$group[] = 'ss.servicestatus_id'; $hostGroupColumns = array_keys($this->columnMap['hostgroups']);
} $selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
if ($this->hasJoinedVirtualTable('hostgroups')) { if (! isset($groupedTables['services'])) {
$selected = false; $group[] = 'so.object_id';
foreach ($this->columns as $alias => $column) { $group[] = 's.service_id';
if ($column instanceof Zend_Db_Expr) { $groupedTables['services'] = true;
continue; }
} $group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$table = $this->aliasToTableName( $groupedTables['hostgroups'] = true;
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias) }
); }
if ($table === 'hostgroups') { if (! empty($groupedTables)) {
$selected = true; foreach ($this->columns as $alias => $column) {
break; if ($column instanceof Zend_Db_Expr || $column === '(NULL)') {
} continue;
} }
$tableName = $this->aliasToTableName(
if ($selected) { $this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
$group[] = 'hg.hostgroup_id'; );
$group[] = 'hgo.object_id'; if (isset($groupedTables[$tableName])) {
} continue;
} }
switch ($tableName) {
if ($this->hasJoinedVirtualTable('servicegroups')) { case 'hosts':
$selected = false; $group[] = 'h.host_id';
foreach ($this->columns as $alias => $column) { break;
if ($column instanceof Zend_Db_Expr) { case 'hoststatus':
continue; $group[] = 'hs.hoststatus_id';
} break;
case 'servicestatus':
$table = $this->aliasToTableName( $group[] = 'ss.servicestatus_id';
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias) break;
); default:
if ($table === 'servicegroups') { continue 2;
$selected = true; }
break; $groupedTables[$tableName] = true;
}
}
if ($selected) {
$group[] = 'sg.servicegroup_id';
$group[] = 'sgo.object_id';
}
} }
} }
return $group; return $group;
} }
} }

View File

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

View File

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

View File

@ -130,6 +130,20 @@ We have to find a better solution here.
*/ */
protected $subQueries = array(); protected $subQueries = array();
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -0,0 +1,43 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
.bg-state-ok,
.bg-state-up {
background-color: @colorOk;
}
.bg-state-warning {
background-color: @colorWarning;
&.handled {
background-color: @colorWarningHandled;
}
}
.bg-state-critical,
.bg-state-down {
background-color: @colorCritical;
&.handled {
background-color: @colorCriticalHandled;
}
}
.bg-state-unreachable {
background-color: @colorUnreachable;
&.handled {
background-color: @colorUnreachableHandled;
}
}
.bg-state-unknown {
background-color: @colorUnknown;
&.handled {
background-color: @colorUnknownHandled;
}
}
.bg-state-pending {
background-color: @colorPending;
}

View File

@ -842,112 +842,6 @@ table.joystick-pagination {
} }
} }
table.pivot {
a {
text-decoration: none;
color: black;
&:hover {
color: @colorTextDefault;
}
}
& > thead {
th {
height: 6em;
div {
margin-right: -1.5em;
padding-left: 1.3em;
span {
width: 1.5em;
margin-right: 0.25em;
margin-top: 4em;
line-height: 2em;
white-space: nowrap;
display: block;
float: left;
transform: rotate(-45deg);
transform-origin: bottom left;
-o-transform: rotate(-45deg);
-o-transform-origin: bottom left;
-ms-transform: rotate(-45deg);
-ms-transform-origin: bottom left;
-moz-transform: rotate(-45deg);
-moz-transform-origin: bottom left;
-webkit-transform: rotate(-45deg);
-webkit-transform-origin: bottom left;
//filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
abbr {
border: 0; // Remove highlighting in firefox
font-size: 0.8em;
}
}
}
}
}
& > tbody {
th {
padding: 0 14px 0 0;
white-space: nowrap;
a {
font-size: 0.8em;
}
}
td {
padding: 2px;
min-width: 1.5em;
line-height: 1.5em;
text-align: center;
a {
width: 1.5em;
height: 1.5em;
display: block;
border-radius: 0.5em;
&.state_ok {
background-color: @colorOk;
}
&.state_pending {
background-color: @colorPending;
}
&.state_warning {
background-color: @colorWarning;
&.handled {
background-color: @colorWarningHandled;
}
}
&.state_critical {
background-color: @colorCritical;
&.handled {
background-color: @colorCriticalHandled;
}
}
&.state_unknown {
background-color: @colorUnknown;
&.handled {
background-color: @colorUnknownHandled;
}
}
}
}
}
}
/* End of monitoring pivot table styles */ /* End of monitoring pivot table styles */
/* Monitoring timeline styles */ /* Monitoring timeline styles */

View File

@ -0,0 +1,38 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
table.service-grid-table {
white-space: nowrap;
th {
a {
color: @colorMainLink;
font-weight: normal;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
td {
text-align: center;
width: 1.5em;
a {
.rounded-corners(0.4em);
display: block;
height: 1.5em;
width: 1.5em;
}
}
th.rotate-45 {
height: 6em;
div {
.transform(translate(0.4em, 2.1em) rotate(315deg));
width: 1.5em;
}
}
}

View File

@ -1,10 +1,12 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller; namespace Icinga\Module\Setup\Controllers;
use Icinga\Module\Setup\WebWizard;
class Setup_IndexController extends Controller use Icinga\Module\Setup\WebWizard;
use Icinga\Web\Controller;
class IndexController extends Controller
{ {
/** /**
* Whether the controller requires the user to be authenticated * Whether the controller requires the user to be authenticated

View File

@ -3,7 +3,7 @@
namespace Icinga\Module\Setup\Forms; namespace Icinga\Module\Setup\Forms;
use PDOException; use Exception;
use Icinga\Web\Form; use Icinga\Web\Form;
use Icinga\Forms\Config\Resource\DbResourceForm; use Icinga\Forms\Config\Resource\DbResourceForm;
use Icinga\Module\Setup\Utils\DbTool; use Icinga\Module\Setup\Utils\DbTool;
@ -112,7 +112,7 @@ class DbResourcePage extends Form
try { try {
$db = new DbTool($this->getValues()); $db = new DbTool($this->getValues());
$db->checkConnectivity(); $db->checkConnectivity();
} catch (PDOException $e) { } catch (Exception $e) {
$this->error(sprintf( $this->error(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'), $this->translate('Failed to successfully validate the configuration: %s'),
$e->getMessage() $e->getMessage()
@ -120,34 +120,49 @@ class DbResourcePage extends Form
return false; return false;
} }
if ($this->getValue('db') === 'pgsql') { $state = true;
if (! $db->isConnected()) { $connectionError = null;
try {
$db->connectToDb();
} catch (PDOException $e) {
$this->warning($this->translate(sprintf(
'Unable to check the server\'s version. This is usually not a critical error as there is'
. ' probably only access to the database permitted which does not exist yet. If you are'
. ' absolutely sure you are running PostgreSQL in a version equal to or newer than 9.1,'
. ' you can skip the validation and safely proceed to the next step. The error was: %s',
$e->getMessage()
)));
return false;
}
}
$version = $db->getServerVersion(); try {
if (version_compare($version, '9.1', '<')) { $db->connectToDb();
$this->error($this->translate(sprintf( } catch (Exception $e) {
'The server\'s version %s is too old. The minimum required version is %s.', $connectionError = $e;
$version, }
'9.1'
if ($connectionError === null && array_search('icinga_instances', $db->listTables(), true) !== false) {
$this->warning($this->translate(
'The database you\'ve configured to use for Icinga Web 2 seems to be the one of Icinga. Please be aware'
. ' that this database configuration is supposed to be used for Icinga Web 2\'s configuration and that'
. ' it is highly recommended to not mix different schemas in the same database. If this is intentional,'
. ' you can skip the validation and ignore this warning. If not, please provide a different database.'
));
$state = false;
}
if ($this->getValue('db') === 'pgsql') {
if ($connectionError !== null) {
$this->warning($this->translate(sprintf(
'Unable to check the server\'s version. This is usually not a critical error as there is'
. ' probably only access to the database permitted which does not exist yet. If you are'
. ' absolutely sure you are running PostgreSQL in a version equal to or newer than 9.1,'
. ' you can skip the validation and safely proceed to the next step. The error was: %s',
$connectionError->getMessage()
))); )));
return false; $state = false;
} else {
$version = $db->getServerVersion();
if (version_compare($version, '9.1', '<')) {
$this->error($this->translate(sprintf(
'The server\'s version %s is too old. The minimum required version is %s.',
$version,
'9.1'
)));
$state = false;
}
} }
} }
return true; return $state;
} }
/** /**

View File

@ -24,9 +24,5 @@ use Icinga\Web\Wizard;
<?php endforeach ?> <?php endforeach ?>
<?= $form->getElement($form->getTokenElementName()); ?> <?= $form->getElement($form->getTokenElementName()); ?>
<?= $form->getElement($form->getUidElementName()); ?> <?= $form->getElement($form->getUidElementName()); ?>
<div class="buttons"> <?= $form->getDisplayGroup('buttons'); ?>
<?= $form->getElement(Wizard::BTN_PREV); ?>
<?= $form->getElement(Wizard::BTN_NEXT); ?>
<?= $form->getElement(Wizard::PROGRESS_ELEMENT); ?>
</div>
</form> </form>

View File

@ -2,6 +2,10 @@
use Icinga\Web\Wizard; use Icinga\Web\Wizard;
if (! $form->getWizard()->getRequirements()->fulfilled()) {
$form->getElement(Wizard::BTN_NEXT)->setAttrib('disabled', 1);
}
?> ?>
<h1>Icinga Web 2</h1> <h1>Icinga Web 2</h1>
<?= $form->getWizard()->getRequirements(true); ?> <?= $form->getWizard()->getRequirements(true); ?>
@ -20,14 +24,12 @@ use Icinga\Web\Wizard;
<?= $form->getElement($form->getTokenElementName()); ?> <?= $form->getElement($form->getTokenElementName()); ?>
<?= $form->getElement($form->getUidElementName()); ?> <?= $form->getElement($form->getUidElementName()); ?>
<div class="buttons"> <div class="buttons">
<?= $form->getElement(Wizard::BTN_PREV); ?>
<?php <?php
$btn = $form->getElement(Wizard::BTN_NEXT); $double = clone $form->getElement(Wizard::BTN_NEXT);
if (! $form->getWizard()->getRequirements()->fulfilled()) { echo $double->setAttrib('class', 'double');
$btn->setAttrib('disabled', 1);
}
echo $btn;
?> ?>
<?= $form->getElement(Wizard::BTN_PREV); ?>
<?= $form->getElement(Wizard::BTN_NEXT); ?>
<?= $form->getElement(Wizard::PROGRESS_ELEMENT); ?> <?= $form->getElement(Wizard::PROGRESS_ELEMENT); ?>
<div class="requirements-refresh"> <div class="requirements-refresh">
<?php $title = $this->translate('You may also need to restart the web-server for the changes to take effect!'); ?> <?php $title = $this->translate('You may also need to restart the web-server for the changes to take effect!'); ?>

View File

@ -2,6 +2,8 @@
use Icinga\Web\Wizard; use Icinga\Web\Wizard;
$form->getElement(Wizard::BTN_NEXT)->setAttrib('class', 'finish');
?> ?>
<p><?= sprintf( <p><?= sprintf(
$this->translate( $this->translate(
@ -31,9 +33,5 @@ use Icinga\Web\Wizard;
> >
<?= $form->getElement($form->getTokenElementName()); ?> <?= $form->getElement($form->getTokenElementName()); ?>
<?= $form->getElement($form->getUidElementName()); ?> <?= $form->getElement($form->getUidElementName()); ?>
<div class="buttons"> <?= $form->getDisplayGroup('buttons'); ?>
<?= $form->getElement(Wizard::BTN_PREV); ?>
<?= $form->getElement(Wizard::BTN_NEXT)->setAttrib('class', 'finish'); ?>
<?= $form->getElement(Wizard::PROGRESS_ELEMENT); ?>
</div>
</form> </form>

View File

@ -271,6 +271,8 @@ class DbTool
$this->config['db'] $this->config['db']
); );
} }
$this->zendConn->getConnection(); // Force connection attempt
} }
/** /**

View File

@ -0,0 +1,19 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
.transform(@transform) {
-webkit-transform: @transform;
-moz-transform: @transform;
-ms-transform: @transform;
-o-transform: @transform;
transform: @transform;
}
.rounded-corners(@border-radius: 0.4em) {
-webkit-border-radius: @border-radius;
-moz-border-radius: @border-radius;
border-radius: @border-radius;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}

View File

@ -4,8 +4,6 @@
"use strict"; "use strict";
var activeMenuId;
Icinga.Behaviors = Icinga.Behaviors || {}; Icinga.Behaviors = Icinga.Behaviors || {};
var Navigation = function (icinga) { var Navigation = function (icinga) {
@ -17,49 +15,93 @@
this.on('mouseenter', '#menu > nav > ul > li', this.menuTitleHovered, this); this.on('mouseenter', '#menu > nav > ul > li', this.menuTitleHovered, this);
this.on('mouseleave', '#sidebar', this.leaveSidebar, this); this.on('mouseleave', '#sidebar', this.leaveSidebar, this);
this.on('rendered', this.onRendered, this); this.on('rendered', this.onRendered, this);
/**
* The DOM-Path of the active item
*
* @see getDomPath
*
* @type {null|Array}
*/
this.active = null;
/**
* The DOM-Path of the hovered item
*
* @see getDomPath
*
* @type {null|Array}
*/
this.hovered = null;
/**
* @type {HTMLElement}
*/
this.element = null;
}; };
Navigation.prototype = new Icinga.EventListener(); Navigation.prototype = new Icinga.EventListener();
/**
* Apply the menu selection and hovering according to the current state
*
* @param evt {Object} The event context
*/
Navigation.prototype.onRendered = function(evt) { Navigation.prototype.onRendered = function(evt) {
var self = evt.data.self; var self = evt.data.self;
// get original source element of the rendered-event this.element = evt.target;
var el = evt.target;
if (activeMenuId) {
// restore old menu state
$('#menu li.active', el).removeClass('active');
var $selectedMenu = $('#' + activeMenuId).addClass('active');
var $outerMenu = $selectedMenu.parent().closest('li');
if ($outerMenu.size()) {
$outerMenu.addClass('active');
}
/* if (! self.active) {
Recreate the html content of the menu item to force the browser to update the layout, or else // There is no stored menu item, therefore it is assumed that this is the first rendering
the link would only be visible as active after another click or page reload in Gecko and WebKit. // of the navigation after the page has been opened.
fixes #7897 // initialise the menu selected by the backend as active.
*/ var $menus = $('#menu li.active', evt.target);
$selectedMenu.html($selectedMenu.html());
} else {
// store menu state
var $menus = $('#menu li.active', el);
if ($menus.size()) { if ($menus.size()) {
activeMenuId = $menus[0].id; $menus.each(function () {
$menus.find('li.active').first().each(function () { self.setActive($(this));
activeMenuId = this.id;
}); });
} else {
// if no item is marked as active, try to select the menu from the current URL
self.setActiveByUrl($('#col1').data('icingaUrl'));
} }
} }
// restore hovered menu after auto-reload self.refresh();
if (self.hovered) { };
var hovered = self.icinga.utils.getElementByDomPath(self.hovered);
/**
* Re-render the menu selection and menu hovering according to the current state
*/
Navigation.prototype.refresh = function() {
// restore selection to current active element
if (this.active) {
var $el = $(this.icinga.utils.getElementByDomPath(this.active));
this.setActive($el);
/*
* Recreate the html content of the menu item to force the browser to update the layout, or else
* the link would only be visible as active after another click or page reload in Gecko and WebKit.
*
* fixes #7897
*/
if ($el.is('li')) {
$el.html($el.html());
}
}
// restore hovered menu to current hovered element
if (this.hovered) {
var hovered = this.icinga.utils.getElementByDomPath(this.hovered);
if (hovered) { if (hovered) {
self.hoverElement($(hovered)); this.hoverElement($(hovered));
} }
} }
}; };
/**
* Handle a link click in the menu
*
* @param event
*/
Navigation.prototype.linkClicked = function(event) { Navigation.prototype.linkClicked = function(event) {
var $a = $(this); var $a = $(this);
var href = $a.attr('href'); var href = $a.attr('href');
@ -71,10 +113,8 @@
if (href.match(/#/)) { if (href.match(/#/)) {
// ...it may be a menu section without a dedicated link. // ...it may be a menu section without a dedicated link.
// Switch the active menu item: // Switch the active menu item:
self.setActive($a);
$li = $a.closest('li'); $li = $a.closest('li');
$('#menu .active').removeClass('active');
$li.addClass('active');
activeMenuId = $($li).attr('id');
if ($li.hasClass('hover')) { if ($li.hasClass('hover')) {
$li.removeClass('hover'); $li.removeClass('hover');
} }
@ -86,7 +126,7 @@
return; return;
} }
} else { } else {
activeMenuId = $(event.target).closest('li').attr('id'); self.setActive($(event.target));
} }
// update target url of the menu container to the clicked link // update target url of the menu container to the clicked link
var $menu = $('#menu'); var $menu = $('#menu');
@ -95,9 +135,82 @@
$menu.data('icinga-url', menuDataUrl); $menu.data('icinga-url', menuDataUrl);
}; };
/**
* Activate a menu item based on the current URL
*
* Activate a menu item that is an exact match or fall back to items that match the base URL
*
* @param url {String} The url to match
*/
Navigation.prototype.setActiveByUrl = function(url) { Navigation.prototype.setActiveByUrl = function(url) {
this.resetActive();
// try to active the first item that has an exact URL match
this.setActive($('#menu [href="' + url + '"]')); this.setActive($('#menu [href="' + url + '"]'));
// the url may point to the search field, which must be activated too
if (! this.active) {
this.setActive($('#menu form[action="' + this.icinga.utils.parseUrl(url).path + '"]'));
}
// some urls may have custom filters which won't match any menu item, in that case search
// for a menu item that points to the base action without any filters
if (! this.active) {
this.setActive($('#menu [href="' + this.icinga.utils.parseUrl(url).path + '"]').first());
}
// if no item with the base action exists, activate the first URL that beings with the base path
// but may have different filters
if (! this.active) {
this.setActive($('#menu [href^="' + this.icinga.utils.parseUrl(url).path + '"]').first());
}
};
/**
* Try to select a new URL by
*
* @param url
*/
Navigation.prototype.trySetActiveByUrl = function(url) {
var active = this.active;
this.setActiveByUrl(url);
if (! this.active && active) {
this.setActive($(this.icinga.utils.getElementByDomPath(active)));
}
};
/**
* Remove all active elements
*/
Navigation.prototype.clear = function() {
// menu items
$('#menu li.active', this.element).removeClass('active');
// search fields
$('#menu input.active', this.element).removeClass('active');
};
/**
* Select all menu items in the selector as active and unfold surrounding menus when necessary
*
* @param $item {jQuery} The jQuery selector
*/
Navigation.prototype.select = function($item) {
// support selecting the url of the menu entry
var $input = $item.find('input');
$item = $item.closest('li');
if ($item.length) {
// select the current item
var $selectedMenu = $item.addClass('active');
// unfold the containing menu
var $outerMenu = $selectedMenu.parent().closest('li');
if ($outerMenu.size()) {
$outerMenu.addClass('active');
}
} else if ($input.length) {
$input.addClass('active');
}
}; };
/** /**
@ -106,14 +219,59 @@
* @param $el {jQuery} A selector pointing to the active element * @param $el {jQuery} A selector pointing to the active element
*/ */
Navigation.prototype.setActive = function($el) { Navigation.prototype.setActive = function($el) {
$el.closest('li').addClass('active'); this.clear();
$el.parents('li').addClass('active'); this.select($el);
activeMenuId = $el.closest('li').attr('id'); if ($el.closest('li')[0]) {
this.active = this.icinga.utils.getDomPath($el.closest('li')[0]);
} else if ($el.find('input')[0]) {
this.active = this.icinga.utils.getDomPath($el[0]);
} else {
this.active = null;
}
// TODO: push to history
}; };
/**
* Reset the active element to nothing
*/
Navigation.prototype.resetActive = function() { Navigation.prototype.resetActive = function() {
$('#menu .active').removeClass('active'); this.clear();
activeMenuId = null; this.active = null;
};
/**
* Called when the history changes
*
* @param url The url of the new state
* @param data The active menu item of the new state
*/
Navigation.prototype.onPopState = function (url, data) {
// 1. get selection data and set active menu
console.log('popstate:', data);
if (data) {
var active = this.icinga.utils.getElementByDomPath(data);
if (!active) {
this.logger.fail(
'Could not restore active menu from history, path in DOM not found.',
data,
url
);
return;
}
this.setActive($(active));
} else {
this.resetActive();
}
};
/**
* Called when the current state gets pushed onto the history, can return a value
* to be preserved as the current state
*
* @returns {null|Array} The currently active menu item
*/
Navigation.prototype.onPushState = function () {
return this.active;
}; };
Navigation.prototype.menuTitleHovered = function(event) { Navigation.prototype.menuTitleHovered = function(event) {

View File

@ -228,11 +228,11 @@
icinga.logger.debug('events/submitForm: Button is event.currentTarget'); icinga.logger.debug('events/submitForm: Button is event.currentTarget');
} }
if ($el && ($el.is('input[type=submit]') || $el.is('button[type=submit]')) && $el.is(':focus')) { if ($el && ($el.is('input[type=submit]') || $el.is('button[type=submit]'))) {
$button = $el; $button = $el;
} else { } else {
icinga.logger.debug( icinga.logger.debug(
'events/submitForm: Can not determine submit button, using the last one in form' 'events/submitForm: Can not determine submit button, using the first one in form'
); );
} }
} }
@ -252,7 +252,7 @@
} }
if ($button.length === 0) { if ($button.length === 0) {
$button = $('button[type=submit]', $form).add('input[type=submit]', $form).last(); $button = $('input[type=submit]', $form).add('button[type=submit]', $form).first();
} }
if ($button.length) { if ($button.length) {

View File

@ -4,7 +4,6 @@
* Icinga.History * Icinga.History
* *
* This is where we care about the browser History API * This is where we care about the browser History API
*
*/ */
(function (Icinga, $) { (function (Icinga, $) {
@ -89,7 +88,6 @@
} }
}); });
// TODO: update navigation
// Did we find any URL? Then push it! // Did we find any URL? Then push it!
if (url !== '') { if (url !== '') {
this.push(url); this.push(url);
@ -110,7 +108,26 @@
return; return;
} }
this.lastPushUrl = url; this.lastPushUrl = url;
window.history.pushState({icinga: true}, null, url); window.history.pushState(
this.getBehaviorState(),
null,
url
);
},
/**
* Fetch the current state of all JS behaviors that need history support
*
* @return {Object} A key-value map, mapping behavior names to state
*/
getBehaviorState: function () {
var data = {};
$.each(this.icinga.behaviors, function (i, behavior) {
if (behavior.onPushState instanceof Function) {
data[i] = behavior.onPushState();
}
});
return data;
}, },
/** /**
@ -143,6 +160,13 @@
self.lastPushUrl = location.href; self.lastPushUrl = location.href;
self.applyLocationBar(); self.applyLocationBar();
// notify behaviors of the state change
$.each(this.icinga.behaviors, function (i, behavior) {
if (behavior.onPopState instanceof Function && history.state) {
behavior.onPopState(location.href, history.state[i]);
}
});
}, },
applyLocationBar: function (onload) { applyLocationBar: function (onload) {

View File

@ -570,34 +570,26 @@
if (! req.autorefresh) { if (! req.autorefresh) {
// TODO: Hook for response/url? // TODO: Hook for response/url?
var url = req.url; var url = req.url;
if (req.$target[0].id === 'col1') {
self.icinga.behaviors.navigation.trySetActiveByUrl(url);
}
var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]');
var $matches = $.merge($('[href="' + url + '"]'), $forms); var $matches = $.merge($('[href="' + url + '"]'), $forms);
$matches.each(function (idx, el) {
if ($(el).closest('#menu').length) {
if (req.$target[0].id === 'col1') {
self.icinga.behaviors.navigation.resetActive();
}
}
});
$matches.each(function (idx, el) { $matches.each(function (idx, el) {
var $el = $(el); var $el = $(el);
if ($el.closest('#menu').length) { if ($el.closest('#menu').length) {
if ($el.is('form')) { if ($el.is('form')) {
$('input', $el).addClass('active'); $('input', $el).addClass('active');
} else {
if (req.$target[0].id === 'col1') {
self.icinga.behaviors.navigation.setActive($el);
}
} }
// Interrupt .each, only on menu item shall be active // Interrupt .each, only one menu item shall be active
return false; return false;
} }
}); });
} }
// Update history when necessary. Don't do so for requests triggered // Update history when necessary
// by history or autorefresh events
if (! req.autorefresh && req.addToHistory) { if (! req.autorefresh && req.addToHistory) {
if (req.$target.hasClass('container')) { if (req.$target.hasClass('container')) {
// We only want to care about top-level containers // We only want to care about top-level containers
@ -640,9 +632,10 @@
/* /*
* Test if a manual actions comes in and autorefresh is active: Stop refreshing * Test if a manual actions comes in and autorefresh is active: Stop refreshing
*/ */
if (req.addToHistory && ! req.autorefresh && req.$target.data('icingaRefresh') > 0) { if (req.addToHistory && ! req.autorefresh) {
req.$target.data('icingaRefresh', 0); req.$target.data('icingaRefresh', 0);
req.$target.data('icingaUrl', url); req.$target.data('icingaUrl', url);
icinga.history.pushCurrentState();
} }
if (typeof req.progressTimer !== 'undefined') { if (typeof req.progressTimer !== 'undefined') {

View File

@ -154,7 +154,7 @@
var kill = this.cutContainer($('#col1')); var kill = this.cutContainer($('#col1'));
this.pasteContainer($('#col1'), col2); this.pasteContainer($('#col1'), col2);
this.fixControls(); this.fixControls();
this.icinga.behaviors.navigation.setActiveByUrl($('#col1').data('icingaUrl')); this.icinga.behaviors.navigation.trySetActiveByUrl($('#col1').data('icingaUrl'));
}, },
cutContainer: function ($col) { cutContainer: function ($col) {
@ -480,8 +480,7 @@
* @param value {String} The value to set, can be '1', '0' and 'unchanged' * @param value {String} The value to set, can be '1', '0' and 'unchanged'
* @param $checkbox {jQuery} The checkbox * @param $checkbox {jQuery} The checkbox
*/ */
setTriState: function(value, $checkbox) setTriState: function(value, $checkbox) {
{
switch (value) { switch (value) {
case ('1'): case ('1'):
$checkbox.prop('checked', true).prop('indeterminate', false); $checkbox.prop('checked', true).prop('indeterminate', false);

View File

@ -76,20 +76,19 @@
* @param {selector} element The element to check * @param {selector} element The element to check
* @returns {Boolean} * @returns {Boolean}
*/ */
isVisible: function(element) isVisible: function(element) {
{ var $element = $(element);
var $element = $(element); if (!$element.length) {
if (!$element.length) { return false;
return false; }
}
var docViewTop = $(window).scrollTop(); var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height(); var docViewBottom = docViewTop + $(window).height();
var elemTop = $element.offset().top; var elemTop = $element.offset().top;
var elemBottom = elemTop + $element.height(); var elemBottom = elemTop + $element.height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) && return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) &&
(elemBottom <= docViewBottom) && (elemTop >= docViewTop)); (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}, },
getUrlHelper: function () { getUrlHelper: function () {

View File

@ -22,9 +22,9 @@ require_once 'Mockery/Loader.php';
$mockeryLoader = new \Mockery\Loader; $mockeryLoader = new \Mockery\Loader;
$mockeryLoader->register(); $mockeryLoader->register();
require_once($libraryPath . '/Icinga/Application/Loader.php'); require_once($libraryPath . '/Icinga/Application/ClassLoader.php');
$loader = new Icinga\Application\Loader(); $loader = new Icinga\Application\ClassLoader();
$loader->registerNamespace('Tests', $testLibraryPath); $loader->registerNamespace('Tests', $testLibraryPath);
$loader->registerNamespace('Icinga', $libraryPath . '/Icinga'); $loader->registerNamespace('Icinga', $libraryPath . '/Icinga');
$loader->registerNamespace('Icinga\\Forms', $applicationPath . '/forms'); $loader->registerNamespace('Icinga\\Forms', $applicationPath . '/forms');

View File

@ -4,9 +4,9 @@
namespace Tests\Icinga\Application; namespace Tests\Icinga\Application;
use Icinga\Test\BaseTestCase; use Icinga\Test\BaseTestCase;
use Icinga\Application\Loader; use Icinga\Application\ClassLoader;
class LoaderTest extends BaseTestCase class ClassLoaderTest extends BaseTestCase
{ {
private static $classFile = 'test/My/Library/TestStruct.php'; private static $classFile = 'test/My/Library/TestStruct.php';
@ -43,7 +43,7 @@ EOD;
public function testObjectCreation1() public function testObjectCreation1()
{ {
$loader = new Loader(); $loader = new ClassLoader();
$loader->register(); $loader->register();
$check = false; $check = false;
@ -56,7 +56,7 @@ EOD;
} }
$this->assertTrue($check); $this->assertTrue($check);
$loader->unRegister(); $loader->unregister();
$check = true; $check = true;
foreach (spl_autoload_functions() as $functions) { foreach (spl_autoload_functions() as $functions) {
@ -71,7 +71,7 @@ EOD;
public function testNamespaces() public function testNamespaces()
{ {
$loader = new Loader(); $loader = new ClassLoader();
$loader->registerNamespace('Test\\Laola', '/tmp'); $loader->registerNamespace('Test\\Laola', '/tmp');
$loader->registerNamespace('Dings\\Var', '/var/tmp'); $loader->registerNamespace('Dings\\Var', '/var/tmp');
@ -89,7 +89,7 @@ EOD;
$classFile = $this->baseDir. self::$classFile; $classFile = $this->baseDir. self::$classFile;
$this->assertFileExists($classFile); $this->assertFileExists($classFile);
$loader = new Loader(); $loader = new ClassLoader();
$loader->registerNamespace('My\\Library', dirname($classFile)); $loader->registerNamespace('My\\Library', dirname($classFile));
$this->assertFalse($loader->loadClass('DOES\\NOT\\EXISTS')); $this->assertFalse($loader->loadClass('DOES\\NOT\\EXISTS'));
$this->assertTrue($loader->loadClass('My\\Library\\TestStruct')); $this->assertTrue($loader->loadClass('My\\Library\\TestStruct'));
@ -100,20 +100,11 @@ EOD;
$classFile = $this->baseDir. self::$classFile; $classFile = $this->baseDir. self::$classFile;
$this->assertFileExists($classFile); $this->assertFileExists($classFile);
$loader = new Loader(); $loader = new ClassLoader();
$loader->registerNamespace('My\\Library', dirname($classFile)); $loader->registerNamespace('My\\Library', dirname($classFile));
$loader->register(); $loader->register();
$o = new \My\Library\TestStruct(); $o = new \My\Library\TestStruct();
$this->assertTrue($o->testFlag()); $this->assertTrue($o->testFlag());
} }
/**
* @expectedException Icinga\Exception\ProgrammingError
*/
public function testNonexistingDirectory()
{
$loader = new Loader();
$loader->registerNamespace('My\\Library', '/trullalla/123');
}
} }