diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index d500f23e6..ad8603273 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -26,7 +26,7 @@ # namespace Icinga\Application\Controllers; -use Icinga\Web\ActionController; +use Icinga\Web\Controller\ActionController; use Icinga\Authentication\Credentials; use Icinga\Authentication\Manager as AuthManager; use Icinga\Form\Authentication\LoginForm; diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php new file mode 100644 index 000000000..6418d1ad8 --- /dev/null +++ b/application/controllers/ConfigController.php @@ -0,0 +1,112 @@ + + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +use Icinga\Application\Benchmark; +use Icinga\Authentication\Manager; +use Icinga\Web\Controller\BaseConfigController; +use Icinga\Web\Widget\Tab; +use Icinga\Web\Url; +use Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; +use Icinga\Application\Icinga; + +/** + * Application wide controller for application preferences + * + */ +class ConfigController extends BaseConfigController +{ + /** + * Create tabs for this configuration controller + * + * @return array + * @see BaseConfigController::createProvidedTabs + */ + public static function createProvidedTabs() + { + return array( + "index" => new Tab( + array( + "name" => "index", + "title" => "Configuration", + "iconCls" => "wrench", + "url" => Url::fromPath("/config") + ) + ), + "modules" => new Tab( + array( + "name" => "modules", + "title" => "Modules", + "iconCls" => "puzzle-piece", + "url" => Url::fromPath("/config/moduleoverview") + ) + ) + ); + + } + + /** + * Index action, entry point for configuration + * @TODO: Implement configuration interface (#3777) + */ + public function indexAction() + { + + } + + /** + * Display the list of all modules + */ + public function moduleoverviewAction() + { + $this->view->modules = Icinga::app()->getModuleManager()->select() + ->from('modules') + ->order('name'); + $this->render('module/overview'); + } + + /** + * Enable a specific module provided by the 'name' param + */ + public function moduleenableAction() + { + $manager = Icinga::app()->getModuleManager(); + $manager->enableModule($this->_getParam('name')); + $manager->loadModule($this->_getParam('name')); + + $this->redirectNow('config/moduleoverview?_render=body'); + } + + /** + * Disable a module specific module provided by the 'name' param + */ + public function moduledisableAction() + { + $manager = Icinga::app()->getModuleManager(); + $manager->disableModule($this->_getParam('name')); + $this->redirectNow('config/moduleoverview?_render=body'); + } +} +// @codingStandardsIgnoreEnd \ No newline at end of file diff --git a/application/controllers/DashboardController.php b/application/controllers/DashboardController.php index 864eb1281..65a132890 100644 --- a/application/controllers/DashboardController.php +++ b/application/controllers/DashboardController.php @@ -27,7 +27,7 @@ */ // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Web\ActionController; +use Icinga\Web\Controller\ActionController; use Icinga\Web\Url; use Icinga\Application\Icinga; use Icinga\Web\Widget\Dashboard; diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php index 42756de86..8fdfd99b8 100755 --- a/application/controllers/ErrorController.php +++ b/application/controllers/ErrorController.php @@ -26,7 +26,7 @@ # namespace Icinga\Application\Controllers; -use Icinga\Web\ActionController; +use Icinga\Web\Controller\ActionController; /** * Class ErrorController diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index 7b3e68ad5..9863efd09 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -29,7 +29,7 @@ # namespace Icinga\Application\Controllers; -use Icinga\Web\ActionController; +use Icinga\Web\Controller\ActionController; use Icinga\Application\Icinga; /** @@ -38,6 +38,7 @@ use Icinga\Application\Icinga; */ class IndexController extends ActionController { + /** * @var bool */ diff --git a/application/controllers/ModulesController.php b/application/controllers/ModulesController.php deleted file mode 100644 index fc5fdfdaf..000000000 --- a/application/controllers/ModulesController.php +++ /dev/null @@ -1,107 +0,0 @@ - - * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 - * @author Icinga Development Team - */ -// {{{ICINGA_LICENSE_HEADER}}} - - -# namespace Icinga\Application\Controllers; - -use Icinga\Web\ActionController; -use Icinga\Application\Icinga; -use Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; -use Icinga\Web\Widget\Tabs; - -/** - * Handle module depending frontend actions - */ -class ModulesController extends ActionController -{ - /** - * @var ModuleManager - */ - protected $manager; - - /** - * Setup this controller - * @see ZfActionController::init - */ - public function init() - { - $this->manager = Icinga::app()->getModuleManager(); - } - - /** - * Display a list of all modules - */ - public function indexAction() - { - $tabBuilder = new ConfigurationTabBuilder(new Tabs()); - - $tabBuilder->build(); - - $this->view->tabs = $tabBuilder->getTabs(); - - $this->view->modules = $this->manager->select() - ->from('modules') - ->order('name'); - $this->render('overview'); - } - - /** - * Alias for index - * - * @see self::indexAction - */ - public function overviewAction() - { - $this->indexAction(); - - } - - /** - * Enable a module - */ - public function enableAction() - { - $this->manager->enableModule($this->_getParam('name')); - $this->manager->loadModule($this->_getParam('name')); - $this->getResponse()->setHeader('X-Icinga-Enable-Module', $this->_getParam('name')); - $this->redirectNow('modules/overview?_render=body'); - - } - - /** - * Disable a module - */ - public function disableAction() - { - $this->manager->disableModule($this->_getParam('name')); - $this->redirectNow('modules/overview?_render=body'); - } -} - -// @codingStandardsIgnoreEnd diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php new file mode 100644 index 000000000..bd3ace003 --- /dev/null +++ b/application/controllers/PreferenceController.php @@ -0,0 +1,70 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +use \Icinga\Web\Controller\BasePreferenceController; +use \Icinga\Web\Widget\Tab; +use \Icinga\Web\Url; + +/** + * Application wide preference controller for user preferences + * + */ +class PreferenceController extends BasePreferenceController +{ + + /** + * Create tabs for this preference controller + * + * @return array + * @see BasePreferenceController::createProvidedTabs + */ + public static function createProvidedTabs() + { + return array( + "preference" => new Tab( + array( + "name" => "preferences", + "iconCls" => "user", + "title" => "Preferences", + "url" => Url::fromPath("/preference") + ) + ) + ); + } + + /** + * @TODO: Implement User preferences (feature #5425) + * + */ + public function indexAction() + { + + } +} +// @codingStandardsIgnoreEnd \ No newline at end of file diff --git a/application/controllers/StaticController.php b/application/controllers/StaticController.php index bb86e60ef..8b1007d39 100644 --- a/application/controllers/StaticController.php +++ b/application/controllers/StaticController.php @@ -27,7 +27,7 @@ */ // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Web\ActionController; +use Icinga\Web\Controller\ActionController; use Icinga\Application\Icinga, Zend_Controller_Action_Exception as ActionException; diff --git a/application/layouts/scripts/parts/topbar.phtml b/application/layouts/scripts/parts/topbar.phtml index 4fb8f41ed..049a2ab3a 100755 --- a/application/layouts/scripts/parts/topbar.phtml +++ b/application/layouts/scripts/parts/topbar.phtml @@ -17,7 +17,7 @@ diff --git a/application/views/scripts/config/index.phtml b/application/views/scripts/config/index.phtml new file mode 100644 index 000000000..ef3c458db --- /dev/null +++ b/application/views/scripts/config/index.phtml @@ -0,0 +1,12 @@ +tabs->render($this); ?> + +

Configuration

+ +
+

Dear developer

+
+

+ Add shortcuts for often used configuration options so users can quickly change settings without having to + search in the tab overview +

+
\ No newline at end of file diff --git a/application/views/scripts/modules/overview.phtml b/application/views/scripts/config/module/overview.phtml similarity index 51% rename from application/views/scripts/modules/overview.phtml rename to application/views/scripts/config/module/overview.phtml index 5978530e5..a6d2a5f83 100644 --- a/application/views/scripts/modules/overview.phtml +++ b/application/views/scripts/config/module/overview.phtml @@ -1,4 +1,6 @@ modules->limit(10); $modules = $this->modules->paginate(); @@ -11,22 +13,19 @@ $modules = $this->modules->paginate(); + $module->name))->getAbsoluteUrl(); + $disableUrl = Url::fromPath('config/moduledisable/',array('name' => $module->name))->getAbsoluteUrl(); + ?>
enabled): ?> - qlink( - $module->name, - 'modules/disable', - array('name' => $module->name), - array('target'=>'body') - ) ?> + + escape($module->name); ?> + - qlink( - $module->name, - 'modules/enable', - array('name' => $module->name), - array('target'=>'body') - ) ?> + + escape($module->name); ?> (enabled diff --git a/application/views/scripts/modules/show.phtml b/application/views/scripts/config/module/show.phtml similarity index 100% rename from application/views/scripts/modules/show.phtml rename to application/views/scripts/config/module/show.phtml diff --git a/application/views/scripts/configuration/index.phtml b/application/views/scripts/configuration/index.phtml deleted file mode 100644 index e6445410a..000000000 --- a/application/views/scripts/configuration/index.phtml +++ /dev/null @@ -1,12 +0,0 @@ -tabs->render($this); ?> - -

Configuration

- -

- This is the configuration over page. Modules can register their handler to - provide own controllers for configuration. -

- -

- Many thanks for your attention! -

\ No newline at end of file diff --git a/application/views/scripts/preference/index.phtml b/application/views/scripts/preference/index.phtml new file mode 100644 index 000000000..81dc0fb8c --- /dev/null +++ b/application/views/scripts/preference/index.phtml @@ -0,0 +1,3 @@ +tabs->render($this); ?> + +

Preferences

\ No newline at end of file diff --git a/config/menu.ini b/config/menu.ini index b4e42e7b5..a1a6b1a89 100755 --- a/config/menu.ini +++ b/config/menu.ini @@ -1,4 +1,4 @@ [menu] Dashboard = "/dashboard/index" -Configuration = "/configuration/index" +Configuration = "/config/index" diff --git a/doc/module/configuration_and_preferences.md b/doc/module/configuration_and_preferences.md new file mode 100644 index 000000000..a259e524a --- /dev/null +++ b/doc/module/configuration_and_preferences.md @@ -0,0 +1,57 @@ +# Module Development: Configuration and Preferences Dialogs + +When developing modules, you might want your module's configuration and preferences dialogs to appear in the Icinga Web +Configuration/Preferences interface. This is rather easy to accomplish and should be the preferred way to allow user's +to customize your module. + +## Terminology + +When talking about 'Configuration' and 'Preference', we have a clear distinction between those words: + +- **Configurations** are application/module wide settings that affect every user when being changed. This could be + the data backend of your module or other 'global' settings that affect everyone when being changed +- **Preferences** are settings a user can set for *his* account only, like the page size of pagination, entry points, etc. + + +## Usage + +The two base classes for preferences and configurations are \Icinga\Web\Controller\BasePreferenceController for preferences and + \Icinga\Web\Controller\BaseConfigController for configurations. + +If you want to create a preference or configuration panel you have to create a ConfigController and/or PreferenceController + in your Module's a controller directory and make it a subclass of BaseConfigController or BasePreferenceController. + +Those controllers can be used like normal controllers, with two exceptions: + +- If you want your module to appear as a tab in the applications configuration/preference interface you have to implement + the static createProvidedTabs function that returns an array of tabs to be displayed +- The init() method of the base class must be called in order to make sure tabs are collected and the view's tabs variable + is populated + +## Example + +We'll just provide an example for ConfigControllers here, as PreferenceController are the same with a different name + + use \Icinga\Web\Controller\BaseConfigController; + use \Icinga\Web\Widget\Tab; + use \Icinga\Web\Url; + + class My_ConfigController extends BaseConfigController { + + static public function createProvidedTabs() + { + return array( + "myModuleTab" => new Tab(array( + "name" => "myModuleTab", // the internal name of the tab + "iconCls" => "myicon", // the icon to be displayed + "title" => "The tab title", // The title of the configuration's tab + "url" => Url::fromPath("/myModule/config") // The Url that will ne called (can also be just a path) + )) + ); + } + + public function indexAction() + { + // create the form here + } + } diff --git a/library/Icinga/Application/Modules/Manager.php b/library/Icinga/Application/Modules/Manager.php index 96a6234c8..3a83affc9 100644 --- a/library/Icinga/Application/Modules/Manager.php +++ b/library/Icinga/Application/Modules/Manager.php @@ -1,28 +1,79 @@ app = $app; @@ -38,11 +89,17 @@ class Manager $this->detectEnabledModules(); } - protected function prepareEssentials($moduleDir) + /** + * Set the module dir and checks for existence + * + * @param string $moduleDir The module directory to set for the module manager + * @throws \Icinga\Exception\ProgrammingError + */ + private function prepareEssentials($moduleDir) { $this->enableDir = $moduleDir; - if (! file_exists($this->enableDir) || ! is_dir($this->enableDir)) { + if (!file_exists($this->enableDir) || !is_dir($this->enableDir)) { throw new ProgrammingError( sprintf( 'Missing module directory: %s', @@ -52,13 +109,21 @@ class Manager } } + /** + * Query interface for the module manager + * + * @return \Icinga\Data\ArrayQuery + */ public function select() { $source = new \Icinga\Data\ArrayDatasource($this->getModuleInfo()); return $source->select(); } - protected function detectEnabledModules() + /** + * Check for enabled modules and update the internal $enabledDirs property with the enabled modules + */ + private function detectEnabledModules() { $fh = opendir($this->enableDir); @@ -70,12 +135,23 @@ class Manager } $link = $this->enableDir . '/' . $file; - if (! is_link($link)) { + if (!is_link($link)) { + Logger::warn( + 'Found invalid module in enabledModule directory "%s": "%s" is not a symlink', + $this->enableDir, + $link + ); continue; } $dir = realpath($link); - if (! file_exists($dir) || ! is_dir($dir)) { + if (!file_exists($dir) || !is_dir($dir)) { + Logger::warn( + 'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"', + $this->enableDir, + $link, + $dir + ); continue; } @@ -83,6 +159,12 @@ class Manager } } + /** + * Try to set all enabled modules in loaded sate + * + * @return self + * @see Manager::loadModule() + */ public function loadEnabledModules() { foreach ($this->listEnabledModules() as $name) { @@ -91,6 +173,15 @@ class Manager return $this; } + /** + * Try to load the module and register it in the application + * + * @param string $name The name of the module to load + * @param mixed $moduleBase Alternative class to use instead of \Icinga\Application\Modules\Module for + * instantiating modules, used for testing + * + * @return self + */ public function loadModule($name, $moduleBase = null) { if ($this->hasLoaded($name)) { @@ -108,9 +199,18 @@ class Manager return $this; } + /** + * Set the given module to the enabled state + * + * @param string $name The module to enable + * + * @return self + * @throws \Icinga\Exception\ConfigurationError When trying to enable a module that is not installed + * @throws \Icinga\Exception\SystemPermissionException When insufficient permissions for the application exist + */ public function enableModule($name) { - if (! $this->hasInstalled($name)) { + if (!$this->hasInstalled($name)) { throw new ConfigurationError( sprintf( "Cannot enable module '%s' as it isn't installed", @@ -122,7 +222,7 @@ class Manager clearstatcache(true); $target = $this->installedBaseDirs[$name]; $link = $this->enableDir . '/' . $name; - if (! is_writable($this->enableDir)) { + if (!is_writable($this->enableDir)) { throw new SystemPermissionException( "Insufficient system permissions for enabling modules", "write", @@ -142,32 +242,42 @@ class Manager return $this; } + /** + * Disable the given module and remove it's enabled state + * + * @param string $name The name of the module to disable + * + * @return self + * + * @throws \Icinga\Exception\ConfigurationError When the module is not installed or it's not symlinked + * @throws \Icinga\Exception\SystemPermissionException When the module can't be disabled + */ public function disableModule($name) { - if (! $this->hasEnabled($name)) { + if (!$this->hasEnabled($name)) { return $this; } - if (! is_writable($this->enableDir)) { + if (!is_writable($this->enableDir)) { throw new SystemPermissionException("Can't write the module directory", "write", $this->enableDir); return $this; } $link = $this->enableDir . '/' . $name; if (!file_exists($link)) { - throw new ConfigurationError("The module $name could not be found, can't disable it"); + throw new ConfigurationError('The module ' . $name . ' could not be found, can\'t disable it'); } if (!is_link($link)) { throw new ConfigurationError( - "The module $name can't be disabled as this would delete the whole module. ". - "It looks like you have installed this module manually and moved it to your module folder.". - "In order to dynamically enable and disable modules, you have to create a symlink to ". - "the enabled_modules folder" + 'The module ' . $name . ' can\'t be disabled as this would delete the whole module. ' + . 'It looks like you have installed this module manually and moved it to your module folder. ' + . 'In order to dynamically enable and disable modules, you have to create a symlink to ' + . 'the enabled_modules folder' ); } - + if (file_exists($link) && is_link($link)) { if (!@unlink($link)) { $error = error_get_last(); - throw new SystemPermissionException($error["message"], "unlink", $link); + throw new SystemPermissionException($error['message'], 'unlink', $link); } } else { @@ -176,11 +286,16 @@ class Manager return $this; } - public function getModuleConfigDir($name) - { - return $this->getModuleDir($name, '/config'); - } - + /** + * Return the directory of the given module as a string, optionally with a given sub directoy + * + * @param string $name The module name to return the module directory of + * @param string $subdir The sub directory to append to the path + * + * @return string + * + * @throws \Icinga\Exception\ProgrammingError When the module is not installed or existing + */ public function getModuleDir($name, $subdir = '') { if ($this->hasEnabled($name)) { @@ -199,6 +314,13 @@ class Manager ); } + /** + * Return true when the module with the given name is installed, otherwise false + * + * @param string $name The module to check for being installed + * + * @return bool + */ public function hasInstalled($name) { if ($this->installedBaseDirs === null) { @@ -207,24 +329,54 @@ class Manager return array_key_exists($name, $this->installedBaseDirs); } + /** + * Return true when the given module is in enabled state, otherwise false + * + * @param string $name The module to check for being enabled + * + * @return bool + */ public function hasEnabled($name) { return array_key_exists($name, $this->enabledDirs); } + /** + * Return true when the module is in loaded state, otherwise false + * + * @param string $name The module to check for being loaded + * + * @return bool + */ public function hasLoaded($name) { return array_key_exists($name, $this->loadedModules); } + /** + * Return an array containing all loaded modules + * + * @return array + * + * @see \Icinga\Application\Modules\Module + */ public function getLoadedModules() { return $this->loadedModules; } + /** + * Return the module instance of the given module when it is loaded + * + * @param string $name The module name to return + * + * @return \Icinga\Application\Modules\Module + * + * @throws \Icinga\Exception\ProgrammingError When the module hasn't been loaded + */ public function getModule($name) { - if (! $this->hasLoaded($name)) { + if (!$this->hasLoaded($name)) { throw new ProgrammingError( sprintf( 'Cannot access module %s as it hasn\'t been loaded', @@ -235,15 +387,26 @@ class Manager return $this->loadedModules[$name]; } + /** + * Return an array containing information objects for each available module + * + * Each entry has the following fields + * * name, name of the module as a string + * * path, path where the module is located as a string + * * enabled, whether the module is enabled or not as a boolean + * * loaded, whether the module is loaded or not as a boolean + * + * @return array + */ public function getModuleInfo() { $installed = $this->listInstalledModules(); - + $info = array(); if ($installed === null) { return $info; } - + foreach ($installed as $name) { $info[] = (object) array( 'name' => $name, @@ -255,27 +418,51 @@ class Manager return $info; } + /** + * Return an array containing all enabled module names as strings + * + * @return array + */ public function listEnabledModules() { return array_keys($this->enabledDirs); } + /** + * Return an array containing all loaded module names as strings + * + * @return array + */ public function listLoadedModules() { return array_keys($this->loadedModules); } + /** + * Return an array containing all installled module names as strings + * + * Calls detectInstalledModules() if no module discovery has been performed yet + * + * @return array + * + * @see detectInstalledModules() + */ public function listInstalledModules() { if ($this->installedBaseDirs === null) { $this->detectInstalledModules(); } - + if ($this->installedBaseDirs !== null) { return array_keys($this->installedBaseDirs); } } + /** + * Detect installed modules from every path provided in modulePaths + * + * @return self + */ public function detectInstalledModules() { foreach ($this->modulePaths as $basedir) { @@ -283,7 +470,7 @@ class Manager if ($fh === false) { return $this; } - + while ($name = readdir($fh)) { if ($name[0] === '.') { continue; @@ -293,5 +480,6 @@ class Manager } } } + return $this; } } diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 40ce159b0..86f0546e7 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -2,24 +2,24 @@ // {{{ICINGA_LICENSE_HEADER}}} /** * This file is part of Icinga 2 Web. - * + * * Icinga 2 Web - Head for multiple monitoring backends. * Copyright (C) 2013 Icinga Development Team - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * + * * @copyright 2013 Icinga Development Team * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 * @author Icinga Development Team @@ -28,11 +28,11 @@ namespace Icinga\Application\Modules; -use Icinga\Application\ApplicationBootstrap; +use \Icinga\Application\ApplicationBootstrap; use \Icinga\Application\Config; -use Icinga\Application\Icinga; -use Icinga\Web\Hook; -use Zend_Controller_Router_Route as Route; +use \Icinga\Application\Icinga; +use \Icinga\Web\Hook; +use \Zend_Controller_Router_Route as Route; /** * Module handling @@ -107,9 +107,9 @@ class Module /** * Create a new module object * - * @param ApplicationBootstrap $app - * @param string $name - * @param strinb $basedir + * @param ApplicationBootstrap $app + * @param string $name + * @param string $basedir */ public function __construct(ApplicationBootstrap $app, $name, $basedir) { @@ -141,8 +141,9 @@ class Module /** * Test for an enabled module by name * - * @param string $name - * @return boolean + * @param string $name + * + * @return boolean */ public static function exists($name) { @@ -152,9 +153,12 @@ class Module /** * Get module by name * - * @param string $name - * @param bool $autoload - * @return mixed + * @param string $name + * @param bool $autoload + * + * @return mixed + * + * @throws \Icinga\Exception\ProgrammingError When the module is not yet loaded */ public static function get($name, $autoload = false) { @@ -164,7 +168,7 @@ class Module $manager->loadModule($name); } } - // @throws ProgrammingError: + // Throws ProgrammingError when the module is not yet loaded return $manager->getModule($name); } @@ -200,6 +204,7 @@ class Module /** * Getter for base directory + * * @return string */ public function getBaseDir() @@ -207,6 +212,11 @@ class Module return $this->basedir; } + public function getControllerDir() + { + return $this->controllerdir; + } + /** * Getter for library directory * @@ -240,8 +250,9 @@ class Module /** * Getter for module config object * - * @param null|string $file - * @return Config + * @param string $file + * + * @return Config */ public function getConfig($file = null) { @@ -375,10 +386,11 @@ class Module /** * Register hook * - * @param string $name - * @param string $class - * @param string $key - * @return self + * @param string $name + * @param string $class + * @param string $key + * + * @return self */ protected function registerHook($name, $key, $class) { diff --git a/library/Icinga/Web/ActionController.php b/library/Icinga/Web/Controller/ActionController.php similarity index 86% rename from library/Icinga/Web/ActionController.php rename to library/Icinga/Web/Controller/ActionController.php index 5e29ed207..8ade9e2e9 100755 --- a/library/Icinga/Web/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -25,19 +25,18 @@ * @author Icinga Development Team */ // {{{ICINGA_LICENSE_HEADER}}} +namespace Icinga\Web\Controller; -namespace Icinga\Web; - -use Icinga\Authentication\Manager as AuthManager; -use Icinga\Application\Benchmark; -use Icinga\Exception; +use \Icinga\Authentication\Manager as AuthManager; +use \Icinga\Application\Benchmark; +use \Icinga\Exception; use \Icinga\Application\Config; -use Icinga\Web\Notification; -use Zend_Layout as ZfLayout; -use Zend_Controller_Action as ZfController; -use Zend_Controller_Request_Abstract as ZfRequest; -use Zend_Controller_Response_Abstract as ZfResponse; -use Zend_Controller_Action_HelperBroker as ZfActionHelper; +use \Icinga\Web\Notification; +use \Zend_Layout as ZfLayout; +use \Zend_Controller_Action as ZfController; +use \Zend_Controller_Request_Abstract as ZfRequest; +use \Zend_Controller_Response_Abstract as ZfResponse; +use \Zend_Controller_Action_HelperBroker as ZfActionHelper; /* * @TODO(el): There was a note from tg that the following line is temporary. Ask him why. @@ -224,12 +223,14 @@ class ActionController extends ZfController // TODO(el): What is this, why do we need that here? $this->view->compact = $this->_request->getParam('view') === 'compact'; - Benchmark::measure(sprintf( - 'Action::preDispatched(): %s / %s / %s', - $this->module_name, - $this->controller_name, - $this->action_name - )); + Benchmark::measure( + sprintf( + 'Action::preDispatched(): %s / %s / %s', + $this->module_name, + $this->controller_name, + $this->action_name + ) + ); //$this->quickRedirect('/authentication/login?a=e'); } @@ -282,26 +283,10 @@ class ActionController extends ZfController require_once 'vendor/lessphp/lessc.inc.php'; $less = new \lessc; $cssdir = dirname(ICINGA_LIBDIR) . '/public/css'; - // TODO: We need a way to retrieve public dir, even if located elsewhere + // TODO: We need a way to retrieve public dir, even if located elsewhere $css = $less->compileFile($cssdir . '/pdfprint.less'); -/* - foreach (\Icinga\Application\Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) { - if ($module->hasCss()) { - $css .= $less->compile( - '.icinga-module.module-' - . $name - . " {\n" - . file_get_contents($module->getCssFilename()) - . "}\n\n" - ); - } - } - -*/ - // END of CSS test - - $this->render( + $this->render( null, $this->_helper->viewRenderer->getResponseSegment(), $this->_helper->viewRenderer->getNoController() @@ -309,18 +294,15 @@ class ActionController extends ZfController $html = (string) $this->getResponse(); if ($this->module_name !== null) { $html = '
' - . "\n" - . $html - . '
'; + . $this->module_name + . '">' + . "\n" + . $html + . ''; } $html = '' . $html; - //$html .= $this->view->action('services', 'list', 'monitoring', array('limit' => 10)); -// $html = preg_replace('~icinga-module.module-bpapp~', 'csstest', $html); -// echo $html; exit; $pdf = new Pdf(); $pdf->AddPage(); $pdf->setFontSubsetting(false); @@ -357,6 +339,5 @@ class ActionController extends ZfController Benchmark::measure('Response ready'); $this->_helper->layout()->benchmark = $this->renderBenchmark(); } - } } diff --git a/library/Icinga/Web/Controller/BaseConfigController.php b/library/Icinga/Web/Controller/BaseConfigController.php new file mode 100644 index 000000000..a3b8a7fe2 --- /dev/null +++ b/library/Icinga/Web/Controller/BaseConfigController.php @@ -0,0 +1,66 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Web\Controller; + +use \Icinga\Application\Icinga; + +/** + * Base class for Configuration Controllers + * + * Module preferences use this class to make sure they are automatically + * added to the application's configuration dialog. If you create a subclass of + * BasePreferenceController and overwrite @see init(), make sure you call + * parent::init(), otherwise you won't have the $tabs property in your view. + * + */ +class BaseConfigController extends ActionController +{ + /* + * Return an array of tabs provided by this configuration controller. + * + * Those tabs will automatically be added to the application's configuration dialog + * + * @return array + */ + public static function createProvidedTabs() + { + return array(); + } + + /** + * Initialize the controller and collect all tabs for it from the application and it's modules + * + * @see ActionController::init + */ + public function init() + { + parent::init(); + $this->view->tabs = ControllerTabCollector::collectControllerTabs('ConfigController'); + } +} diff --git a/library/Icinga/Web/Controller/BasePreferenceController.php b/library/Icinga/Web/Controller/BasePreferenceController.php new file mode 100644 index 000000000..94293655a --- /dev/null +++ b/library/Icinga/Web/Controller/BasePreferenceController.php @@ -0,0 +1,64 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Web\Controller; + +/** + * Base class for Preference Controllers + * + * Module preferences use this class to make sure they are automatically + * added to the general preferences dialog. If you create a subclass of + * BasePreferenceController and overwrite @see init(), make sure you call + * parent::init(), otherwise you won't have the $tabs property in your view. + * + */ +class BasePreferenceController extends ActionController +{ + /** + * Return an array of tabs provided by this preference controller. + * + * Those tabs will automatically be added to the application's preference dialog + * + * @return array + */ + public static function createProvidedTabs() + { + return array(); + } + + /** + * Initialize the controller and collect all tabs for it from the application and it's modules + * + * @see ActionController::init() + */ + public function init() + { + parent::init(); + $this->view->tabs = ControllerTabCollector::collectControllerTabs('PreferenceController'); + } +} diff --git a/library/Icinga/Web/Controller/ControllerTabCollector.php b/library/Icinga/Web/Controller/ControllerTabCollector.php new file mode 100644 index 000000000..d5d48fa4c --- /dev/null +++ b/library/Icinga/Web/Controller/ControllerTabCollector.php @@ -0,0 +1,125 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Web\Controller; + +use \Icinga\Application\Modules\Module; +use \Icinga\Application\Icinga; +use \Icinga\Web\Widget\Tabs; + +/** + * Static helper class that collects tabs provided by the 'createProvidedTabs' method + * of controllers. + * + */ +class ControllerTabCollector +{ + + /** + * Scan all controllers with the provided name + * in the application and (loaded) module folders and collects their provided tabs + * + * @param string $controller The name of the controllers to use for tab collection + * + * @return Tabs A @see Tabs instance containing the application tabs first + * followed by the tabs provided from the modules + */ + public static function collectControllerTabs($controller) + { + require_once(Icinga::app()->getApplicationDir('/controllers/'.$controller.'.php')); + + $applicationTabs = $controller::createProvidedTabs(); + $moduleTabs = self::collectModuleTabs($controller); + + $tabs = new Tabs(); + foreach ($applicationTabs as $name => $tab) { + $tabs->add($name, $tab); + } + + foreach ($moduleTabs as $name => $tab) { + // don't overwrite application tabs if the module wants to + if ($tabs->has($name)) { + continue; + } + $tabs->add($name, $tab); + } + return $tabs; + } + + /** + * Collect module tabs for all modules containing the given controller + * + * @param string $controller The controller name to use for tab collection + * + * @return array An array of Tabs objects or arrays containing Tab descriptions + */ + private static function collectModuleTabs($controller) + { + $moduleManager = Icinga::app()->getModuleManager(); + $modules = $moduleManager->listEnabledModules(); + $tabs = array(); + foreach ($modules as $module) { + $tabs += self::createModuleConfigurationTabs($controller, $moduleManager->getModule($module)); + } + + return $tabs; + } + + /** + * 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 + * an empty array will be returned + * + * @param string $controller The name of the controller that provides tabs via createProvidedTabs + * @param Module $module The module instance that provides the controller + * + * @return array + */ + private static function createModuleConfigurationTabs($controller, Module $module) + { + $controllerDir = $module->getControllerDir(); + $name = $module->getName(); + + $controllerDir = $controllerDir . '/' . $controller . '.php'; + $controllerName = ucfirst($name) . '_' . $controller; + + if (is_readable($controllerDir)) { + require_once(realpath($controllerDir)); + if (!method_exists($controllerName, "createProvidedTabs")) { + return array(); + } + $tab = $controllerName::createProvidedTabs(); + if (!is_array($tab)) { + $tab = array($name => $tab); + } + return $tab; + } + return array(); + } +} diff --git a/library/Icinga/Web/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php similarity index 98% rename from library/Icinga/Web/ModuleActionController.php rename to library/Icinga/Web/Controller/ModuleActionController.php index f338ad803..a2fdf6c68 100644 --- a/library/Icinga/Web/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -26,7 +26,7 @@ /** * Module action controller */ -namespace Icinga\Web; +namespace Icinga\Web\Controller; use \Icinga\Application\Config as IcingaConfig; use Icinga\Application\Icinga; diff --git a/modules/monitoring/application/controllers/CommandController.php b/modules/monitoring/application/controllers/CommandController.php index 651969235..0033a7730 100644 --- a/modules/monitoring/application/controllers/CommandController.php +++ b/modules/monitoring/application/controllers/CommandController.php @@ -1,6 +1,5 @@ - * @author Icinga Development Team + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team */ // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Application\Benchmark; -use Icinga\Authentication\Manager; -use Icinga\Web\ActionController; -use Icinga\Web\Widget\Tabs; -use Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; +use \Icinga\Web\Controller\BaseConfigController; +use \Icinga\Web\Widget\Tab; +use \Icinga\Web\Url; -/** - * Class ConfigurationController - */ -class ConfigurationController extends ActionController -{ - public function init() +class Monitoring_ConfigController extends BaseConfigController { + + static public function createProvidedTabs() { - parent::init(); - } - - - /** - * Index action - */ - public function indexAction() - { - $tabBuilder = new ConfigurationTabBuilder( - new Tabs() + return array( + "backends" => new Tab(array( + "name" => "backends", + "iconCls" => "hdd", + "title" => "Monitoring Backends", + "url" => Url::fromPath("/monitoring/config/backend") + )) ); - - $tabBuilder->build(); - $this->view->tabs = $tabBuilder->getTabs(); } -} + public function backendAction() + { + $this->redirectNow("/config"); + } + +} // @codingStandardsIgnoreEnd diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 9b3313e8f..9fcb3638f 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -1,6 +1,32 @@ + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} +use Icinga\Web\Controller\ModuleActionController; use Icinga\Web\Hook; use Icinga\File\Csv; use Monitoring\Backend; @@ -9,9 +35,26 @@ use Icinga\Web\Widget\Tabs; class Monitoring_ListController extends ModuleActionController { + /** + * The backend used for this controller + * + * @var \Icinga\Backend + */ protected $backend; + + /** + * Set to a string containing the compact layout name to use when + * 'compact' is set as the layout parameter, otherwise null + * + * @var string|null + */ private $compactView = null; + /** + * Retrieve backend and hooks for this controller + * + * @see ActionController::init + */ public function init() { $this->view->tabs = $this->getTabs() @@ -21,6 +64,9 @@ class Monitoring_ListController extends ModuleActionController $this->view->grapher = Hook::get('grapher'); } + /** + * Display host list + */ public function hostsAction() { Benchmark::measure("hostsAction::query()"); @@ -50,6 +96,9 @@ class Monitoring_ListController extends ModuleActionController ); } + /** + * Display service list + */ public function servicesAction() { $state_type = $this->_getParam('_statetype', 'soft'); @@ -93,6 +142,11 @@ class Monitoring_ListController extends ModuleActionController $this->inheritCurrentSortColumn(); } + /** + * Display hostgroup list + * + * @TODO Implement hostgroup overview (feature #4184) + */ public function hostgroupsAction() { $this->view->hostgroups = $this->backend->select() @@ -102,6 +156,11 @@ class Monitoring_ListController extends ModuleActionController ))->applyRequest($this->_request); } + /** + * Display servicegroup list + * + * @TODO Implement servicegroup overview (feature #4185) + */ public function servicegroupsAction() { $this->view->servicegroups = $this->backend->select() @@ -111,6 +170,11 @@ class Monitoring_ListController extends ModuleActionController ))->applyRequest($this->_request); } + /** + * Display contactgroups overview + * + * + */ public function contactgroupsAction() { $this->view->contactgroups = $this->backend->select() @@ -120,33 +184,6 @@ class Monitoring_ListController extends ModuleActionController ))->applyRequest($this->_request); } - public function contactsAction() - { - $this->view->contacts = $this->backend->select() - ->from('contact', array( - 'contact_name', - 'contact_alias', - 'contact_email', - 'contact_pager' - ))->applyRequest($this->_request); - } - - // TODO: Search helper playground - public function searchAction() - { - $data = array( - 'service_description', - 'service_state', - 'service_acknowledged', - 'service_handled', - 'service_output', - // '_host_satellite', - 'service_last_state_change' - ); - echo json_encode($data); - exit; - } - /** * Fetch the current downtimes and put them into the view * property 'downtimes' @@ -177,6 +214,14 @@ class Monitoring_ListController extends ModuleActionController $this->inheritCurrentSortColumn(); } + /** + * Create a query for the given view + * + * @param string $view An string identifying view to query + * @param array $columns An array with the column names to display + * + * @return \Icinga\Data\Db\Query + */ protected function query($view, $columns) { $extra = preg_split( @@ -194,6 +239,11 @@ class Monitoring_ListController extends ModuleActionController return $query; } + /** + * Handle the 'format' and 'view' parameter + * + * @param \Icinga\Data\Db\Query $query The current query + */ protected function handleFormatRequest($query) { if ($this->compactView !== null && ($this->_getParam('view', false) === 'compact')) { @@ -220,6 +270,11 @@ class Monitoring_ListController extends ModuleActionController } } + /** + * Return all tabs for this controller + * + * @return Tabs + */ protected function getTabs() { $tabs = new Tabs(); @@ -275,3 +330,4 @@ class Monitoring_ListController extends ModuleActionController } } } +// @codingStandardsIgnoreEnd diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index 975da9c6d..f5f95a23a 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -28,7 +28,7 @@ // {{{ICINGA_LICENSE_HEADER}}} use Monitoring\Backend; -use Icinga\Web\ModuleActionController; +use Icinga\Web\Controller\ModuleActionController; use Icinga\Web\Hook; use Monitoring\Object\Host; use Monitoring\Object\Service; diff --git a/modules/monitoring/application/controllers/SoapController.php b/modules/monitoring/application/controllers/SoapController.php index f849200ec..cec6372bc 100644 --- a/modules/monitoring/application/controllers/SoapController.php +++ b/modules/monitoring/application/controllers/SoapController.php @@ -1,10 +1,14 @@ $e->getMessage()); } - return $result; + return $result; } } @@ -47,3 +51,4 @@ class Monitoring_SoapController extends ModuleActionController exit; } } +// @codingStandardsIgnoreEnd diff --git a/modules/monitoring/application/controllers/SummaryController.php b/modules/monitoring/application/controllers/SummaryController.php index ead0c22ec..ba15eb2ca 100644 --- a/modules/monitoring/application/controllers/SummaryController.php +++ b/modules/monitoring/application/controllers/SummaryController.php @@ -1,6 +1,32 @@ + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} +use Icinga\Web\Controller\ModuleActionController; use Icinga\Backend; use Icinga\Web\Widget\Tabs; @@ -68,3 +94,4 @@ class Monitoring_SummaryController extends ModuleActionController } } +// @codingStandardsIgnoreEnd \ No newline at end of file diff --git a/modules/monitoring/test/php/testlib/MonitoringControllerTest.php b/modules/monitoring/test/php/testlib/MonitoringControllerTest.php index 0c8f0a1d2..6475e8e8c 100644 --- a/modules/monitoring/test/php/testlib/MonitoringControllerTest.php +++ b/modules/monitoring/test/php/testlib/MonitoringControllerTest.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Web +namespace Icinga\Web\Controller { /** * Mocked controller base class to avoid the complete @@ -82,7 +82,7 @@ namespace Test\Monitoring\Testlib require_once 'Zend/View.php'; use Icinga\Protocol\Statusdat\Reader; - use Icinga\Web\ActionController; + use Icinga\Web\Controller\ActionController; use Test\Monitoring\Testlib\DataSource\TestFixture; use Test\Monitoring\Testlib\DataSource\DataSourceTestSetup; use Monitoring\Backend\Ido; diff --git a/public/js/icinga/components/mainDetail.js b/public/js/icinga/components/mainDetail.js index 368c5c5f7..359f2d925 100755 --- a/public/js/icinga/components/mainDetail.js +++ b/public/js/icinga/components/mainDetail.js @@ -87,9 +87,9 @@ var onLinkTagClick = function(ev) { '.layout-main-detail * a' : { 'click' : onLinkTagClick }, - 'a' : { +/* 'a' : { 'click' : onOuterLinkClick - }, + },*/ '.layout-main-detail .icinga-container#icinga-detail' : { 'focus' : expandDetailView } diff --git a/test/php/application/controllers/IndexControllerTest.php b/test/php/application/controllers/IndexControllerTest.php index 2b3504928..68162a396 100644 --- a/test/php/application/controllers/IndexControllerTest.php +++ b/test/php/application/controllers/IndexControllerTest.php @@ -12,7 +12,7 @@ require '../../library/Icinga/Exception/ProgrammingError.php'; require '../../library/Icinga/Application/Benchmark.php'; require '../../library/Icinga/Application/Config.php'; require '../../library/Icinga/Application/Icinga.php'; -require '../../library/Icinga/Web/ActionController.php'; +require '../../library/Icinga/Web/Controller/ActionController.php'; require '../../library/Icinga/Web/Notification.php'; require '../../library/Icinga/Application/Platform.php'; diff --git a/test/php/library/Icinga/Web/ActionControllerTest.php b/test/php/library/Icinga/Web/ActionControllerTest.php index 642b8939c..9e7ed9e66 100755 --- a/test/php/library/Icinga/Web/ActionControllerTest.php +++ b/test/php/library/Icinga/Web/ActionControllerTest.php @@ -1,6 +1,6 @@