From 6ff5a986dd9818b6ecb81786abe01d29beb28ef3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 26 May 2014 13:08:47 +0200 Subject: [PATCH 001/184] Modules: Don't call `FrontController::getRoute()' twice refs #6303 --- library/Icinga/Application/Modules/Module.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index c765ca380..e9a8ab583 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -600,18 +600,19 @@ class Module */ protected function registerRoutes() { - $this->app->getFrontController()->getRouter()->addRoute( + $router = $this->app->getFrontController()->getRouter(); + $router->addRoute( $this->name . '_jsprovider', new Route( 'js/' . $this->name . '/:file', array( 'controller' => 'static', 'action' =>'javascript', - 'module_name' => $this->name + 'module_name' => $this->name ) ) ); - $this->app->getFrontController()->getRouter()->addRoute( + $router->addRoute( $this->name . '_img', new Route( 'img/' . $this->name . '/:file', From a91961284dd580e283cd1dc7bfa4b0b7e17228fc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 09:26:53 +0200 Subject: [PATCH 002/184] modules/doc: Show nothing on index yet Before, doc's IndexController::indexAction() showed the documentation of Icinga Web 2 but index should rather show an overview of available docs. refs #4820 --- .../controllers/IndexController.php | 26 +------------------ .../views/scripts/index/index.phtml | 5 ---- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/modules/doc/application/controllers/IndexController.php b/modules/doc/application/controllers/IndexController.php index 299ae5c65..1911f0534 100644 --- a/modules/doc/application/controllers/IndexController.php +++ b/modules/doc/application/controllers/IndexController.php @@ -1,37 +1,13 @@ parser = new DocParser($module); - } - - - public function tocAction() - { - // Temporary workaround - list($html, $toc) = $this->parser->getDocumentation(); - $this->view->toc = $toc; - } - - /** - * Display the application's documentation - */ public function indexAction() { - $this->populateView(); + } } -// @codingStandardsIgnoreEnd diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml index a178cc155..e69de29bb 100644 --- a/modules/doc/application/views/scripts/index/index.phtml +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -1,5 +0,0 @@ -

Icinga 2 Documentation

-partial('module/view.phtml', 'doc', array( - 'toc' => $toc, - 'html' => $html -)); ?> \ No newline at end of file From 83c12cfb2ef5c29158c885d5f493dddb0394b2a8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 09:35:02 +0200 Subject: [PATCH 003/184] modules/doc: Add partial for displaying doc and toc refs #4820 --- .../views/scripts/partials/docandtoc.phtml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 modules/doc/application/views/scripts/partials/docandtoc.phtml diff --git a/modules/doc/application/views/scripts/partials/docandtoc.phtml b/modules/doc/application/views/scripts/partials/docandtoc.phtml new file mode 100644 index 000000000..704e5ef88 --- /dev/null +++ b/modules/doc/application/views/scripts/partials/docandtoc.phtml @@ -0,0 +1,19 @@ + +

No documentation available.

+ +
+ partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) + ); + ?> +
+
+ +
+ From e6abe21fb8bb2ec444e9432a012abfbf3a9af457 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 09:36:14 +0200 Subject: [PATCH 004/184] modules/doc: Rename DocParser::getDocumentation() to getDocAndToc() refs #4820 --- modules/doc/library/Doc/Controller.php | 14 +++++++------- modules/doc/library/Doc/DocParser.php | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/doc/library/Doc/Controller.php b/modules/doc/library/Doc/Controller.php index 3f7b1da4e..840011150 100644 --- a/modules/doc/library/Doc/Controller.php +++ b/modules/doc/library/Doc/Controller.php @@ -9,15 +9,15 @@ use Icinga\Web\Controller\ActionController; class Controller extends ActionController { /** - * Set HTML and toc + * Publish doc HTML and toc to the view * - * @param string $module + * @param string $module Name of the module for which to populate doc and toc. `null` for Icinga Web 2's doc */ protected function populateView($module = null) { - $parser = new DocParser($module); - list($html, $toc) = $parser->getDocumentation(); - $this->view->html = $html; - $this->view->toc = $toc; + $parser = new DocParser($module); + list($docHtml, $docToc) = $parser->getDocAndToc(); + $this->view->docHtml = $docHtml; + $this->view->docToc = $docToc; } -} \ No newline at end of file +} diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 945a96eea..ac3c58ea6 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -4,6 +4,8 @@ namespace Icinga\Module\Doc; +require_once 'vendor/Parsedown/Parsedown.php'; + use RecursiveIteratorIterator; use RecursiveDirectoryIterator; use Parsedown; @@ -11,8 +13,6 @@ use Icinga\Application\Icinga; use Icinga\Web\Menu; use Icinga\Web\Url; -require_once 'vendor/Parsedown/Parsedown.php'; - /** * Parser for documentation written in Markdown */ @@ -51,12 +51,12 @@ class DocParser } /** - * Retrieve table of contents and HTML converted from markdown files sorted by filename + * Retrieve doc as HTML converted from markdown files sorted by filename and the table of contents * * @return array * @throws DocException */ - public function getDocumentation() + public function getDocAndToc() { $iter = new RecursiveIteratorIterator( new MarkdownFileIterator( From ce37c20736b10309353a284d62c5bfc09206213b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 09:40:00 +0200 Subject: [PATCH 005/184] modules/doc: Add route 'modules/doc/icingaweb' to display Icinga Web 2's documentation refs #4820 --- .../application/controllers/IcingawebController.php | 13 +++++++++++++ .../application/views/scripts/icingaweb/index.phtml | 9 +++++++++ 2 files changed, 22 insertions(+) create mode 100644 modules/doc/application/controllers/IcingawebController.php create mode 100644 modules/doc/application/views/scripts/icingaweb/index.phtml diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php new file mode 100644 index 000000000..209e751f4 --- /dev/null +++ b/modules/doc/application/controllers/IcingawebController.php @@ -0,0 +1,13 @@ +populateView(); + } +} diff --git a/modules/doc/application/views/scripts/icingaweb/index.phtml b/modules/doc/application/views/scripts/icingaweb/index.phtml new file mode 100644 index 000000000..ae6573b2b --- /dev/null +++ b/modules/doc/application/views/scripts/icingaweb/index.phtml @@ -0,0 +1,9 @@ +

Icinga Web 2 Documentation

+partial( + 'partials/docandtoc.phtml', + 'doc', + array( + 'docToc' => $docToc, + 'docHtml' => $docHtml + ) +); ?> From 99971e241c22f5a6c5d3a9665c5f310a2ad65422 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:03:18 +0200 Subject: [PATCH 006/184] modules/doc: Replace Parsedown's decprecated method parse() with text() refs #4820 --- modules/doc/library/Doc/DocParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index ac3c58ea6..f098d6907 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -125,7 +125,7 @@ class DocParser } $fileObject->flock(LOCK_UN); } - $html = Parsedown::instance()->parse(implode('', $cat)); + $html = Parsedown::instance()->text(implode('', $cat)); $html = preg_replace_callback( '#
(.*?)\
#s', array($this, 'highlight'), From 74ea4d19b829738f66f6778536532257b44069c8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:04:36 +0200 Subject: [PATCH 007/184] modules/doc: Rename populateView() to renderDocAndToc() Rename populateView() to renderDocAndToc() since the method now sets the viewHelper to docandtoc.phtml refs #4820 --- .../views/scripts/partials/docandtoc.phtml | 33 ++++++++++--------- modules/doc/library/Doc/Controller.php | 4 ++- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/modules/doc/application/views/scripts/partials/docandtoc.phtml b/modules/doc/application/views/scripts/partials/docandtoc.phtml index 704e5ef88..46f7b0bfe 100644 --- a/modules/doc/application/views/scripts/partials/docandtoc.phtml +++ b/modules/doc/application/views/scripts/partials/docandtoc.phtml @@ -1,19 +1,20 @@ +

documentation

-

No documentation available.

+

Documentation not available.

-
- partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) - ); - ?> -
-
- -
+
+partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) + ); +?> +
+
+ +
diff --git a/modules/doc/library/Doc/Controller.php b/modules/doc/library/Doc/Controller.php index 840011150..ae5bc177f 100644 --- a/modules/doc/library/Doc/Controller.php +++ b/modules/doc/library/Doc/Controller.php @@ -13,11 +13,13 @@ class Controller extends ActionController * * @param string $module Name of the module for which to populate doc and toc. `null` for Icinga Web 2's doc */ - protected function populateView($module = null) + protected function renderDocAndToc($module = null) { $parser = new DocParser($module); list($docHtml, $docToc) = $parser->getDocAndToc(); $this->view->docHtml = $docHtml; $this->view->docToc = $docToc; + $this->view->docName = $module === null ? 'Icinga Web 2' : ucfirst($module); + $this->_helper->viewRenderer('partials/docandtoc', null, true); } } From 40c2c64985be040976c27ca169ec0ca9174db63d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:06:28 +0200 Subject: [PATCH 008/184] modules/doc: Implement doc/module/$moduleName URLs refs #4820 --- .../controllers/ModuleController.php | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 5e3636f92..5c7851542 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -1,5 +1,4 @@ view->enabledModules = Icinga::app()->getModuleManager()->listEnabledModules(); } - /** - * Display a module's documentation - */ - public function viewAction() - { - $this->populateView($this->getParam('name')); - } - /** * Provide run-time dispatching of module documentation * - * @param string $methodName - * @param array $args + * @param string $methodName + * @param array $args * * @return mixed */ public function __call($methodName, $args) { - // TODO(el): Setup routing to retrieve module name as param and point route to moduleAction - $moduleManager = Icinga::app()->getModuleManager(); - $moduleName = substr($methodName, 0, -6); // Strip 'Action' suffix - if (!$moduleManager->hasEnabled($moduleName)) { - // TODO(el): Throw a not found exception once the code has been moved to the moduleAction (see TODO above) + $moduleManager = Icinga::app()->getModuleManager(); + $moduleName = substr($methodName, 0, -6); // Strip 'Action' suffix + if (! $moduleManager->hasEnabled($moduleName)) { + // TODO(el): Distinguish between not enabled and not installed return parent::__call($methodName, $args); } - $this->_helper->redirector->gotoSimpleAndExit('view', null, null, array('name' => $moduleName)); + $this->renderDocAndToc($moduleName); } } -// @codingStandardsIgnoreEnd From ba860edb75f76b42bb16e5975783b20d17d8e86c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:07:09 +0200 Subject: [PATCH 009/184] modules/doc: Remove icingaweb/index.phtml since it's replaced by renderDocAndToc() refs #4820 --- .../doc/application/controllers/IcingawebController.php | 2 +- .../doc/application/views/scripts/icingaweb/index.phtml | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 modules/doc/application/views/scripts/icingaweb/index.phtml diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 209e751f4..90b1e23f4 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -8,6 +8,6 @@ class Doc_IcingawebController extends DocController { public function indexAction() { - $this->populateView(); + $this->renderDocAndToc(); } } diff --git a/modules/doc/application/views/scripts/icingaweb/index.phtml b/modules/doc/application/views/scripts/icingaweb/index.phtml deleted file mode 100644 index ae6573b2b..000000000 --- a/modules/doc/application/views/scripts/icingaweb/index.phtml +++ /dev/null @@ -1,9 +0,0 @@ -

Icinga Web 2 Documentation

-partial( - 'partials/docandtoc.phtml', - 'doc', - array( - 'docToc' => $docToc, - 'docHtml' => $docHtml - ) -); ?> From 4001f1de62fb6bf0dc8d1e5df03c83ea350dedac Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:09:50 +0200 Subject: [PATCH 010/184] modules/doc: Remove module/view.phtml as it has been replaced by renderDocAndToc() refs #4820 --- modules/doc/application/views/scripts/module/view.phtml | 7 ------- modules/doc/library/Doc/DocException.php | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 modules/doc/application/views/scripts/module/view.phtml diff --git a/modules/doc/application/views/scripts/module/view.phtml b/modules/doc/application/views/scripts/module/view.phtml deleted file mode 100644 index 291947ad7..000000000 --- a/modules/doc/application/views/scripts/module/view.phtml +++ /dev/null @@ -1,7 +0,0 @@ - -

No documentation available.

- -
- -
- diff --git a/modules/doc/library/Doc/DocException.php b/modules/doc/library/Doc/DocException.php index cb7134045..795c9a5f9 100644 --- a/modules/doc/library/Doc/DocException.php +++ b/modules/doc/library/Doc/DocException.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Doc; -use \Exception; +use Exception; class DocException extends Exception { From a40f34394b7e17914937640f43653cf467633f5b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:11:03 +0200 Subject: [PATCH 011/184] modules/doc: List available documentations on index refs #4820 --- modules/doc/application/views/scripts/index/index.phtml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml index e69de29bb..9edb5910d 100644 --- a/modules/doc/application/views/scripts/index/index.phtml +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -0,0 +1,5 @@ +

Available documentations

+ From 5627c09b3771857bb3fe97cf4a8e07fec58de79b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:11:28 +0200 Subject: [PATCH 012/184] modules/doc: Reduce tab size to two spaces in module/index.phtml refs #4820 --- modules/doc/application/views/scripts/module/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml index 36f11e15e..5e9eaae9d 100644 --- a/modules/doc/application/views/scripts/module/index.phtml +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -1,6 +1,6 @@

Module documentations

    -
  • +
From 89bddb10e7cfe90fed4426228e9837d058cf8d16 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:16:58 +0200 Subject: [PATCH 013/184] modules/doc: Tell which doc directory does not exist, in case it does not exist :) Thanks Michael refs #4820 --- modules/doc/library/Doc/DocParser.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index f098d6907..39f8c8718 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -35,16 +35,16 @@ class DocParser $dir = Icinga::app()->getApplicationDir('/../doc'); } else { $mm = Icinga::app()->getModuleManager(); - if (!$mm->hasInstalled($module)) { + if (! $mm->hasInstalled($module)) { throw new DocException('Module is not installed'); } - if (!$mm->hasEnabled($module)) { + if (! $mm->hasEnabled($module)) { throw new DocException('Module is not enabled'); } $dir = $mm->getModuleDir($module, '/doc'); } - if (!is_dir($dir)) { - throw new DocException('Doc directory does not exist'); + if (! is_dir($dir)) { + throw new DocException('Doc directory `' . $dir .'\' does not exist'); } $this->dir = $dir; $this->module = $module; @@ -81,7 +81,7 @@ class DocParser throw new DocException('Couldn\'t get the lock'); } $line = null; - while (!$fileObject->eof()) { + while (! $fileObject->eof()) { // Save last line for setext-style headers $lastLine = $line; $line = $fileObject->fgets(); @@ -156,7 +156,7 @@ class DocParser */ protected function extractHeader($line, $lastLine) { - if (!$line) { + if (! $line) { return null; } $header = null; @@ -167,7 +167,7 @@ class DocParser // Atx-style $level = strlen($match[0]); $header = trim(substr($line, $level)); - if (!$header) { + if (! $header) { return null; } } elseif ( @@ -177,7 +177,7 @@ class DocParser ) { // Setext $header = trim($lastLine); - if (!$header) { + if (! $header) { return null; } if ($match[0][0] === '=') { From 1d5c4c50554f24ac81ada45c20d1e61de6089730 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:17:37 +0200 Subject: [PATCH 014/184] modules/doc: Add styles refs #4820 --- modules/doc/public/css/module.less | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/doc/public/css/module.less diff --git a/modules/doc/public/css/module.less b/modules/doc/public/css/module.less new file mode 100644 index 000000000..45d97e5c6 --- /dev/null +++ b/modules/doc/public/css/module.less @@ -0,0 +1,9 @@ +.toc { + float: left; + width: 33.333% +} + +.doc { + float: left; + width: 66.667% +} From 5923622fb8b80089fbc00189da4be84ebf8b4db4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 23 May 2014 14:22:35 +0200 Subject: [PATCH 015/184] modules/doc: Fix toc links The DocParser generates toc links with a hard-coded URL. This should be changed to be generated from a route. But we did not decide how to handle route overrides yet. refs #4820 --- modules/doc/library/Doc/DocParser.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 39f8c8718..04086469b 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -104,14 +104,13 @@ class DocParser $item = end($toc)->item->addChild( $id, array( + // TODO(el): URL should be generated from a route else we always have to adapt the + // URL here when we change URLs 'url' => Url::fromPath( - 'doc/module/view', - array( - 'name' => $this->module - ) + $this->module === null ? 'doc/icingaweb' : 'doc/module/' . $this->module )->setAnchor($id)->getRelativeUrl(), 'title' => htmlspecialchars($header), - 'priority' => $itemPriority++, + 'priority' => $itemPriority++, // Post-increment is on purpose 'attribs' => $attribs ) ); From b58ec5f445afd020c57a90c07634eb18bf9a2ec2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 26 May 2014 13:33:32 +0200 Subject: [PATCH 016/184] Add Module::addRoute() to add a route to the route chain refs #6303 --- library/Icinga/Application/Modules/Module.php | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index e9a8ab583..9d3fb3dee 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -30,6 +30,7 @@ namespace Icinga\Application\Modules; use Exception; +use Zend_Controller_Router_Route_Abstract; use Zend_Controller_Router_Route as Route; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Config; @@ -150,6 +151,16 @@ class Module */ private $app; + + /** + * Routes to add to the route chain + * + * @var array Array of name-route pairs + * + * @see addRoute() + */ + protected $routes = array(); + /** * Create a new module object * @@ -594,13 +605,17 @@ class Module } /** - * Register routes for web access + * Add routes for static content and any route added via addRoute() to the route chain * - * @return self + * @return self + * @see addRoute() */ protected function registerRoutes() { $router = $this->app->getFrontController()->getRouter(); + foreach ($this->routes as $name => $route) { + $router->addRoute($name, $route); + } $router->addRoute( $this->name . '_jsprovider', new Route( @@ -687,4 +702,19 @@ class Module return $this; } + + /** + * Add a route which will be added to the route chain + * + * @param string $name Name of the route + * @param Zend_Controller_Router_Route_Abstract $route Instance of the route + * + * @return self + * @see registerRoutes() + */ + protected function addRoute($name, Zend_Controller_Router_Route_Abstract $route) + { + $this->routes[$name] = $route; + return $this; + } } From 6ce739e23d4e1c01249e803cf80357bf44cfc037 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 14:31:17 +0200 Subject: [PATCH 017/184] Doc: Construct parser with the path to the documentation Before, the parser decided which path to used based on a given module name. Now, the parser requires the path to the documentation. Further the toc items no longer include a URL. The must URL must be generated from a render function or view script. refs #4820 --- modules/doc/library/Doc/DocParser.php | 54 +++++++++++---------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 04086469b..fc87faf27 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -9,7 +9,7 @@ require_once 'vendor/Parsedown/Parsedown.php'; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; use Parsedown; -use Icinga\Application\Icinga; +use Icinga\Exception\NotReadableError; use Icinga\Web\Menu; use Icinga\Web\Url; @@ -18,36 +18,30 @@ use Icinga\Web\Url; */ class DocParser { - protected $dir; - - protected $module; + /** + * Path to the documentation + * + * @var string + */ + protected $path; /** - * Create a new documentation parser for the given module or the application + * Create a new documentation parser for the given path * - * @param string $module + * @param string $path Path to the documentation * * @throws DocException + * @throws NotReadableError */ - public function __construct($module = null) + public function __construct($path) { - if ($module === null) { - $dir = Icinga::app()->getApplicationDir('/../doc'); - } else { - $mm = Icinga::app()->getModuleManager(); - if (! $mm->hasInstalled($module)) { - throw new DocException('Module is not installed'); - } - if (! $mm->hasEnabled($module)) { - throw new DocException('Module is not enabled'); - } - $dir = $mm->getModuleDir($module, '/doc'); + if (! is_dir($path)) { + throw new DocException('Doc directory `' . $path .'\' does not exist'); } - if (! is_dir($dir)) { - throw new DocException('Doc directory `' . $dir .'\' does not exist'); + if (! is_readable($path)) { + throw new NotReadableError('Doc directory `' . $path .'\' is not readable'); } - $this->dir = $dir; - $this->module = $module; + $this->path = $path; } /** @@ -60,7 +54,7 @@ class DocParser { $iter = new RecursiveIteratorIterator( new MarkdownFileIterator( - new RecursiveDirectoryIterator($this->dir) + new RecursiveDirectoryIterator($this->path) ) ); $fileInfos = iterator_to_array($iter); @@ -89,7 +83,7 @@ class DocParser if ($header !== null) { list($header, $level) = $header; $id = $this->extractHeaderId($header); - $attribs = array(); + $nofollow = false; $this->reduceToc($toc, $level); if ($id === null) { $path = array(); @@ -98,20 +92,16 @@ class DocParser } $path[] = $header; $id = implode('-', $path); - $attribs['rel'] = 'nofollow'; + $nofollow = true; } $id = urlencode(str_replace('.', '.', strip_tags($id))); $item = end($toc)->item->addChild( $id, array( - // TODO(el): URL should be generated from a route else we always have to adapt the - // URL here when we change URLs - 'url' => Url::fromPath( - $this->module === null ? 'doc/icingaweb' : 'doc/module/' . $this->module - )->setAnchor($id)->getRelativeUrl(), - 'title' => htmlspecialchars($header), + 'id' => $id, + 'title' => $header, 'priority' => $itemPriority++, // Post-increment is on purpose - 'attribs' => $attribs + 'nofollow' => $nofollow ) ); $toc[] = ((object) array( From 71615151a4a20ba8098d857fe3ed5ebaf9fcd112 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 14:48:35 +0200 Subject: [PATCH 018/184] Doc: Rename `Controller' to `DocController' refs #4820 --- modules/doc/library/Doc/{Controller.php => DocController.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename modules/doc/library/Doc/{Controller.php => DocController.php} (94%) diff --git a/modules/doc/library/Doc/Controller.php b/modules/doc/library/Doc/DocController.php similarity index 94% rename from modules/doc/library/Doc/Controller.php rename to modules/doc/library/Doc/DocController.php index ae5bc177f..7a6b4f854 100644 --- a/modules/doc/library/Doc/Controller.php +++ b/modules/doc/library/Doc/DocController.php @@ -6,7 +6,7 @@ namespace Icinga\Module\Doc; use Icinga\Web\Controller\ActionController; -class Controller extends ActionController +class DocController extends ActionController { /** * Publish doc HTML and toc to the view From 884e4c8e946c40c5a3b0570ad64ab85e8101b790 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 14:49:32 +0200 Subject: [PATCH 019/184] Doc: Add `DocController::renderToc()' method refs #4820 --- modules/doc/library/Doc/DocController.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 7a6b4f854..967f7f070 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -22,4 +22,19 @@ class DocController extends ActionController $this->view->docName = $module === null ? 'Icinga Web 2' : ucfirst($module); $this->_helper->viewRenderer('partials/docandtoc', null, true); } + + /** + * Render a toc + * + * @param string $path Path to the documentation + * @param string + */ + protected function renderToc($path, $name) + { + $parser = new DocParser($path); + list($docHtml, $docToc) = $parser->getDocAndToc(); + $this->view->docToc = $docToc; + $this->view->docName = $name; + $this->_helper->viewRenderer('partials/toc', null, true); + } } From dd6427019a637f45cffc3dcf8e568e0c447d61e9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 14:50:48 +0200 Subject: [PATCH 020/184] Doc: Add action to render the toc of Icinga Web 2's documentation refs #4820 --- .../application/controllers/IcingawebController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 90b1e23f4..f9e05cefd 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -2,12 +2,16 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Module\Doc\Controller as DocController; +use Icinga\Application\Icinga; +use Icinga\Module\Doc\DocController; class Doc_IcingawebController extends DocController { - public function indexAction() + /** + * View toc of Icinga Web 2's documentation + */ + public function tocAction() { - $this->renderDocAndToc(); + $this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2'); } } From 0f5e0767713f2c9db96d2ad1e13d34c625bcad54 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 14:53:25 +0200 Subject: [PATCH 021/184] Doc: Add action to view the toc of a module's documentation refs #4820 --- .../controllers/ModuleController.php | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 5c7851542..cdeca3e50 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -2,8 +2,9 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} +use \Zend_Controller_Action_Exception; use Icinga\Application\Icinga; -use Icinga\Module\Doc\Controller as DocController; +use Icinga\Module\Doc\DocController; class Doc_ModuleController extends DocController { @@ -16,21 +17,34 @@ class Doc_ModuleController extends DocController } /** - * Provide run-time dispatching of module documentation + * Assert that the given module is enabled * - * @param string $methodName - * @param array $args + * @param $moduleName * - * @return mixed + * @throws Zend_Controller_Action_Exception */ - public function __call($methodName, $args) + protected function assertModuleEnabled($moduleName) { - $moduleManager = Icinga::app()->getModuleManager(); - $moduleName = substr($methodName, 0, -6); // Strip 'Action' suffix - if (! $moduleManager->hasEnabled($moduleName)) { - // TODO(el): Distinguish between not enabled and not installed - return parent::__call($methodName, $args); + if ($moduleName === null) { + throw new Zend_Controller_Action_Exception('Missing parameter "moduleName"', 404); } - $this->renderDocAndToc($moduleName); + $moduleManager = Icinga::app()->getModuleManager(); + if (! $moduleManager->hasInstalled($moduleName)) { + throw new Zend_Controller_Action_Exception('Module ' . $moduleName . ' is not installed', 404); + } + if (! $moduleManager->hasEnabled($moduleName)) { + throw new Zend_Controller_Action_Exception('Module ' . $moduleName. ' is not enabled', 404); + } + } + + /** + * View toc of a module's documentation + */ + public function tocAction() + { + $moduleName = $this->getParam('moduleName'); + $this->assertModuleEnabled($moduleName); + $moduleManager = Icinga::app()->getModuleManager(); + $this->renderToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); } } From e320d8613c87e41617c4b7e183550f4ad16f5239 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:02:09 +0200 Subject: [PATCH 022/184] Doc: Rename `docandtoc' view script to `chapter' Prepare that every chapter is displayed on a new page. refs #4820 --- .../views/scripts/partials/chapter.phtml | 1 + .../views/scripts/partials/docandtoc.phtml | 20 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) create mode 100644 modules/doc/application/views/scripts/partials/chapter.phtml delete mode 100644 modules/doc/application/views/scripts/partials/docandtoc.phtml diff --git a/modules/doc/application/views/scripts/partials/chapter.phtml b/modules/doc/application/views/scripts/partials/chapter.phtml new file mode 100644 index 000000000..b0e1fe0cb --- /dev/null +++ b/modules/doc/application/views/scripts/partials/chapter.phtml @@ -0,0 +1 @@ + diff --git a/modules/doc/application/views/scripts/partials/docandtoc.phtml b/modules/doc/application/views/scripts/partials/docandtoc.phtml deleted file mode 100644 index 46f7b0bfe..000000000 --- a/modules/doc/application/views/scripts/partials/docandtoc.phtml +++ /dev/null @@ -1,20 +0,0 @@ -

documentation

- -

Documentation not available.

- -
-partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) - ); -?> -
-
- -
- From 282af5a79467ea4cc49b9c6da0424877ccc0b304 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:03:02 +0200 Subject: [PATCH 023/184] Doc: Add `toc' view script refs #4820 --- .../application/views/scripts/partials/toc.phtml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 modules/doc/application/views/scripts/partials/toc.phtml diff --git a/modules/doc/application/views/scripts/partials/toc.phtml b/modules/doc/application/views/scripts/partials/toc.phtml new file mode 100644 index 000000000..c32285516 --- /dev/null +++ b/modules/doc/application/views/scripts/partials/toc.phtml @@ -0,0 +1,14 @@ +
+

documentation

+
+
+partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) +) ?> +
From dad7dc9e6ca0c9d2028a8b9d799a8ff8cf5fa0e4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:06:48 +0200 Subject: [PATCH 024/184] Doc: Rename `DocController::renderDocAndToc()' to `renderChapter()' Prepare that every chapter is displayed on a new page. refs #4820 --- modules/doc/library/Doc/DocController.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 967f7f070..0929eac4e 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -9,25 +9,24 @@ use Icinga\Web\Controller\ActionController; class DocController extends ActionController { /** - * Publish doc HTML and toc to the view + * Render a chapter * - * @param string $module Name of the module for which to populate doc and toc. `null` for Icinga Web 2's doc + * @param string $chapterName Name of the chapter + * @param string $path Path to the documentation */ - protected function renderDocAndToc($module = null) + protected function renderChapter($chapterName, $path) { - $parser = new DocParser($module); + $parser = new DocParser($path); list($docHtml, $docToc) = $parser->getDocAndToc(); - $this->view->docHtml = $docHtml; - $this->view->docToc = $docToc; - $this->view->docName = $module === null ? 'Icinga Web 2' : ucfirst($module); - $this->_helper->viewRenderer('partials/docandtoc', null, true); + $this->view->chapterHtml = $docHtml; + $this->_helper->viewRenderer('partials/chapter', null, true); } /** * Render a toc * * @param string $path Path to the documentation - * @param string + * @param string $name Name of the documentation */ protected function renderToc($path, $name) { From e78d98a60786b14e2cb7fbb498587013f1046704 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:07:49 +0200 Subject: [PATCH 025/184] Doc: Add action to display a chapter of Icinga Web 2's documentation Note that the FULL documentation is displayed yet. refs #4820 --- .../controllers/IcingawebController.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index f9e05cefd..5ddc4d323 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -2,6 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} +use \Zend_Controller_Action_Exception; use Icinga\Application\Icinga; use Icinga\Module\Doc\DocController; @@ -14,4 +15,18 @@ class Doc_IcingawebController extends DocController { $this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2'); } + + /** + * View a chapter of Icinga Web 2's documentation + * + * @throws Zend_Controller_Action_Exception + */ + public function chapterAction() + { + $chapterName = $this->getParam('chapterName'); + if ($chapterName === null) { + throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); + } + $this->renderChapter($chapterName, Icinga::app()->getApplicationDir('/../doc')); + } } From 178402b65c80e5029be43ddad74636d83c1f698f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:09:01 +0200 Subject: [PATCH 026/184] Doc: Add action to view a chapter of a module's documentation Note that the FULL documentation is displayed yet. refs #4820 --- .../controllers/ModuleController.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index cdeca3e50..fd74050ec 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -47,4 +47,21 @@ class Doc_ModuleController extends DocController $moduleManager = Icinga::app()->getModuleManager(); $this->renderToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); } + + /** + * View a chapter of a module's documentation + * + * @throws Zend_Controller_Action_Exception + */ + public function chapterAction() + { + $moduleName = $this->getParam('moduleName'); + $this->assertModuleEnabled($moduleName); + $chapterName = $this->getParam('chapterName'); + if ($chapterName === null) { + throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); + } + $moduleManager = Icinga::app()->getModuleManager(); + $this->renderChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + } } From 6159c0545794b39d503ca2dfa8fda78b81189c81 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 27 May 2014 15:09:43 +0200 Subject: [PATCH 027/184] Doc: Fix IndexController not using `DocController' refs #4820 --- modules/doc/application/controllers/IndexController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/application/controllers/IndexController.php b/modules/doc/application/controllers/IndexController.php index 1911f0534..aa44c9c5d 100644 --- a/modules/doc/application/controllers/IndexController.php +++ b/modules/doc/application/controllers/IndexController.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Icinga\Module\Doc\Controller as DocController; +use Icinga\Module\Doc\DocController; class Doc_IndexController extends DocController { From 93ee8e0a3d6bfb4b6e98e58f1d5eec14898d5cf6 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 13:28:06 +0200 Subject: [PATCH 028/184] Doc: Remove index/toc view script I see no reference to this file. refs #4820 --- .../doc/application/views/scripts/index/toc.phtml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 modules/doc/application/views/scripts/index/toc.phtml diff --git a/modules/doc/application/views/scripts/index/toc.phtml b/modules/doc/application/views/scripts/index/toc.phtml deleted file mode 100644 index 9188e21ff..000000000 --- a/modules/doc/application/views/scripts/index/toc.phtml +++ /dev/null @@ -1,14 +0,0 @@ -
-

Module documentations

-
-
-partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $toc->getChildren(), - 'sub' => false, - 'url' => '' - ) -) ?> -
From c12c4a9e4ca64543d981b3aa1486c12fd3ffedcf Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 13:29:02 +0200 Subject: [PATCH 029/184] Modules: Call `Module::registerWebIntegration()' after including the run script Routes added via a module's run script were not respected since `Module::registerRoutes()' is called from `Module::registerWebIntegration()'. refs #6303 --- library/Icinga/Application/Modules/Module.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 9d3fb3dee..6959c6903 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -192,8 +192,7 @@ class Module */ public function register() { - $this->registerAutoloader() - ->registerWebIntegration(); + $this->registerAutoloader(); try { $this->launchRunScript(); } catch (Exception $e) { @@ -205,6 +204,7 @@ class Module ); return false; } + $this->registerWebIntegration(); return true; } From 0f4d2ad80c6a3b89f06232837a11bae6324b63f0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 13:33:07 +0200 Subject: [PATCH 030/184] Doc: Support `doc/module/:moduleName/chapter/:chapterName' URLs refs #4820 --- modules/doc/run.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 modules/doc/run.php diff --git a/modules/doc/run.php b/modules/doc/run.php new file mode 100644 index 000000000..4db96cbc3 --- /dev/null +++ b/modules/doc/run.php @@ -0,0 +1,19 @@ +isCli()) { + return; +} + +$docModuleChapter = new Zend_Controller_Router_Route( + 'doc/module/:moduleName/chapter/:chapterName', + array( + 'controller' => 'module', + 'action' => 'chapter', + 'module' => 'doc' + ) +); + +$this->addRoute('doc/module/chapter', $docModuleChapter); From b11c0c36f9c82bb7967ab896eb4eb68624d8ce22 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 13:34:39 +0200 Subject: [PATCH 031/184] Doc: Support `doc/icingaweb/chapter/:chapterName' URLs refs #4820 --- modules/doc/run.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/doc/run.php b/modules/doc/run.php index 4db96cbc3..9d93ee865 100644 --- a/modules/doc/run.php +++ b/modules/doc/run.php @@ -16,4 +16,15 @@ $docModuleChapter = new Zend_Controller_Router_Route( ) ); +$docIcingaWebChapter = new Zend_Controller_Router_Route( + 'doc/icingaweb/chapter/:chapterName', + array( + 'controller' => 'icingaweb', + 'action' => 'chapter', + 'module' => 'doc' + ) +); + $this->addRoute('doc/module/chapter', $docModuleChapter); +$this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter); + From 040473f9860065b30a120f417662af9e8a2fdd1d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:13:42 +0200 Subject: [PATCH 032/184] lib: Add NodeInterface --- library/Icinga/Data/Tree/NodeInterface.php | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 library/Icinga/Data/Tree/NodeInterface.php diff --git a/library/Icinga/Data/Tree/NodeInterface.php b/library/Icinga/Data/Tree/NodeInterface.php new file mode 100644 index 000000000..8ecca86c6 --- /dev/null +++ b/library/Icinga/Data/Tree/NodeInterface.php @@ -0,0 +1,38 @@ + Date: Wed, 28 May 2014 17:14:33 +0200 Subject: [PATCH 033/184] lib: Add TreeIterator --- library/Icinga/Data/Tree/TreeIterator.php | 90 +++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 library/Icinga/Data/Tree/TreeIterator.php diff --git a/library/Icinga/Data/Tree/TreeIterator.php b/library/Icinga/Data/Tree/TreeIterator.php new file mode 100644 index 000000000..6722f3b35 --- /dev/null +++ b/library/Icinga/Data/Tree/TreeIterator.php @@ -0,0 +1,90 @@ +nodes = $node->getChildren(); +// } +// +// public function hasChildren() +// { +// return $this->current()->hasChildren(); +// } +// +// public function getChildren() +// { +// return new self($this->current()); +// } +// +// public function current() +// { +// return $this->nodes[$this->position]; +// } +// +// public function next() +// { +// ++$this->position; +// } +// +// public function valid() +// { +// return isset($this->nodes[$this->position]); +// } +// +// public function rewind() +// { +// $this->position = 0; +// } +// +// public function key() +// { +// return $this->position; +// } +//} + +use ArrayIterator; +use RecursiveIterator; + +class TreeIterator extends ArrayIterator implements RecursiveIterator +{ + /** + * Create a new TreeIterator + * + * @param NodeInterface $node + */ + public function __construct(NodeInterface $node) + { + parent::__construct($node->getChildren()); + } + + /** + * Whether an iterator can be created for the current node + * + * @return bool + */ + public function hasChildren() + { + return $this->current()->hasChildren(); + } + + /** + * Return an iterator for the current node + * + * @return self + */ + public function getChildren() + { + return new self($this->current()); + } +} From d2936d033808aaaf90dab0a8fe7456604e50ebb4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:15:08 +0200 Subject: [PATCH 034/184] doc/lib: Add DocToc class representing a toc refs #4820 --- modules/doc/library/Doc/DocToc.php | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 modules/doc/library/Doc/DocToc.php diff --git a/modules/doc/library/Doc/DocToc.php b/modules/doc/library/Doc/DocToc.php new file mode 100644 index 000000000..db7d9cb03 --- /dev/null +++ b/modules/doc/library/Doc/DocToc.php @@ -0,0 +1,48 @@ +value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function appendChild($value) + { + $child = new self($value); + $this->children[] = $child; + return $child; + } + + public function hasChildren() + { + return ! empty($this->children); + } + + public function getChildren() + { + return $this->children; + } + + public function getIterator() + { + return new TreeIterator($this); + } +} From f0b6a3557e2fe622bd83c4e53fe6fd109028171e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:15:43 +0200 Subject: [PATCH 035/184] doc/lib: Add DocTocHtmlRenderer class to render a toc to HTML refs #4820 --- .../doc/library/Doc/DocTocHtmlRenderer.php | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 modules/doc/library/Doc/DocTocHtmlRenderer.php diff --git a/modules/doc/library/Doc/DocTocHtmlRenderer.php b/modules/doc/library/Doc/DocTocHtmlRenderer.php new file mode 100644 index 000000000..e402f3eb4 --- /dev/null +++ b/modules/doc/library/Doc/DocTocHtmlRenderer.php @@ -0,0 +1,45 @@ +html[] = '
    '; + } + + public function endIteration() + { + $this->html[] = '
'; + } + + public function beginChildren() + { + $this->html[] = '
    '; + } + + public function endChildren() + { + $this->html[] = '
'; + } + + public function render($callback) + { + foreach ($this as $node) { + $this->html[] = $callback($node->getValue()); + } + return implode("\n", $this->html); + } +} From 2f1303a13b7471100958982c07d56b55b6d4f361 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:16:37 +0200 Subject: [PATCH 036/184] doc: No longer use a partial for viewing a toc refs #4820 --- .../application/views/scripts/partials/toc.phtml | 14 -------------- modules/doc/library/Doc/DocController.php | 9 ++++----- 2 files changed, 4 insertions(+), 19 deletions(-) delete mode 100644 modules/doc/application/views/scripts/partials/toc.phtml diff --git a/modules/doc/application/views/scripts/partials/toc.phtml b/modules/doc/application/views/scripts/partials/toc.phtml deleted file mode 100644 index c32285516..000000000 --- a/modules/doc/application/views/scripts/partials/toc.phtml +++ /dev/null @@ -1,14 +0,0 @@ -
-

documentation

-
-
-partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) -) ?> -
diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 0929eac4e..ef44de478 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -23,17 +23,16 @@ class DocController extends ActionController } /** - * Render a toc + * Populate toc * * @param string $path Path to the documentation * @param string $name Name of the documentation */ - protected function renderToc($path, $name) + protected function populateToc($path, $name) { $parser = new DocParser($path); - list($docHtml, $docToc) = $parser->getDocAndToc(); - $this->view->docToc = $docToc; + list($docHtml, $tocRenderer) = $parser->getDocAndToc(); + $this->view->tocRenderer = $tocRenderer; $this->view->docName = $name; - $this->_helper->viewRenderer('partials/toc', null, true); } } From d3a9f17fc2f8d0bcbacc4cc7bbbec3b7e21b773a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:18:07 +0200 Subject: [PATCH 037/184] doc/parser: Return the DocTocHtmlRenderer instead of an array refs #4820 --- modules/doc/library/Doc/DocParser.php | 37 ++++++++++++--------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index fc87faf27..3bd6b3a1e 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -10,8 +10,6 @@ use RecursiveIteratorIterator; use RecursiveDirectoryIterator; use Parsedown; use Icinga\Exception\NotReadableError; -use Icinga\Web\Menu; -use Icinga\Web\Url; /** * Parser for documentation written in Markdown @@ -59,10 +57,10 @@ class DocParser ); $fileInfos = iterator_to_array($iter); natcasesort($fileInfos); - $cat = array(); - $toc = array((object) array( + $cat = array(); + $tocStack = array((object) array( 'level' => 0, - 'item' => new Menu('doc') + 'node' => new DocToc() )); $itemPriority = 1; foreach ($fileInfos as $fileInfo) { @@ -84,30 +82,29 @@ class DocParser list($header, $level) = $header; $id = $this->extractHeaderId($header); $nofollow = false; - $this->reduceToc($toc, $level); + $this->reduceToc($tocStack, $level); if ($id === null) { $path = array(); - foreach (array_slice($toc, 1) as $entry) { - $path[] = $entry->item->getTitle(); + foreach (array_slice($tocStack, 1) as $entity) { + $path[] = $entity->node->getValue()->title; } $path[] = $header; $id = implode('-', $path); $nofollow = true; } $id = urlencode(str_replace('.', '.', strip_tags($id))); - $item = end($toc)->item->addChild( - $id, - array( + $node = end($tocStack)->node->appendChild( + (object) array( 'id' => $id, 'title' => $header, 'priority' => $itemPriority++, // Post-increment is on purpose 'nofollow' => $nofollow ) ); - $toc[] = ((object) array( + $tocStack[] = (object) array( 'level' => $level, - 'item' => $item - )); + 'node' => $node + ); $line = '' . PHP_EOL . $line; } $cat[] = $line; @@ -120,7 +117,7 @@ class DocParser array($this, 'highlight'), $html ); - return array($html, $toc[0]->item); + return array($html, new DocTocHtmlRenderer($tocStack[0]->node)); } /** @@ -200,14 +197,14 @@ class DocParser } /** - * Reduce the toc to the given level + * Reduce the toc stack to the given level * - * @param array &$toc + * @param array &$tocStack * @param int $level */ - protected function reduceToc(array &$toc, $level) { - while (end($toc)->level >= $level) { - array_pop($toc); + protected function reduceToc(array &$tocStack, $level) { + while (end($tocStack)->level >= $level) { + array_pop($tocStack); } } } From 72ab5027b5afc998b9aea89fdbcbed69a56cdfe8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:18:57 +0200 Subject: [PATCH 038/184] doc: Add view script for the toc of Icinga Web 2's doc refs #4820 --- .../controllers/IcingawebController.php | 2 +- .../application/views/scripts/icingaweb/toc.phtml | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 modules/doc/application/views/scripts/icingaweb/toc.phtml diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 5ddc4d323..0820a82ca 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -13,7 +13,7 @@ class Doc_IcingawebController extends DocController */ public function tocAction() { - $this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2'); + $this->populateToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2'); } /** diff --git a/modules/doc/application/views/scripts/icingaweb/toc.phtml b/modules/doc/application/views/scripts/icingaweb/toc.phtml new file mode 100644 index 000000000..065071f01 --- /dev/null +++ b/modules/doc/application/views/scripts/icingaweb/toc.phtml @@ -0,0 +1,15 @@ +
+

documentation

+
+
+ getHelper('Url'); + $view = $this; + ?> + render(function ($section) use ($urlHelper, $view) { + // Chapter name is not yet defined + $path = $urlHelper->url(array('chapterName' => 'tbd'), 'doc/icingaweb/chapter', false, false); + $url = $view->url($path)->setAnchor($section->id); + return sprintf('
  • %s
  • ', $url->getAbsoluteUrl(), $section->title); + }); ?> +
    From dc1bada56f98abe33def0bb002c10b8bb04e7ce0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:19:24 +0200 Subject: [PATCH 039/184] doc: Add a view script for module's doc toc refs #4820 --- .../controllers/ModuleController.php | 3 ++- .../application/views/scripts/module/toc.phtml | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 modules/doc/application/views/scripts/module/toc.phtml diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index fd74050ec..b864ff324 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -45,7 +45,8 @@ class Doc_ModuleController extends DocController $moduleName = $this->getParam('moduleName'); $this->assertModuleEnabled($moduleName); $moduleManager = Icinga::app()->getModuleManager(); - $this->renderToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); + $this->populateToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); + $this->view->moduleName = $moduleName; } /** diff --git a/modules/doc/application/views/scripts/module/toc.phtml b/modules/doc/application/views/scripts/module/toc.phtml new file mode 100644 index 000000000..b3be77d79 --- /dev/null +++ b/modules/doc/application/views/scripts/module/toc.phtml @@ -0,0 +1,17 @@ +
    +

    documentation

    +
    +
    + getHelper('Url'); + $view = $this; + ?> + render(function ($section) use ($urlHelper, $view, $moduleName) { + // Chapter name is not yet defined + $path = $urlHelper->url( + array('moduleName' => $moduleName, 'chapterName' => 'tbd'), 'doc/module/chapter', false, false + ); + $url = $view->url($path)->setAnchor($section->id); + return sprintf('
  • %s
  • ', $url->getAbsoluteUrl(), $section->title); + }); ?> +
    From 49e927c4bf3d72e8575889ada522d57cf929600a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:19:48 +0200 Subject: [PATCH 040/184] doc: Support `doc/module/:moduleName/toc' URLs refs #4820 --- modules/doc/run.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/doc/run.php b/modules/doc/run.php index 9d93ee865..592d85ede 100644 --- a/modules/doc/run.php +++ b/modules/doc/run.php @@ -25,6 +25,16 @@ $docIcingaWebChapter = new Zend_Controller_Router_Route( ) ); +$docModuleToc = new Zend_Controller_Router_Route( + 'doc/module/:moduleName/toc', + array( + 'controller' => 'module', + 'action' => 'toc', + 'module' => 'doc' + ) +); + $this->addRoute('doc/module/chapter', $docModuleChapter); $this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter); +$this->addRoute('doc/module/toc', $docModuleToc); From 0f1983fb25146e8b39bbfce3d5bf8300d5ba0571 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 28 May 2014 17:24:28 +0200 Subject: [PATCH 041/184] doc: Fix links in index view scripts refs #4820 --- modules/doc/application/views/scripts/index/index.phtml | 2 +- modules/doc/application/views/scripts/module/index.phtml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml index 9edb5910d..4381bc2a0 100644 --- a/modules/doc/application/views/scripts/index/index.phtml +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -1,5 +1,5 @@

    Available documentations

    diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml index 5e9eaae9d..bbd74345a 100644 --- a/modules/doc/application/views/scripts/module/index.phtml +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -1,6 +1,10 @@

    Module documentations

    From 370bfca6e6e1f0897aa74b3b755711bb3a9ec501 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 3 Jun 2014 14:53:28 +0200 Subject: [PATCH 042/184] DocParser: Remove itemPriority since it's nowhere used refs #4820 --- modules/doc/library/Doc/DocParser.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 3bd6b3a1e..299295a03 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -62,7 +62,6 @@ class DocParser 'level' => 0, 'node' => new DocToc() )); - $itemPriority = 1; foreach ($fileInfos as $fileInfo) { try { $fileObject = $fileInfo->openFile(); @@ -97,7 +96,6 @@ class DocParser (object) array( 'id' => $id, 'title' => $header, - 'priority' => $itemPriority++, // Post-increment is on purpose 'nofollow' => $nofollow ) ); From ed8de18ad5cfd3fa2d14c0ce740e1755e113de28 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 3 Jun 2014 14:56:44 +0200 Subject: [PATCH 043/184] DocParser: Use first header in a file as section title refs #4820 --- modules/doc/library/Doc/DocParser.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 299295a03..0957b2dd0 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -72,15 +72,20 @@ class DocParser throw new DocException('Couldn\'t get the lock'); } $line = null; + $sectionTitle = null; while (! $fileObject->eof()) { // Save last line for setext-style headers $lastLine = $line; $line = $fileObject->fgets(); $header = $this->extractHeader($line, $lastLine); if ($header !== null) { - list($header, $level) = $header; - $id = $this->extractHeaderId($header); - $nofollow = false; + list($header, $level) = $header; + if ($sectionTitle === null) { + // The first header is the section's title + $sectionTitle = $header; + } + $id = $this->extractHeaderId($header); + $nofollow = false; $this->reduceToc($tocStack, $level); if ($id === null) { $path = array(); From d5cf2f24726d68880f6fcc3e319317c862040563 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 3 Jun 2014 15:23:59 +0200 Subject: [PATCH 044/184] doc: Implement `DocParser::getToc()' refs #4820 --- modules/doc/library/Doc/DocParser.php | 122 ++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 17 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 0957b2dd0..3eb97794d 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -6,8 +6,62 @@ namespace Icinga\Module\Doc; require_once 'vendor/Parsedown/Parsedown.php'; +use ArrayIterator; +use RunetimeException; + +class FileLockingIterator extends ArrayIterator +{ + public function next() + { + $this->current()->flock(LOCK_UN); + parent::next(); + } + + public function valid() + { + if (!parent::valid()) { + return false; + } + $fileInfo = $this->current(); + try { + $fileObject = $fileInfo->openFile(); + } catch (RuntimeException $e) { + throw new DocException($e->getMessage()); + } + if ($fileObject->flock(LOCK_SH) === false) { + throw new DocException('Couldn\'t get the lock'); + } + $this[$this->key()] = $fileObject; + return true; + } +} + +use IteratorAggregate; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; + +class DocIterator implements IteratorAggregate +{ + protected $fileInfos; + + public function __construct($path) + { + $iter = new RecursiveIteratorIterator( + new MarkdownFileIterator( + new RecursiveDirectoryIterator($path) + ) + ); + $fileInfos = iterator_to_array($iter); + natcasesort($fileInfos); + $this->fileInfos = $fileInfos; + } + + public function getIterator() + { + return new FileLockingIterator($this->fileInfos); + } +} + use Parsedown; use Icinga\Exception\NotReadableError; @@ -42,6 +96,56 @@ class DocParser $this->path = $path; } + /** + * Retrieve the table of contents + * + * @return DocTocHtmlRenderer + */ + public function getToc() + { + $tocStack = array((object) array( + 'level' => 0, + 'node' => new DocToc() + )); + foreach (new DocIterator($this->path) as $fileObject) { + $line = null; + while (! $fileObject->eof()) { + // Save last line for setext-style headers + $lastLine = $line; + $line = $fileObject->fgets(); + $header = $this->extractHeader($line, $lastLine); + if ($header !== null) { + list($header, $level) = $header; + $id = $this->extractHeaderId($header); + $nofollow = false; + $this->reduceToc($tocStack, $level); + if ($id === null) { + $path = array(); + foreach (array_slice($tocStack, 1) as $entity) { + $path[] = $entity->node->getValue()->title; + } + $path[] = $header; + $id = implode('-', $path); + $nofollow = true; + } + $id = urlencode(str_replace('.', '.', strip_tags($id))); + $node = end($tocStack)->node->appendChild( + (object) array( + 'id' => $id, + 'title' => $header, + 'nofollow' => $nofollow + ) + ); + $tocStack[] = (object) array( + 'level' => $level, + 'node' => $node + ); + } + } + } + return new DocTocHtmlRenderer($tocStack[0]->node); + } + /** * Retrieve doc as HTML converted from markdown files sorted by filename and the table of contents * @@ -50,27 +154,12 @@ class DocParser */ public function getDocAndToc() { - $iter = new RecursiveIteratorIterator( - new MarkdownFileIterator( - new RecursiveDirectoryIterator($this->path) - ) - ); - $fileInfos = iterator_to_array($iter); - natcasesort($fileInfos); $cat = array(); $tocStack = array((object) array( 'level' => 0, 'node' => new DocToc() )); - foreach ($fileInfos as $fileInfo) { - try { - $fileObject = $fileInfo->openFile(); - } catch (RuntimeException $e) { - throw new DocException($e->getMessage()); - } - if ($fileObject->flock(LOCK_SH) === false) { - throw new DocException('Couldn\'t get the lock'); - } + foreach (new DocIterator($this->path) as $fileObject) { $line = null; $sectionTitle = null; while (! $fileObject->eof()) { @@ -112,7 +201,6 @@ class DocParser } $cat[] = $line; } - $fileObject->flock(LOCK_UN); } $html = Parsedown::instance()->text(implode('', $cat)); $html = preg_replace_callback( From 794ae141fae89371dfe5bd0e1fbc63d104776c38 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:55:58 +0200 Subject: [PATCH 045/184] lib: Let NodeInterface extend RecursiveIterator Since a node may have children and they should be iterable, the Nodefinterface now extends RecursiveIterator. --- library/Icinga/Data/Tree/NodeInterface.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/library/Icinga/Data/Tree/NodeInterface.php b/library/Icinga/Data/Tree/NodeInterface.php index 8ecca86c6..1d96973e0 100644 --- a/library/Icinga/Data/Tree/NodeInterface.php +++ b/library/Icinga/Data/Tree/NodeInterface.php @@ -4,7 +4,9 @@ namespace Icinga\Data\Tree; -interface NodeInterface +use RecursiveIterator; + +interface NodeInterface extends RecursiveIterator { /** * Append a child to the node @@ -21,18 +23,4 @@ interface NodeInterface * @return mixed */ public function getValue(); - - /** - * Whether the node has children - * - * @return bool - */ - public function hasChildren(); - - /** - * Get the node's children - * - * @return array - */ - public function getChildren(); } From d84532d593e71a0024194cab8f05906431b2e044 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:57:18 +0200 Subject: [PATCH 046/184] lib: Remove TreeIterator Nodes implementing the NodeInterface (which extends the RecursiveIterator interface) are already iterable. --- library/Icinga/Data/Tree/TreeIterator.php | 90 ----------------------- 1 file changed, 90 deletions(-) delete mode 100644 library/Icinga/Data/Tree/TreeIterator.php diff --git a/library/Icinga/Data/Tree/TreeIterator.php b/library/Icinga/Data/Tree/TreeIterator.php deleted file mode 100644 index 6722f3b35..000000000 --- a/library/Icinga/Data/Tree/TreeIterator.php +++ /dev/null @@ -1,90 +0,0 @@ -nodes = $node->getChildren(); -// } -// -// public function hasChildren() -// { -// return $this->current()->hasChildren(); -// } -// -// public function getChildren() -// { -// return new self($this->current()); -// } -// -// public function current() -// { -// return $this->nodes[$this->position]; -// } -// -// public function next() -// { -// ++$this->position; -// } -// -// public function valid() -// { -// return isset($this->nodes[$this->position]); -// } -// -// public function rewind() -// { -// $this->position = 0; -// } -// -// public function key() -// { -// return $this->position; -// } -//} - -use ArrayIterator; -use RecursiveIterator; - -class TreeIterator extends ArrayIterator implements RecursiveIterator -{ - /** - * Create a new TreeIterator - * - * @param NodeInterface $node - */ - public function __construct(NodeInterface $node) - { - parent::__construct($node->getChildren()); - } - - /** - * Whether an iterator can be created for the current node - * - * @return bool - */ - public function hasChildren() - { - return $this->current()->hasChildren(); - } - - /** - * Return an iterator for the current node - * - * @return self - */ - public function getChildren() - { - return new self($this->current()); - } -} From ce0aee5e4129dbd5d74e6476c5899dba4c5068ec Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:58:14 +0200 Subject: [PATCH 047/184] lib: Add Data/Tree/Node.php --- library/Icinga/Data/Tree/Node.php | 62 +++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 library/Icinga/Data/Tree/Node.php diff --git a/library/Icinga/Data/Tree/Node.php b/library/Icinga/Data/Tree/Node.php new file mode 100644 index 000000000..db3a49749 --- /dev/null +++ b/library/Icinga/Data/Tree/Node.php @@ -0,0 +1,62 @@ +value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function appendChild($value) + { + $child = new static($value); + $this->push($child); + return $child; + } + + public function hasChildren() + { + $current = $this->current(); + if ($current === null) {; + $current = $this; + } + return ! $current->isEmpty(); + } + + public function getChildren() + { + $current = $this->current(); + if ($current === null) {; + $current = $this; + } + return $current; + } + + public function findNodeBy($callback) + { + if (! is_callable($callback)) { + throw new RuntimeException(); + } + foreach (new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST) as $node) { + if (call_user_func($callback, $node)) { + return $node; + } + } + return null; + } +} From 6c8d35c6671f5e5ea9217b9a5a77f4c5f9ff1097 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:58:40 +0200 Subject: [PATCH 048/184] lib: Add a not yet customizable node renderer --- library/Icinga/Data/Tree/NodeRenderer.php | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 library/Icinga/Data/Tree/NodeRenderer.php diff --git a/library/Icinga/Data/Tree/NodeRenderer.php b/library/Icinga/Data/Tree/NodeRenderer.php new file mode 100644 index 000000000..4cebb7e01 --- /dev/null +++ b/library/Icinga/Data/Tree/NodeRenderer.php @@ -0,0 +1,58 @@ +content[] = '
      '; + } + + public function endIteration() + { + $this->content[] = '
    '; + } + + public function beginChildren() + { + $this->content[] = '
      '; + } + + public function endChildren() + { + $this->content[] = '
    '; + } + + public function render($callback) + { + if (! is_callable($callback)) { + throw new RuntimeException('Callable expected'); + } + foreach ($this as $node) { + try { + $content = call_user_func($callback, $node); + } catch (Exception $e) { + throw new RuntimeException($e); + } + $this->content[] = $content; + } + return implode("\n", $this->content); + } +} From cf987544c5c5499abbbbad0fb13b4b6dc63a6b87 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:59:11 +0200 Subject: [PATCH 049/184] doc: Remove class DocToc as it's superseded by Data/Tree/Node refs #4820 --- modules/doc/library/Doc/DocToc.php | 48 ------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 modules/doc/library/Doc/DocToc.php diff --git a/modules/doc/library/Doc/DocToc.php b/modules/doc/library/Doc/DocToc.php deleted file mode 100644 index db7d9cb03..000000000 --- a/modules/doc/library/Doc/DocToc.php +++ /dev/null @@ -1,48 +0,0 @@ -value = $value; - } - - public function getValue() - { - return $this->value; - } - - public function appendChild($value) - { - $child = new self($value); - $this->children[] = $child; - return $child; - } - - public function hasChildren() - { - return ! empty($this->children); - } - - public function getChildren() - { - return $this->children; - } - - public function getIterator() - { - return new TreeIterator($this); - } -} From 625a6dae404420998163416ca7240c4c1ba1f0d9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 13:59:59 +0200 Subject: [PATCH 050/184] doc: Remove DocTocHtmlRenderer as it's superseded by Data/Tree/NodeRenderer refs #4820 --- .../doc/library/Doc/DocTocHtmlRenderer.php | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 modules/doc/library/Doc/DocTocHtmlRenderer.php diff --git a/modules/doc/library/Doc/DocTocHtmlRenderer.php b/modules/doc/library/Doc/DocTocHtmlRenderer.php deleted file mode 100644 index e402f3eb4..000000000 --- a/modules/doc/library/Doc/DocTocHtmlRenderer.php +++ /dev/null @@ -1,45 +0,0 @@ -html[] = '
      '; - } - - public function endIteration() - { - $this->html[] = '
    '; - } - - public function beginChildren() - { - $this->html[] = '
      '; - } - - public function endChildren() - { - $this->html[] = '
    '; - } - - public function render($callback) - { - foreach ($this as $node) { - $this->html[] = $callback($node->getValue()); - } - return implode("\n", $this->html); - } -} From 07330c1ca90b7f0e380927c3866c5aff7443f0b2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 14:09:12 +0200 Subject: [PATCH 051/184] doc: Don't use absolute 'use' in MarkdownFileIterator refs #4820 --- modules/doc/library/Doc/MarkdownFileIterator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/library/Doc/MarkdownFileIterator.php b/modules/doc/library/Doc/MarkdownFileIterator.php index e8d982ba6..b68195a3e 100644 --- a/modules/doc/library/Doc/MarkdownFileIterator.php +++ b/modules/doc/library/Doc/MarkdownFileIterator.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Doc; -use \RecursiveFilterIterator; +use RecursiveFilterIterator; /** * Iterator over Markdown files recursively From d446e0db2e860b5e9002164b3fad6ee4e1c5ab2b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 14:10:13 +0200 Subject: [PATCH 052/184] doc: Implement DocParser::getChapter() refs #4820 --- modules/doc/library/Doc/DocIterator.php | 61 ++++++++++ modules/doc/library/Doc/DocParser.php | 143 +++++++++--------------- 2 files changed, 111 insertions(+), 93 deletions(-) create mode 100644 modules/doc/library/Doc/DocIterator.php diff --git a/modules/doc/library/Doc/DocIterator.php b/modules/doc/library/Doc/DocIterator.php new file mode 100644 index 000000000..07a49732f --- /dev/null +++ b/modules/doc/library/Doc/DocIterator.php @@ -0,0 +1,61 @@ +current()->flock(LOCK_UN); + parent::next(); + } + + public function valid() + { + if (!parent::valid()) { + return false; + } + $fileInfo = $this->current(); + try { + $fileObject = $fileInfo->openFile(); + } catch (RuntimeException $e) { + throw new DocException($e->getMessage()); + } + if ($fileObject->flock(LOCK_SH) === false) { + throw new DocException('Couldn\'t get the lock'); + } + $this[$this->key()] = $fileObject; + return true; + } +} + +use IteratorAggregate; +use RecursiveIteratorIterator; +use RecursiveDirectoryIterator; + +class DocIterator implements IteratorAggregate +{ + protected $fileInfos; + + public function __construct($path) + { + $iter = new RecursiveIteratorIterator( + new MarkdownFileIterator( + new RecursiveDirectoryIterator($path) + ) + ); + $fileInfos = iterator_to_array($iter); + natcasesort($fileInfos); + $this->fileInfos = $fileInfos; + } + + public function getIterator() + { + return new FileLockingIterator($this->fileInfos); + } +} diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 3eb97794d..1d77f47b1 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -1,68 +1,13 @@ current()->flock(LOCK_UN); - parent::next(); - } - - public function valid() - { - if (!parent::valid()) { - return false; - } - $fileInfo = $this->current(); - try { - $fileObject = $fileInfo->openFile(); - } catch (RuntimeException $e) { - throw new DocException($e->getMessage()); - } - if ($fileObject->flock(LOCK_SH) === false) { - throw new DocException('Couldn\'t get the lock'); - } - $this[$this->key()] = $fileObject; - return true; - } -} - -use IteratorAggregate; -use RecursiveIteratorIterator; -use RecursiveDirectoryIterator; - -class DocIterator implements IteratorAggregate -{ - protected $fileInfos; - - public function __construct($path) - { - $iter = new RecursiveIteratorIterator( - new MarkdownFileIterator( - new RecursiveDirectoryIterator($path) - ) - ); - $fileInfos = iterator_to_array($iter); - natcasesort($fileInfos); - $this->fileInfos = $fileInfos; - } - - public function getIterator() - { - return new FileLockingIterator($this->fileInfos); - } -} - use Parsedown; +use Icinga\Data\Tree\Node; use Icinga\Exception\NotReadableError; /** @@ -88,10 +33,10 @@ class DocParser public function __construct($path) { if (! is_dir($path)) { - throw new DocException('Doc directory `' . $path .'\' does not exist'); + throw new DocException('Doc directory `' . $path . '\' does not exist'); } if (! is_readable($path)) { - throw new NotReadableError('Doc directory `' . $path .'\' is not readable'); + throw new NotReadableError('Doc directory `' . $path . '\' is not readable'); } $this->path = $path; } @@ -99,16 +44,17 @@ class DocParser /** * Retrieve the table of contents * - * @return DocTocHtmlRenderer + * @return Node */ public function getToc() { $tocStack = array((object) array( 'level' => 0, - 'node' => new DocToc() + 'node' => new Node() )); foreach (new DocIterator($this->path) as $fileObject) { $line = null; + $currentChapterName = null; while (! $fileObject->eof()) { // Save last line for setext-style headers $lastLine = $line; @@ -129,11 +75,17 @@ class DocParser $nofollow = true; } $id = urlencode(str_replace('.', '.', strip_tags($id))); + if ($currentChapterName === null) { + // The first header is the chapter's name + $currentChapterName = $id; + $id = null; + } $node = end($tocStack)->node->appendChild( (object) array( - 'id' => $id, - 'title' => $header, - 'nofollow' => $nofollow + 'id' => $id, + 'title' => $header, + 'nofollow' => $nofollow, + 'chapterName' => $currentChapterName ) ); $tocStack[] = (object) array( @@ -143,72 +95,77 @@ class DocParser } } } - return new DocTocHtmlRenderer($tocStack[0]->node); + return $tocStack[0]->node; } /** - * Retrieve doc as HTML converted from markdown files sorted by filename and the table of contents + * Retrieve a chapter * - * @return array - * @throws DocException + * @param string $chapterName + * + * @return string */ - public function getDocAndToc() + public function getChapter($chapterName) { $cat = array(); $tocStack = array((object) array( 'level' => 0, - 'node' => new DocToc() + 'node' => new Node() )); + $chapterFound = false; foreach (new DocIterator($this->path) as $fileObject) { $line = null; - $sectionTitle = null; + $currentChapterName = null; + $chapter = array(); while (! $fileObject->eof()) { // Save last line for setext-style headers - $lastLine = $line; - $line = $fileObject->fgets(); - $header = $this->extractHeader($line, $lastLine); + $lastLine = $line; + $line = $fileObject->fgets(); + $header = $this->extractHeader($line, $lastLine); if ($header !== null) { list($header, $level) = $header; - if ($sectionTitle === null) { - // The first header is the section's title - $sectionTitle = $header; - } - $id = $this->extractHeaderId($header); - $nofollow = false; + $id = $this->extractHeaderId($header); $this->reduceToc($tocStack, $level); if ($id === null) { $path = array(); foreach (array_slice($tocStack, 1) as $entity) { $path[] = $entity->node->getValue()->title; } - $path[] = $header; - $id = implode('-', $path); - $nofollow = true; + $path[] = $header; + $id = implode('-', $path); } - $id = urlencode(str_replace('.', '.', strip_tags($id))); - $node = end($tocStack)->node->appendChild( + $id = urlencode(str_replace('.', '.', strip_tags($id))); + if ($currentChapterName === null) { + $currentChapterName = $id; + $id = null; + } + $node = end($tocStack)->node->appendChild( (object) array( - 'id' => $id, - 'title' => $header, - 'nofollow' => $nofollow + 'title' => $header ) ); - $tocStack[] = (object) array( + $tocStack[] = (object) array( 'level' => $level, 'node' => $node ); $line = '' . PHP_EOL . $line; } - $cat[] = $line; + $chapter[] = $line; + } + if ($currentChapterName === $chapterName) { + $chapterFound = true; + $cat = $chapter; + } + if (! $chapterFound) { + $cat = array_merge($cat, $chapter); } } - $html = Parsedown::instance()->text(implode('', $cat)); $html = preg_replace_callback( '#
    (.*?)\
    #s', array($this, 'highlight'), - $html + Parsedown::instance()->text(implode('', $cat)) ); - return array($html, new DocTocHtmlRenderer($tocStack[0]->node)); + return $html; } /** From 16d5d65a05591e18ba721f67abe0a039bf0d7fcb Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 14:10:35 +0200 Subject: [PATCH 053/184] doc: Fix actions and view scripts DocParser usage refs #4820 --- .../controllers/IcingawebController.php | 2 +- .../controllers/IndexController.php | 1 - .../controllers/ModuleController.php | 3 +- .../views/scripts/icingaweb/chapter.phtml | 38 +++++++++++++++++++ .../views/scripts/icingaweb/toc.phtml | 26 ++++++++----- .../views/scripts/index/index.phtml | 4 +- .../views/scripts/module/chapter.phtml | 38 +++++++++++++++++++ .../views/scripts/module/index.phtml | 14 +++---- .../views/scripts/module/toc.phtml | 30 +++++++++------ .../views/scripts/partials/chapter.phtml | 1 - modules/doc/library/Doc/DocController.php | 14 +++---- 11 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 modules/doc/application/views/scripts/icingaweb/chapter.phtml create mode 100644 modules/doc/application/views/scripts/module/chapter.phtml delete mode 100644 modules/doc/application/views/scripts/partials/chapter.phtml diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 0820a82ca..092de4025 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -27,6 +27,6 @@ class Doc_IcingawebController extends DocController if ($chapterName === null) { throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); } - $this->renderChapter($chapterName, Icinga::app()->getApplicationDir('/../doc')); + $this->populateChapter($chapterName, Icinga::app()->getApplicationDir('/../doc')); } } diff --git a/modules/doc/application/controllers/IndexController.php b/modules/doc/application/controllers/IndexController.php index aa44c9c5d..63b5e8cdf 100644 --- a/modules/doc/application/controllers/IndexController.php +++ b/modules/doc/application/controllers/IndexController.php @@ -8,6 +8,5 @@ class Doc_IndexController extends DocController { public function indexAction() { - } } diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index b864ff324..07092c4ed 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -63,6 +63,7 @@ class Doc_ModuleController extends DocController throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); } $moduleManager = Icinga::app()->getModuleManager(); - $this->renderChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + $this->populateChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + $this->view->moduleName = $moduleName; } } diff --git a/modules/doc/application/views/scripts/icingaweb/chapter.phtml b/modules/doc/application/views/scripts/icingaweb/chapter.phtml new file mode 100644 index 000000000..328d0bebe --- /dev/null +++ b/modules/doc/application/views/scripts/icingaweb/chapter.phtml @@ -0,0 +1,38 @@ +getHelper('Url'); +$view = $this; +?> +[^>]*?\s+)?href="#(?P[^"]+)"/im', + function($match) use ($toc, $urlHelper, $view) { + if (($node = $toc->findNodeBy(function ($node) use ($match) { + $section = $node->getValue(); + if (($section->id === null && $section->chapterName === $match['fragment']) + || $section->id === $match['fragment'] + ) { + return true; + } + return false; + }))) { + $section = $node->getValue(); + $path = $urlHelper->url( + array('chapterName' => $section->chapterName), + 'doc/icingaweb/chapter', + false, + false + ); + $url = $view->url($path); + if ($section->id) { + $url->setAnchor($section->id); + } + return sprintf( + 'nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl() + ); + } + return $match[0]; + }, + $chapterHtml +); ?> diff --git a/modules/doc/application/views/scripts/icingaweb/toc.phtml b/modules/doc/application/views/scripts/icingaweb/toc.phtml index 065071f01..bf91ffc77 100644 --- a/modules/doc/application/views/scripts/icingaweb/toc.phtml +++ b/modules/doc/application/views/scripts/icingaweb/toc.phtml @@ -1,15 +1,23 @@
    -

    documentation

    +

    documentation

    - getHelper('Url'); $view = $this; - ?> - render(function ($section) use ($urlHelper, $view) { - // Chapter name is not yet defined - $path = $urlHelper->url(array('chapterName' => 'tbd'), 'doc/icingaweb/chapter', false, false); - $url = $view->url($path)->setAnchor($section->id); - return sprintf('
  • %s
  • ', $url->getAbsoluteUrl(), $section->title); - }); ?> + ?> + render(function ($node) use ($urlHelper, $view) { + $section = $node->getValue(); + $path = $urlHelper->url(array('chapterName' => $section->chapterName), 'doc/icingaweb/chapter', false, false); + $url = $view->url($path); + if ($section->id) { + $url->setAnchor($section->id); + } + return sprintf( + '
  • %s
  • ', + $section->nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $section->title + ); + }); ?>
    diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml index 4381bc2a0..3cebdca69 100644 --- a/modules/doc/application/views/scripts/index/index.phtml +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -1,5 +1,5 @@

    Available documentations

    diff --git a/modules/doc/application/views/scripts/module/chapter.phtml b/modules/doc/application/views/scripts/module/chapter.phtml new file mode 100644 index 000000000..59a6ecb33 --- /dev/null +++ b/modules/doc/application/views/scripts/module/chapter.phtml @@ -0,0 +1,38 @@ +getHelper('Url'); +$view = $this; +?> +[^>]*?\s+)?href="#(?P[^"]+)"/im', + function($match) use ($toc, $moduleName, $urlHelper, $view) { + if (($node = $toc->findNodeBy(function ($node) use ($match) { + $section = $node->getValue(); + if (($section->id === null && $section->chapterName === $match['fragment']) + || $section->id === $match['fragment'] + ) { + return true; + } + return false; + }))) { + $section = $node->getValue(); + $path = $urlHelper->url( + array('moduleName' => $moduleName, 'chapterName' => $section->chapterName), + 'doc/module/chapter', + false, + false + ); + $url = $view->url($path); + if ($section->id) { + $url->setAnchor($section->id); + } + return sprintf( + 'nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl() + ); + } + return $match[0]; + }, + $chapterHtml +); ?> diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml index bbd74345a..c3522c4c2 100644 --- a/modules/doc/application/views/scripts/module/index.phtml +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -1,10 +1,10 @@

    Module documentations

    diff --git a/modules/doc/application/views/scripts/module/toc.phtml b/modules/doc/application/views/scripts/module/toc.phtml index b3be77d79..ec33c507c 100644 --- a/modules/doc/application/views/scripts/module/toc.phtml +++ b/modules/doc/application/views/scripts/module/toc.phtml @@ -1,17 +1,25 @@
    -

    documentation

    +

    documentation

    - getHelper('Url'); $view = $this; - ?> - render(function ($section) use ($urlHelper, $view, $moduleName) { - // Chapter name is not yet defined - $path = $urlHelper->url( - array('moduleName' => $moduleName, 'chapterName' => 'tbd'), 'doc/module/chapter', false, false - ); - $url = $view->url($path)->setAnchor($section->id); - return sprintf('
  • %s
  • ', $url->getAbsoluteUrl(), $section->title); - }); ?> + ?> + render(function ($node) use ($urlHelper, $view, $moduleName) { + $section = $node->getValue(); + $path = $urlHelper->url( + array('moduleName' => $moduleName, 'chapterName' => $section->chapterName), 'doc/module/chapter', false, false + ); + $url = $view->url($path); + if ($section->id) { + $url->setAnchor($section->id); + } + return sprintf( + '
  • %s
  • ', + $section->nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $section->title + ); + }); ?>
    diff --git a/modules/doc/application/views/scripts/partials/chapter.phtml b/modules/doc/application/views/scripts/partials/chapter.phtml deleted file mode 100644 index b0e1fe0cb..000000000 --- a/modules/doc/application/views/scripts/partials/chapter.phtml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index ef44de478..bc335f381 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -4,22 +4,22 @@ namespace Icinga\Module\Doc; +use Icinga\Data\Tree\NodeRenderer; use Icinga\Web\Controller\ActionController; class DocController extends ActionController { /** - * Render a chapter + * Populate a chapter * * @param string $chapterName Name of the chapter * @param string $path Path to the documentation */ - protected function renderChapter($chapterName, $path) + protected function populateChapter($chapterName, $path) { $parser = new DocParser($path); - list($docHtml, $docToc) = $parser->getDocAndToc(); - $this->view->chapterHtml = $docHtml; - $this->_helper->viewRenderer('partials/chapter', null, true); + $this->view->chapterHtml = $parser->getChapter($chapterName); + $this->view->toc = $parser->getToc(); } /** @@ -31,8 +31,8 @@ class DocController extends ActionController protected function populateToc($path, $name) { $parser = new DocParser($path); - list($docHtml, $tocRenderer) = $parser->getDocAndToc(); - $this->view->tocRenderer = $tocRenderer; + $toc = $parser->getToc(); + $this->view->tocRenderer = new NodeRenderer($toc); $this->view->docName = $name; } } From 61ac3b0168cd2979bd20012f35de9c95ee6637b7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 6 Jun 2014 14:45:13 +0200 Subject: [PATCH 054/184] doc: Fix translation exceptions refs #6432 refs #4820 --- modules/doc/application/locale/.gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 modules/doc/application/locale/.gitkeep diff --git a/modules/doc/application/locale/.gitkeep b/modules/doc/application/locale/.gitkeep new file mode 100644 index 000000000..9fc47527c --- /dev/null +++ b/modules/doc/application/locale/.gitkeep @@ -0,0 +1 @@ +Remove this file and the locale directory once #6432 has been fixed. From 7f6010e1f8b1cca800627088428c581b36f9ea38 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 13 Jun 2014 17:22:43 +0200 Subject: [PATCH 055/184] lib/tree: Add PHPDoc to Node's methods --- library/Icinga/Data/Tree/Node.php | 55 ++++++++++++++++++++-- library/Icinga/Data/Tree/NodeInterface.php | 6 +-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/library/Icinga/Data/Tree/Node.php b/library/Icinga/Data/Tree/Node.php index db3a49749..b8f9e8dad 100644 --- a/library/Icinga/Data/Tree/Node.php +++ b/library/Icinga/Data/Tree/Node.php @@ -4,24 +4,47 @@ namespace Icinga\Data\Tree; +use Exception; use RecursiveIteratorIterator; use RuntimeException; use SplDoublyLinkedList; class Node extends SplDoublyLinkedList implements NodeInterface { + /** + * The node's value + * + * @var mixed + */ protected $value; + /** + * Create a new node + * + * @param mixed $value The node's value + */ public function __construct($value = null) { $this->value = $value; } + /** + * Get the node's value + * + * @return mixed + */ public function getValue() { return $this->value; } + /** + * Create a new node from the given value and insert the node as the last child of this node + * + * @param mixed $value The node's value + * + * @return NodeInterface The appended node + */ public function appendChild($value) { $child = new static($value); @@ -29,31 +52,55 @@ class Node extends SplDoublyLinkedList implements NodeInterface return $child; } + /** + * Whether this node has child nodes + * + * @return bool + */ public function hasChildren() { $current = $this->current(); - if ($current === null) {; + if ($current === null) { $current = $this; } return ! $current->isEmpty(); } + /** + * Get the node's child nodes + * + * @return NodeInterface + */ public function getChildren() { $current = $this->current(); - if ($current === null) {; + if ($current === null) { $current = $this; } return $current; } + /** + * Find the first child node by searching through nodes deeper than the immediate children using a custom function + * + * @param $callback + * + * @return NodeInterface|null + * @throws Exception + */ public function findNodeBy($callback) { if (! is_callable($callback)) { - throw new RuntimeException(); + throw new RuntimeException('Callable expected'); } foreach (new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST) as $node) { - if (call_user_func($callback, $node)) { + try { + $found = call_user_func($callback, $node); + } catch (Exception $e) { + // TODO(el): Log exception and return false instead? + throw $e; + } + if ($found) { return $node; } } diff --git a/library/Icinga/Data/Tree/NodeInterface.php b/library/Icinga/Data/Tree/NodeInterface.php index 1d96973e0..6953214dc 100644 --- a/library/Icinga/Data/Tree/NodeInterface.php +++ b/library/Icinga/Data/Tree/NodeInterface.php @@ -9,11 +9,11 @@ use RecursiveIterator; interface NodeInterface extends RecursiveIterator { /** - * Append a child to the node + * Create a new node from the given value and insert the node as the last child of this node * - * @param mixed $value + * @param mixed $value The node's value * - * @return self + * @return NodeInterface The appended node */ public function appendChild($value); From 8fe1d49ce94161d37ce643a05b4ddaff6dd2dc2c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 13 Jun 2014 17:23:20 +0200 Subject: [PATCH 056/184] doc/cs: Begin additional if conditions on a new line --- modules/doc/library/Doc/DocParser.php | 19 +++++++++---------- .../doc/library/Doc/MarkdownFileIterator.php | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 40a6c298e..728dd1c08 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -160,12 +160,11 @@ class DocParser $cat = array_merge($cat, $chapter); } } - $html = preg_replace_callback( + return preg_replace_callback( '#
    (.*?)\
    #s', array($this, 'highlight'), Parsedown::instance()->text(implode('', $cat)) ); - return $html; } /** @@ -194,9 +193,9 @@ class DocParser return null; } $header = null; - if ($line && - $line[0] === '#' && - preg_match('/^#+/', $line, $match) === 1 + if ($line + && $line[0] === '#' + && preg_match('/^#+/', $line, $match) === 1 ) { // Atx-style $level = strlen($match[0]); @@ -205,9 +204,9 @@ class DocParser return null; } } elseif ( - $line && - ($line[0] === '=' || $line[0] === '-') && - preg_match('/^[=-]+\s*$/', $line, $match) === 1 + $line + && ($line[0] === '=' || $line[0] === '-') + && preg_match('/^[=-]+\s*$/', $line, $match) === 1 ) { // Setext $header = trim($lastLine); @@ -235,8 +234,8 @@ class DocParser */ protected function extractHeaderId(&$header) { - if ($header[0] === '<' && - preg_match('#(?:<(?Pa|span) id="(?P.+)">)#u', $header, $match) + if ($header[0] === '<' + && preg_match('#(?:<(?Pa|span) id="(?P.+)">)#u', $header, $match) ) { $header = str_replace($match[0], '', $header); return $match['id']; diff --git a/modules/doc/library/Doc/MarkdownFileIterator.php b/modules/doc/library/Doc/MarkdownFileIterator.php index b68195a3e..2fc8cef1b 100644 --- a/modules/doc/library/Doc/MarkdownFileIterator.php +++ b/modules/doc/library/Doc/MarkdownFileIterator.php @@ -20,7 +20,7 @@ class MarkdownFileIterator extends RecursiveFilterIterator public function accept() { $current = $this->getInnerIterator()->current(); - if (!$current->isFile()) { + if (! $current->isFile()) { return false; } $filename = $current->getFilename(); From e73471030c946229f62eb5bbc4fef3ec7de72b0d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 30 Jun 2014 11:43:25 +0200 Subject: [PATCH 057/184] doc/modules/index: Only list modules which have the 'doc' directory refs #4820 --- .../application/controllers/ModuleController.php | 14 +++++++++++--- .../application/views/scripts/module/index.phtml | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 07092c4ed..e8b7c2fdd 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -2,18 +2,26 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use \Zend_Controller_Action_Exception; +use Zend_Controller_Action_Exception; use Icinga\Application\Icinga; use Icinga\Module\Doc\DocController; class Doc_ModuleController extends DocController { /** - * List available modules + * List modules which are enabled and having the 'doc' directory */ public function indexAction() { - $this->view->enabledModules = Icinga::app()->getModuleManager()->listEnabledModules(); + $moduleManager = Icinga::app()->getModuleManager(); + $modules = array(); + foreach (Icinga::app()->getModuleManager()->listEnabledModules() as $enabledModule) { + $docDir = $moduleManager->getModuleDir($enabledModule, '/doc'); + if (is_dir($docDir)) { + $modules[] = $enabledModule; + } + } + $this->view->modules = $modules; } /** diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml index c3522c4c2..0755748c7 100644 --- a/modules/doc/application/views/scripts/module/index.phtml +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -1,6 +1,6 @@

    Module documentations

      - +
    • From 93cc24a93a87d5a2548486021e816884f1db6ff7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 30 Jun 2014 15:18:22 +0200 Subject: [PATCH 058/184] doc module: Move `DocException' beneath `Exception' namespace There are more exceptions to come which also will be beneath the `Exception' namespace. refs #4820 --- modules/doc/library/Doc/{ => Exception}/DocException.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename modules/doc/library/Doc/{ => Exception}/DocException.php (52%) diff --git a/modules/doc/library/Doc/DocException.php b/modules/doc/library/Doc/Exception/DocException.php similarity index 52% rename from modules/doc/library/Doc/DocException.php rename to modules/doc/library/Doc/Exception/DocException.php index 795c9a5f9..f684f1faa 100644 --- a/modules/doc/library/Doc/DocException.php +++ b/modules/doc/library/Doc/Exception/DocException.php @@ -2,10 +2,13 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Module\Doc; +namespace Icinga\Module\Doc\Exception; use Exception; +/** + * Exception thrown if an error in the documentation module occurs + */ class DocException extends Exception { } From 0382e2265c6c14594fe40321d91dc518407655bb Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 30 Jun 2014 15:21:16 +0200 Subject: [PATCH 059/184] doc module: Add `ChapterNotFoundException' refs #4820 --- .../Doc/Exception/ChapterNotFoundException.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 modules/doc/library/Doc/Exception/ChapterNotFoundException.php diff --git a/modules/doc/library/Doc/Exception/ChapterNotFoundException.php b/modules/doc/library/Doc/Exception/ChapterNotFoundException.php new file mode 100644 index 000000000..1e9bcf7e9 --- /dev/null +++ b/modules/doc/library/Doc/Exception/ChapterNotFoundException.php @@ -0,0 +1,12 @@ + Date: Mon, 30 Jun 2014 15:21:35 +0200 Subject: [PATCH 060/184] doc module: Add `DocEmptyException' refs #4820 --- .../doc/library/Doc/Exception/DocEmptyException.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 modules/doc/library/Doc/Exception/DocEmptyException.php diff --git a/modules/doc/library/Doc/Exception/DocEmptyException.php b/modules/doc/library/Doc/Exception/DocEmptyException.php new file mode 100644 index 000000000..032a01744 --- /dev/null +++ b/modules/doc/library/Doc/Exception/DocEmptyException.php @@ -0,0 +1,12 @@ + Date: Mon, 30 Jun 2014 15:22:32 +0200 Subject: [PATCH 061/184] doc module: Add `DocIterator::count()' Method used to count available markdown documentation files. refs #4820 --- modules/doc/library/Doc/DocIterator.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/doc/library/Doc/DocIterator.php b/modules/doc/library/Doc/DocIterator.php index 07a49732f..1a229c918 100644 --- a/modules/doc/library/Doc/DocIterator.php +++ b/modules/doc/library/Doc/DocIterator.php @@ -34,11 +34,12 @@ class FileLockingIterator extends ArrayIterator } } +use Countable; use IteratorAggregate; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; -class DocIterator implements IteratorAggregate +class DocIterator implements Countable, IteratorAggregate { protected $fileInfos; @@ -54,6 +55,11 @@ class DocIterator implements IteratorAggregate $this->fileInfos = $fileInfos; } + public function count() + { + return count($this->fileInfos); + } + public function getIterator() { return new FileLockingIterator($this->fileInfos); From 1bbfa9b9cadfe8ac0179375a714565f0ed1c36fe Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 30 Jun 2014 15:24:40 +0200 Subject: [PATCH 062/184] doc module: Let `DocParser' throw `DocEmptyException' and `ChapterNotFound' exception `DocEmptyException' is thrown during construction when a documentation directory is empty. `ChapterNotFound' is thrown when a chapter was not found when calling `getChapter()'. refs #4820 --- modules/doc/library/Doc/DocParser.php | 45 +++++++++++++++++---------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 728dd1c08..c213c764a 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -9,6 +9,9 @@ require_once 'IcingaVendor/Parsedown/Parsedown.php'; use Parsedown; use Icinga\Data\Tree\Node; use Icinga\Exception\NotReadableError; +use Icinga\Module\Doc\Exception\ChapterNotFoundException; +use Icinga\Module\Doc\Exception\DocEmptyException; +use Icinga\Module\Doc\Exception\DocException; /** * Parser for documentation written in Markdown @@ -22,13 +25,21 @@ class DocParser */ protected $path; + /** + * Iterator over documentation files + * + * @var DocIterator + */ + protected $docIterator; + /** * Create a new documentation parser for the given path * * @param string $path Path to the documentation * - * @throws DocException - * @throws NotReadableError + * @throws DocException If the documentation directory does not exist + * @throws NotReadableError If the documentation directory is not readable + * @throws DocEmptyException If the documentation directory is empty */ public function __construct($path) { @@ -38,13 +49,18 @@ class DocParser if (! is_readable($path)) { throw new NotReadableError('Doc directory `' . $path . '\' is not readable'); } + $docIterator = new DocIterator($path); + if ($docIterator->count() === 0) { + throw new DocEmptyException('Doc directory `' . $path . '\' is empty'); + } $this->path = $path; + $this->docIterator = $docIterator; } /** * Retrieve the table of contents * - * @return Node + * @return Node */ public function getToc() { @@ -52,7 +68,7 @@ class DocParser 'level' => 0, 'node' => new Node() )); - foreach (new DocIterator($this->path) as $fileObject) { + foreach ($this->docIterator as $fileObject) { $line = null; $currentChapterName = null; while (! $fileObject->eof()) { @@ -104,16 +120,15 @@ class DocParser * @param string $chapterName * * @return string + * @throws ChapterNotFoundException If the chapter was not found */ public function getChapter($chapterName) { - $cat = array(); $tocStack = array((object) array( 'level' => 0, 'node' => new Node() )); - $chapterFound = false; - foreach (new DocIterator($this->path) as $fileObject) { + foreach ($this->docIterator as $fileObject) { $line = null; $currentChapterName = null; $chapter = array(); @@ -153,18 +168,14 @@ class DocParser $chapter[] = $line; } if ($currentChapterName === $chapterName) { - $chapterFound = true; - $cat = $chapter; - } - if (! $chapterFound) { - $cat = array_merge($cat, $chapter); + return preg_replace_callback( + '#
      (.*?)\
      #s', + array($this, 'highlight'), + Parsedown::instance()->text(implode('', $chapter)) + ); } } - return preg_replace_callback( - '#
      (.*?)\
      #s', - array($this, 'highlight'), - Parsedown::instance()->text(implode('', $cat)) - ); + throw new ChapterNotFoundException('Chapter \'' . $chapterName . '\' not found'); } /** From 2ea418cbe60ebca1b89e7110b7ef444bcf54e1f7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 30 Jun 2014 15:48:43 +0200 Subject: [PATCH 063/184] doc module: Respond with 404 if a chapter was not found or the doc directory is empty refs #4820 --- .../application/controllers/ModuleController.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index e8b7c2fdd..2ce1b1e73 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -2,9 +2,10 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -use Zend_Controller_Action_Exception; +use \Zend_Controller_Action_Exception; use Icinga\Application\Icinga; use Icinga\Module\Doc\DocController; +use Icinga\Module\Doc\Exception\DocException; class Doc_ModuleController extends DocController { @@ -53,7 +54,11 @@ class Doc_ModuleController extends DocController $moduleName = $this->getParam('moduleName'); $this->assertModuleEnabled($moduleName); $moduleManager = Icinga::app()->getModuleManager(); - $this->populateToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); + try { + $this->populateToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); + } catch (DocException $e) { + throw new Zend_Controller_Action_Exception($e->getMessage(), 404); + } $this->view->moduleName = $moduleName; } @@ -71,7 +76,11 @@ class Doc_ModuleController extends DocController throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); } $moduleManager = Icinga::app()->getModuleManager(); - $this->populateChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + try { + $this->populateChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + } catch (DocException $e) { + throw new Zend_Controller_Action_Exception($e->getMessage(), 404); + } $this->view->moduleName = $moduleName; } } From eca0d50baed1521633bf7c416cb79a0388c3875b Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 17 Jul 2014 11:26:18 +0200 Subject: [PATCH 064/184] Don't use printf format strings as array keys An array with printf format strings as keys looks pretty ugly --- .../views/scripts/list/contacts.phtml | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml index 642fd7b57..fb53fef55 100644 --- a/modules/monitoring/application/views/scripts/list/contacts.phtml +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -26,15 +26,26 @@ $contactHelper = $this->getHelper('ContactFlags'); ) ?>">contact_name ?>
      (contact_alias ?>) %1$s' => $contact->contact_email, - 'Pager: %s' => $contact->contact_pager, - 'Service notification period: %s' => $contact->contact_notify_service_timeperiod, - 'Host notification period: %s' => $contact->contact_notify_host_timeperiod - ) as $format => $value): + 'eMail' => array( + $contact->contact_email, '%1$s' + ), + 'Pager' => $contact->contact_pager, + 'Service notification period' => $contact->contact_notify_service_timeperiod, + 'Host notification period' => $contact->contact_notify_host_timeperiod + ) as $key => $value): + if (is_string($value)) { + $format = '%s'; + } else { + $format = $value[1]; + $value = $value[0]; + } if ($value): ?>
      From 882699201d1baf8bc6506a89c3872a9475364394 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 17 Jul 2014 17:33:45 +0200 Subject: [PATCH 065/184] Contacts: replace foreach loop with hardcoded HTML and move the styles to a CSS file --- .../views/scripts/list/contacts.phtml | 63 +++++++++---------- modules/monitoring/public/css/module.less | 21 +++++++ 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml index fb53fef55..81a061973 100644 --- a/modules/monitoring/application/views/scripts/list/contacts.phtml +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -14,42 +14,41 @@ $contactHelper = $this->getHelper('ContactFlags'); href('monitoring/show/contacts', array('contact' => $contact->contact_name)); ?> -
      - + foreach ($contacts as $contact): ?> +
      + contact_name ?> (contact_alias ?>) - array( - $contact->contact_email, '%1$s' - ), - 'Pager' => $contact->contact_pager, - 'Service notification period' => $contact->contact_notify_service_timeperiod, - 'Host notification period' => $contact->contact_notify_host_timeperiod - ) as $key => $value): - if (is_string($value)) { - $format = '%s'; - } else { - $format = $value[1]; - $value = $value[0]; - } - if ($value): ?> -
      - -
      +
      %2$s', + t('Email'), + htmlspecialchars($contact->contact_email) + ) ?>
      + contact_pager): ?> +
      + : + contact_pager) ?> +
      + +
      +
      +
      + : + contact_notify_service_timeperiod) ?> +
      +
      + : + contact_notify_host_timeperiod) ?> +
      +
      - + +
      +
      diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index c11b18414..1d60b75c7 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -32,3 +32,24 @@ table.objectstate td.state { padding-top: 0.5em; padding-bottom: 0.5em; } + +div.contacts div.contact { + background-color: lightgray; + padding: 0.5em; + border-radius: 0.5em; + overflow: hidden; + margin: 0.125em; + float: left; +} + +div.contacts div.contact > img { + width: 60px; + border: 4px solid white; + border-radius: 8px; + margin-right: 8px; + float: left; +} + +div.contacts div.notification-periods { + margin-top: 0.5em; +} From 96d34064463ae2dd1e3356f231d491ae730ffea1 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 17 Jul 2014 17:46:28 +0200 Subject: [PATCH 066/184] Prefer style="clear: both;" rather than Bootstrap's class="clearfix" --- .../monitoring/application/controllers/MonitoringCommands.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/MonitoringCommands.php b/modules/monitoring/application/controllers/MonitoringCommands.php index 0a94c17b2..faa04114e 100644 --- a/modules/monitoring/application/controllers/MonitoringCommands.php +++ b/modules/monitoring/application/controllers/MonitoringCommands.php @@ -64,7 +64,7 @@ class Zend_View_Helper_MonitoringCommands extends Zend_View_Helper_Abstract $out .= ''; - $out .= '
      '; + $out .= '
      '; if ($type === Meta::TYPE_FULL) { return '
      '. $out. '
      '; From f9513cc14b7f81552cbdddb5ea51854cccf60db4 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 17 Jul 2014 19:07:35 +0200 Subject: [PATCH 067/184] Fix comment prefix in config/authentication.ini --- config/authentication.ini.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/authentication.ini.in b/config/authentication.ini.in index 554356492..71d9e4402 100644 --- a/config/authentication.ini.in +++ b/config/authentication.ini.in @@ -1,7 +1,7 @@ ; authentication.ini ; ; Each section listed in this configuration represents a backend used to authenticate users. The backend configurations -: must define a resource referring to a configured database or LDAP connection in the INI file resources.ini. +; must define a resource referring to a configured database or LDAP connection in the INI file resources.ini. ; ; The backends will be processed from top to bottom using the first backend for authentication which reports that ; the user trying to log in is available. From 209894d85726bb95cf15a26d73a685da34cf3548 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 18 Jul 2014 16:30:14 +0200 Subject: [PATCH 068/184] DbQuery: add dummy isTimestamp function Not the best solution, but helps for now. fixes #6675 --- library/Icinga/Data/Db/DbQuery.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 7fef56efc..f064c4df6 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -192,6 +192,24 @@ class DbQuery extends SimpleQuery return $this->escapeForSql(date('Y-m-d H:i:s', $value)); } + /** + * Return true if an field contains an explicit timestamp + * + * TODO: This is not here to do automagic timestamp stuff. One may + * override this function for custom voodoo, IdoQuery right now + * does. IMO we need to split whereToSql functionality, however + * I'd prefer to wait with this unless we understood how other + * backends will work. We probably should also rename this + * function to isTimestampColumn(). + * + * @param string $field Field Field name to checked + * @return bool Whether this field expects timestamps + */ + public function isTimestamp($field) + { + return $this; + } + public function whereToSql($col, $sign, $expression) { if ($this->isTimestamp($col)) { From 2a204897b49c404a318f4f9c6b7324053b7fe874 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 18 Jul 2014 16:48:52 +0200 Subject: [PATCH 069/184] DbQuery: improve method description Not English mine this was, copy paste did I ;) --- library/Icinga/Data/Db/DbQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index f064c4df6..3fb74a656 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -193,7 +193,7 @@ class DbQuery extends SimpleQuery } /** - * Return true if an field contains an explicit timestamp + * Check for timestamp fields * * TODO: This is not here to do automagic timestamp stuff. One may * override this function for custom voodoo, IdoQuery right now From f9a274d0790a5c8abfba38457794c08d08081013 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Fri, 18 Jul 2014 11:48:26 +0200 Subject: [PATCH 070/184] Add contact detail view refs #4804 refs #6514 --- .../controllers/ShowController.php | 34 +++++++++++ .../views/scripts/show/contact.phtml | 56 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 modules/monitoring/application/views/scripts/show/contact.phtml diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index d7d14ce15..d503ba512 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -103,6 +103,40 @@ class Monitoring_ShowController extends Controller )); } + public function contactAction() + { + $contact = $this->getParam('contact'); + if (! $contact) { + throw new Zend_Controller_Action_Exception( + $this->translate('The parameter `contact\' is required'), + 404 + ); + } + $query = $this->backend->select()->from('contact', array( + 'contact_name', + 'contact_id', + 'contact_alias', + 'contact_email', + 'contact_pager', + 'contact_notify_service_timeperiod', + 'contact_notify_service_recovery', + 'contact_notify_service_warning', + 'contact_notify_service_critical', + 'contact_notify_service_unknown', + 'contact_notify_service_flapping', + 'contact_notify_service_downtime', + 'contact_notify_host_timeperiod', + 'contact_notify_host_recovery', + 'contact_notify_host_down', + 'contact_notify_host_unreachable', + 'contact_notify_host_flapping', + 'contact_notify_host_downtime', + )); + $query->where('contact_name', $contact); + $this->view->contacts = $query->paginate(); + $this->view->contact_name = $contact; + } + /** * Creating tabs for this controller * @return Tabs diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml new file mode 100644 index 000000000..609bbbc69 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -0,0 +1,56 @@ +getHelper('ContactFlags'); +?> +
      + + + + + + + + + + + + + contact_pager): ?> + + + + + + + + + + + + + + + + + + + + + + +
      + contact_name) ?> (contact_alias) + ?>) +
      %1$s', + htmlspecialchars($contact->contact_email) + ); ?>
      contact_pager) ?>
      contactFlags($contact, 'service')) ?>
      contactFlags($contact, 'host')) ?>
      contact_notify_service_timeperiod) ?>
      contact_notify_host_timeperiod) ?>
      + +
      From 5ea02b41eab3b6e4b697695b80df77799526556c Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Fri, 18 Jul 2014 12:30:39 +0200 Subject: [PATCH 071/184] Optimize variable names and positions, cast array direct to object --- library/Icinga/Protocol/File/Reader.php | 64 ++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/library/Icinga/Protocol/File/Reader.php b/library/Icinga/Protocol/File/Reader.php index f5677e855..fcefdfc06 100644 --- a/library/Icinga/Protocol/File/Reader.php +++ b/library/Icinga/Protocol/File/Reader.php @@ -4,6 +4,7 @@ namespace Icinga\Protocol\File; +use Exception; use Icinga\Data\DatasourceInterface; /** @@ -15,6 +16,8 @@ use Icinga\Data\DatasourceInterface; */ class Reader implements DatasourceInterface { + private static $EOLLen = null; + /** * Name of the file to read * @@ -34,6 +37,9 @@ class Reader implements DatasourceInterface */ public function __construct($config) { + if (self::$EOLLen === null) { + self::$EOLLen = strlen(PHP_EOL); + } $this->config = $config; $this->filename = $config->filename; } @@ -57,10 +63,7 @@ class Reader implements DatasourceInterface { $all = array(); foreach ($this->fetchPairs($query) as $index => $value) { - $all[$index] = new \stdClass(); - foreach ($value as $key => $value_2) { - $all[$index]->{$key} = $value_2; - } + $all[$index] = (object) $value; } return $all; } @@ -87,9 +90,9 @@ class Reader implements DatasourceInterface public function fetchColumn(Query $query) { $column = array(); - foreach ($this->fetchPairs($query) as $value) { - foreach ($value as $value_2) { - $column[] = $value_2; + foreach ($this->fetchPairs($query) as $pair) { + foreach ($pair as $value) { + $column[] = $value; break; } } @@ -139,10 +142,10 @@ class Reader implements DatasourceInterface public function validateLine($line, Query $query) { $data = array(); - $PCRE_result = @preg_match($this->config->fields, $line, $data); - if ($PCRE_result === false) { - throw new \Exception('Failed parsing regular expression!'); - } else if ($PCRE_result === 1) { + $PCREResult = @preg_match($this->config->fields, $line, $data); + if ($PCREResult === false) { + throw new Exception('Failed parsing regular expression!'); + } else if ($PCREResult === 1) { foreach ($query->getFilters() as $filter) { if (strpos($line, $filter) === false) { return false; @@ -167,21 +170,20 @@ class Reader implements DatasourceInterface */ public function read(Query $query) { - $skip_lines = $query->getOffset(); - $read_lines = $query->getLimit(); - if ($skip_lines === null) { - $skip_lines = 0; + $skipLines = $query->getOffset(); + $readLines = $query->getLimit(); + if ($skipLines === null) { + $skipLines = 0; } - return $this->{$query->sortDesc() ? 'readFromEnd' : 'readFromStart'}($skip_lines, $read_lines, $query); + return $this->{$query->sortDesc() ? 'readFromEnd' : 'readFromStart'}($skipLines, $readLines, $query); } /** * Backend for $this->read * Direction: LIFO */ - public function readFromEnd($skip_lines, $read_lines, Query $query) + public function readFromEnd($skipLines, $readLines, Query $query) { - $PHP_EOL_len = strlen(PHP_EOL); $lines = array(); $s = ''; $f = @fopen($this->filename, 'rb'); @@ -191,21 +193,21 @@ class Reader implements DatasourceInterface if (ftell($f) === 0) { return array(); } - while ($read_lines === null || count($lines) < $read_lines) { + while ($readLines === null || count($lines) < $readLines) { $c = $this->fgetc($f, $buffer); if ($c === false) { $l = $this->validateLine($s, $query); - if (!($l === false || $skip_lines)) { + if (!($l === false || $skipLines)) { $lines[] = $l; } break; } $s = $c . $s; if (strpos($s, PHP_EOL) === 0) { - $l = $this->validateLine((string)substr($s, $PHP_EOL_len), $query); + $l = $this->validateLine((string)substr($s, self::$EOLLen), $query); if ($l !== false) { - if ($skip_lines) { - $skip_lines--; + if ($skipLines) { + $skipLines--; } else { $lines[] = $l; } @@ -249,20 +251,19 @@ class Reader implements DatasourceInterface * Backend for $this->read * Direction: FIFO */ - public function readFromStart($skip_lines, $read_lines, Query $query) + public function readFromStart($skipLines, $readLines, Query $query) { - $PHP_EOL_len = strlen(PHP_EOL); $lines = array(); $s = ''; $f = @fopen($this->filename, 'rb'); if ($f !== false) { $buffer = ''; - while ($read_lines === null || count($lines) < $read_lines) { + while ($readLines === null || count($lines) < $readLines) { if (strlen($buffer) === 0) { $buffer = fread($f, 4096); if (strlen($buffer) === 0) { $l = $this->validateLine($s, $query); - if (!($l === false || $skip_lines)) { + if (!($l === false || $skipLines)) { $lines[] = $l; } break; @@ -271,10 +272,10 @@ class Reader implements DatasourceInterface $s .= substr($buffer, 0, 1); $buffer = substr($buffer, 1); if (strpos($s, PHP_EOL) !== false) { - $l = $this->validateLine((string)substr($s, 0, strlen($s) - $PHP_EOL_len), $query); + $l = $this->validateLine((string)substr($s, 0, strlen($s) - self::$EOLLen), $query); if ($l !== false) { - if ($skip_lines) { - $skip_lines--; + if ($skipLines) { + $skipLines--; } else { $lines[] = $l; } @@ -294,7 +295,6 @@ class Reader implements DatasourceInterface * @return int */ public function count(Query $query) { - $PHP_EOL_len = strlen(PHP_EOL); $lines = 0; $s = ''; $f = @fopen($this->filename, 'rb'); @@ -313,7 +313,7 @@ class Reader implements DatasourceInterface $s .= substr($buffer, 0, 1); $buffer = substr($buffer, 1); if (strpos($s, PHP_EOL) !== false) { - if ($this->validateLine((string)substr($s, 0, strlen($s) - $PHP_EOL_len), $query) !== false) { + if ($this->validateLine((string)substr($s, 0, strlen($s) - self::$EOLLen), $query) !== false) { $lines++; } $s = ''; From 091ddbe5529d39b96d373de4ef3e5fea13d144db Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 21 Jul 2014 09:19:44 +0200 Subject: [PATCH 072/184] Partiall revert bfc54b7e Refactor Url->getParams() to return an instance of UrlParam instead of an array. fixes #6760 --- library/Icinga/Web/Url.php | 4 ++-- library/Icinga/Web/UrlParams.php | 2 +- library/Icinga/Web/Widget/Dashboard/Component.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index c9da962be..c705b68d7 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -331,11 +331,11 @@ class Url /** * Return all parameters that will be used in the query part * - * @return array An associative key => value array containing all parameters + * @return UrlParams An instance of UrlParam containing all parameters */ public function getParams() { - return $this->params->asArray(); + return $this->params; } /** diff --git a/library/Icinga/Web/UrlParams.php b/library/Icinga/Web/UrlParams.php index 9a8b60929..11fed8333 100644 --- a/library/Icinga/Web/UrlParams.php +++ b/library/Icinga/Web/UrlParams.php @@ -310,7 +310,7 @@ class UrlParams } } - public function asArray() + public function toArray() { return $this->params; } diff --git a/library/Icinga/Web/Widget/Dashboard/Component.php b/library/Icinga/Web/Widget/Dashboard/Component.php index 8bac34359..2d8517526 100644 --- a/library/Icinga/Web/Widget/Dashboard/Component.php +++ b/library/Icinga/Web/Widget/Dashboard/Component.php @@ -126,7 +126,7 @@ EOD; public function toArray() { $array = array('url' => $this->url->getPath()); - foreach ($this->url->getParams() as $param) { + foreach ($this->url->getParams()->toArray() as $param) { $array[$param[0]] = $param[1]; } return $array; From 53c9292b41e943472b7d82b8c144d871ad2b6dca Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 21 Jul 2014 13:06:35 +0200 Subject: [PATCH 073/184] Fix BarChart padding in Dashboard Use max-width instead of width in the chart layout to make better use of the available space, but prevent the charts from growing too big. Use a bigger weight and height in the default dashboard configuration. fixes #6744 --- config/dashboard/dashboard.ini | 16 ++++++++++++---- .../application/controllers/ChartController.php | 8 ++++---- .../views/scripts/chart/hostgroup.phtml | 2 +- .../views/scripts/chart/servicegroup.phtml | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/config/dashboard/dashboard.ini b/config/dashboard/dashboard.ini index 50e5a6373..8d500f341 100644 --- a/config/dashboard/dashboard.ini +++ b/config/dashboard/dashboard.ini @@ -23,13 +23,21 @@ sort = host_severity title = "Landing page" [Landing.Hostgroups] -url = "monitoring/chart/hostgroup?height=400&width=500" +url = "monitoring/chart/hostgroup" +height = 540 +width = 540 [Landing.Servicegroups] -url = "monitoring/chart/servicegroup?height=360&width=450" +url = "monitoring/chart/servicegroup" +height = 540 +width = 540 [Landing.Unhandled Problem Services] -url = "monitoring/list/services?service_handled=0&service_problem=1" +url = "monitoring/list/services" +service_handled = 0 +service_problem = 1 [Landing.Unhandled Problem Hosts] -url = "monitoring/list/hosts?host_handled=0&host_problem=1" +url = "monitoring/list/hosts" +host_handled = 0 +host_problem = 1 diff --git a/modules/monitoring/application/controllers/ChartController.php b/modules/monitoring/application/controllers/ChartController.php index 4ccc30510..3cfb4ef71 100644 --- a/modules/monitoring/application/controllers/ChartController.php +++ b/modules/monitoring/application/controllers/ChartController.php @@ -94,8 +94,8 @@ class Monitoring_ChartController extends Controller 'services_pending' ) )->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 220)); - $this->view->width = intval($this->getParam('width', 520)); + $this->view->height = intval($this->getParam('height', 500)); + $this->view->width = intval($this->getParam('width', 500)); if (count($query) === 1) { $this->drawGroupPie($query[0]); } else { @@ -119,8 +119,8 @@ class Monitoring_ChartController extends Controller 'services_pending' ) )->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 220)); - $this->view->width = intval($this->getParam('width', 520)); + $this->view->height = intval($this->getParam('height', 500)); + $this->view->width = intval($this->getParam('width', 500)); $this->drawServiceGroupChart($query); diff --git a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml index 4ad5f61dc..2bdbe428c 100644 --- a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml +++ b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml @@ -1,5 +1,5 @@ -
      +
      render(); ?> diff --git a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml index ad8a21ab0..2bdbe428c 100644 --- a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml +++ b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml @@ -1,5 +1,5 @@ -
      +
      render(); ?> From da784456868b35bc0d1302f92852c8d92af0ad1e Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 21 Jul 2014 13:38:19 +0200 Subject: [PATCH 074/184] Add apache 2.4 example for 'Require all granted' fixes #6771 --- etc/apache/icingaweb.conf.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etc/apache/icingaweb.conf.in b/etc/apache/icingaweb.conf.in index 69335368e..0451abf5b 100644 --- a/etc/apache/icingaweb.conf.in +++ b/etc/apache/icingaweb.conf.in @@ -5,6 +5,8 @@ Alias @web_path@ "@prefix@/public" AllowOverride None Order allow,deny Allow from all + # new directive needed in Apache 2.4.3+ + #Require all granted SetEnv ICINGAWEB_CONFIGDIR @icingaweb_config_path@ From 6df7be7ee55a99c1ee91dbf72499925d89c94c89 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Mon, 21 Jul 2014 14:09:40 +0200 Subject: [PATCH 075/184] File/Query: Fix `applyFilter' and `order' not matching SimpleQuery's interfaces refs #6722 --- library/Icinga/Protocol/File/Query.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Protocol/File/Query.php b/library/Icinga/Protocol/File/Query.php index d8806b2bc..f88426bca 100644 --- a/library/Icinga/Protocol/File/Query.php +++ b/library/Icinga/Protocol/File/Query.php @@ -5,6 +5,7 @@ namespace Icinga\Protocol\File; use Icinga\Data\SimpleQuery; +use Icinga\Data\Filter\Filter; /** * Class Query @@ -32,7 +33,7 @@ class Query extends SimpleQuery /** * Nothing to do here */ - public function applyFilter() + public function applyFilter(Filter $filter) {} /** @@ -42,9 +43,11 @@ class Query extends SimpleQuery * * @return Query */ - public function order($dir) + public function order($field, $direction = null) { - $this->sortDir = ($dir === null || strtoupper(trim($dir)) === 'DESC') ? self::SORT_DESC : self::SORT_ASC; + $this->sortDir = ( + $direction === null || strtoupper(trim($direction)) === 'DESC' + ) ? self::SORT_DESC : self::SORT_ASC; return $this; } @@ -80,4 +83,4 @@ class Query extends SimpleQuery { return $this->filters; } -} \ No newline at end of file +} From c5ecbf250d8e91b2e36fa33fede1bc7cb3244b9c Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Mon, 21 Jul 2014 14:11:08 +0200 Subject: [PATCH 076/184] lib: add FileReaderException for file reader specific errors refs #6722 --- .../Protocol/File/Exception/FileReaderException.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 library/Icinga/Protocol/File/Exception/FileReaderException.php diff --git a/library/Icinga/Protocol/File/Exception/FileReaderException.php b/library/Icinga/Protocol/File/Exception/FileReaderException.php new file mode 100644 index 000000000..3e0890a57 --- /dev/null +++ b/library/Icinga/Protocol/File/Exception/FileReaderException.php @@ -0,0 +1,10 @@ + Date: Mon, 21 Jul 2014 14:14:13 +0200 Subject: [PATCH 077/184] Fix "Fatal error: Interface 'Icinga\Data\DatasourceInterface' not found in Icinga\Protocol\File\Reader" fixes #6722 --- library/Icinga/Protocol/File/Reader.php | 362 +++++++++--------------- 1 file changed, 130 insertions(+), 232 deletions(-) diff --git a/library/Icinga/Protocol/File/Reader.php b/library/Icinga/Protocol/File/Reader.php index fcefdfc06..50842bc4a 100644 --- a/library/Icinga/Protocol/File/Reader.php +++ b/library/Icinga/Protocol/File/Reader.php @@ -4,44 +4,92 @@ namespace Icinga\Protocol\File; -use Exception; -use Icinga\Data\DatasourceInterface; +use FilterIterator; +use Iterator; +use Zend_Config; +use Icinga\Protocol\File\FileReaderException; +use Icinga\Util\File; /** - * Class Reader - * * Read file line by line - * - * @package Icinga\Protocol\File */ -class Reader implements DatasourceInterface +class Reader extends FilterIterator { - private static $EOLLen = null; - /** - * Name of the file to read + * A PCRE string with the fields to extract from the file's lines as named subpatterns * * @var string */ - private $filename; + protected $fields; /** - * Configuration for this Datasource + * An associative array of the current line's fields ($field => $value) * - * @var \Zend_Config + * @var array */ - private $config; + protected $currentData; /** - * @param \Zend_Config $config + * Create a new reader + * + * @param Zend_Config $config + * + * @throws FileReaderException If a required $config directive (filename or fields) is missing */ - public function __construct($config) + public function __construct(Zend_Config $config) { - if (self::$EOLLen === null) { - self::$EOLLen = strlen(PHP_EOL); + foreach (array('filename', 'fields') as $key) { + if (! isset($config->{$key})) { + throw new FileReaderException('The directive `' . $key . '\' is required'); + } } - $this->config = $config; - $this->filename = $config->filename; + $this->fields = $config->fields; + $f = new File($config->filename); + $f->setFlags( + File::DROP_NEW_LINE | + File::READ_AHEAD | + File::SKIP_EMPTY + ); + parent::__construct($f); + } + + /** + * Return the current data + * + * @return array + */ + public function current() + { + return $this->currentData; + } + + /** + * Accept lines matching the given PCRE pattern + * + * @return bool + * + * @throws FileReaderException If PHP failed parsing the PCRE pattern + */ + public function accept() + { + $data = array(); + $matched = @preg_match( + $this->fields, + $this->getInnerIterator()->current(), + $data + ); + if ($matched === false) { + throw new FileReaderException('Failed parsing regular expression!'); + } else if ($matched === 1) { + foreach ($data as $key) { + if (is_int($key)) { + unset($data[$key]); + } + } + $this->currentData = $data; + return true; + } + return false; } /** @@ -54,10 +102,22 @@ class Reader implements DatasourceInterface return new Query($this); } + /** + * Return the number of available valid lines. + * + * @return int + */ + public function count() + { + return iterator_count($this); + } + /** * Fetch result as an array of objects * - * @return array + * @param Query $query + * + * @return array */ public function fetchAll(Query $query) { @@ -68,10 +128,52 @@ class Reader implements DatasourceInterface return $all; } + /** + * Fetch result as a key/value pair array + * + * @param Query $query + * + * @return array + */ + public function fetchPairs(Query $query) + { + $skipLines = $query->getOffset(); + $readLines = $query->getLimit(); + if ($skipLines === null) { + $skipLines = 0; + } + $lines = array(); + if ($query->sortDesc()) { + $count = $this->count($query); + if ($count <= $skipLines) { + return $lines; + } else if ($count < ($skipLines + $readLines)) { + $readLines = $count - $skipLines; + $skipLines = 0; + } else { + $skipLines = $count - ($skipLines + $readLines); + } + } + foreach ($this as $index => $line) { + if ($index >= $skipLines) { + if ($index >= $skipLines + $readLines) { + break; + } + $lines[] = $line; + } + } + if ($query->sortDesc()) { + $lines = array_reverse($lines); + } + return $lines; + } + /** * Fetch first result row * - * @return object + * @param Query $query + * + * @return object */ public function fetchRow(Query $query) { @@ -85,7 +187,9 @@ class Reader implements DatasourceInterface /** * Fetch first result column * - * @return array + * @param Query $query + * + * @return array */ public function fetchColumn(Query $query) { @@ -102,7 +206,9 @@ class Reader implements DatasourceInterface /** * Fetch first column value from first result row * - * @return mixed + * @param Query $query + * + * @return mixed */ public function fetchOne(Query $query) { @@ -114,212 +220,4 @@ class Reader implements DatasourceInterface } return null; } - - /** - * Fetch result as a key/value pair array - * - * @return array - */ - public function fetchPairs(Query $query) - { - return $this->read($query); - } - - /** - * If given $line matches the $query's PCRE pattern and contains all the strings in the $query's filters array, - * return an associative array of the matches of the PCRE pattern. - * Otherwise, return false. - * If preg_match returns false, it failed parsing the PCRE pattern. - * In that case, throw an exception. - * - * @param string $line - * @param Query $query - * - * @return array|bool - * - * @throws \Exception - */ - public function validateLine($line, Query $query) - { - $data = array(); - $PCREResult = @preg_match($this->config->fields, $line, $data); - if ($PCREResult === false) { - throw new Exception('Failed parsing regular expression!'); - } else if ($PCREResult === 1) { - foreach ($query->getFilters() as $filter) { - if (strpos($line, $filter) === false) { - return false; - } - } - foreach ($data as $key => $value) { - if (is_int($key)) { - unset($data[$key]); - } - } - return $data; - } - return false; - } - - /** - * Skip and read as many lines as needed according to given $query. - * - * @param Query $query - * - * @return array result - */ - public function read(Query $query) - { - $skipLines = $query->getOffset(); - $readLines = $query->getLimit(); - if ($skipLines === null) { - $skipLines = 0; - } - return $this->{$query->sortDesc() ? 'readFromEnd' : 'readFromStart'}($skipLines, $readLines, $query); - } - - /** - * Backend for $this->read - * Direction: LIFO - */ - public function readFromEnd($skipLines, $readLines, Query $query) - { - $lines = array(); - $s = ''; - $f = @fopen($this->filename, 'rb'); - if ($f !== false) { - $buffer = ''; - fseek($f, 0, SEEK_END); - if (ftell($f) === 0) { - return array(); - } - while ($readLines === null || count($lines) < $readLines) { - $c = $this->fgetc($f, $buffer); - if ($c === false) { - $l = $this->validateLine($s, $query); - if (!($l === false || $skipLines)) { - $lines[] = $l; - } - break; - } - $s = $c . $s; - if (strpos($s, PHP_EOL) === 0) { - $l = $this->validateLine((string)substr($s, self::$EOLLen), $query); - if ($l !== false) { - if ($skipLines) { - $skipLines--; - } else { - $lines[] = $l; - } - } - $s = ''; - } - } - } - return $lines; - } - - /** - * Backend for $this->readFromEnd - */ - public function fgetc($file, &$buffer) - { - $strlen = strlen($buffer); - if ($strlen === 0) { - $pos = ftell($file); - if ($pos === 0) { - return false; - } - if ($pos < 4096) { - fseek($file, 0); - $buffer = fread($file, $pos); - fseek($file, 0); - } else { - fseek($file, -4096, SEEK_CUR); - $buffer = fread($file, 4096); - fseek($file, -4096, SEEK_CUR); - } - return $this->fgetc($file, $buffer); - } else { - $char = substr($buffer, -1); - $buffer = substr($buffer, 0, $strlen - 1); - return $char; - } - } - - /** - * Backend for $this->read - * Direction: FIFO - */ - public function readFromStart($skipLines, $readLines, Query $query) - { - $lines = array(); - $s = ''; - $f = @fopen($this->filename, 'rb'); - if ($f !== false) { - $buffer = ''; - while ($readLines === null || count($lines) < $readLines) { - if (strlen($buffer) === 0) { - $buffer = fread($f, 4096); - if (strlen($buffer) === 0) { - $l = $this->validateLine($s, $query); - if (!($l === false || $skipLines)) { - $lines[] = $l; - } - break; - } - } - $s .= substr($buffer, 0, 1); - $buffer = substr($buffer, 1); - if (strpos($s, PHP_EOL) !== false) { - $l = $this->validateLine((string)substr($s, 0, strlen($s) - self::$EOLLen), $query); - if ($l !== false) { - if ($skipLines) { - $skipLines--; - } else { - $lines[] = $l; - } - } - $s = ''; - } - } - } - return $lines; - } - - /** - * Return the number of available valid lines. - * - * @param Query $query - * - * @return int - */ - public function count(Query $query) { - $lines = 0; - $s = ''; - $f = @fopen($this->filename, 'rb'); - if ($f !== false) { - $buffer = ''; - while (true) { - if (strlen($buffer) === 0) { - $buffer = fread($f, 4096); - if (strlen($buffer) === 0) { - if ($this->validateLine($s, $query) !== false) { - $lines++; - } - break; - } - } - $s .= substr($buffer, 0, 1); - $buffer = substr($buffer, 1); - if (strpos($s, PHP_EOL) !== false) { - if ($this->validateLine((string)substr($s, 0, strlen($s) - self::$EOLLen), $query) !== false) { - $lines++; - } - $s = ''; - } - } - } - return $lines; - } } From 0a500efd8a3375ac4c02a80a181cc6bc53a25e11 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 21 Jul 2014 17:17:09 +0200 Subject: [PATCH 078/184] Determine the max value in LinearUnit dynamically The range between min and max should always be divisable by the amount of ticks, to ensure that the vertical lines are always at a full discrete value. fixes #6769 --- library/Icinga/Chart/Unit/LinearUnit.php | 43 +++++++++++++++++-- .../controllers/ChartController.php | 7 ++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Chart/Unit/LinearUnit.php b/library/Icinga/Chart/Unit/LinearUnit.php index bf27486d2..d776fe304 100644 --- a/library/Icinga/Chart/Unit/LinearUnit.php +++ b/library/Icinga/Chart/Unit/LinearUnit.php @@ -90,17 +90,52 @@ class LinearUnit implements AxisUnit } sort($datapoints); if (!$this->staticMax) { - $this->max = max($this->max, $datapoints[count($datapoints)-1]); + $this->max = max($this->max, $datapoints[count($datapoints) - 1]); } if (!$this->staticMin) { $this->min = min($this->min, $datapoints[0]); } - + if (!$this->staticMin || !$this->staticMax) { + $this->updateMaxValue(); + } $this->currentTick = 0; $this->currentValue = $this->min; return $this; } + /** + * Refresh the range depending on the current values of min, max and nrOfTicks + */ + private function updateMaxValue() + { + $this->max = $this->calculateTickRange($this->max - $this->min, $this->nrOfTicks) * + $this->nrOfTicks + $this->min; + } + + /** + * Determine the minimum tick range that is necessary to display the given value range + * correctly + * + * @param int range The range to display + * @param int ticks The amount of ticks to use + * + * @return int The value for each tick + */ + private function calculateTickRange($range, $ticks) + { + $factor = 1; + $steps = array(1, 2, 5); + $step = 0; + while ($range / ($factor * $steps[$step]) > $ticks) { + $step++; + if ($step === count($steps)) { + $step = 0; + $factor *= 10; + } + } + return $steps[$step] * $factor; + } + /** * Transform the absolute value to an axis relative value * @@ -114,7 +149,7 @@ class LinearUnit implements AxisUnit } elseif ($value > $this->max) { return 100; } else { - return 100 * ($value - $this->min) / ($this->max - $this->min); + return 100 * ($value - $this->min) / $this->max - $this->min; } } @@ -176,6 +211,7 @@ class LinearUnit implements AxisUnit if ($max !== null) { $this->max = $max; $this->staticMax = true; + $this->updateMaxValue(); } } @@ -189,6 +225,7 @@ class LinearUnit implements AxisUnit if ($min !== null) { $this->min = $min; $this->staticMin = true; + $this->updateMaxValue(); } } diff --git a/modules/monitoring/application/controllers/ChartController.php b/modules/monitoring/application/controllers/ChartController.php index 3cfb4ef71..31bea79af 100644 --- a/modules/monitoring/application/controllers/ChartController.php +++ b/modules/monitoring/application/controllers/ChartController.php @@ -140,7 +140,8 @@ class Monitoring_ChartController extends Controller } $this->view->chart = new GridChart(); $this->view->chart->setAxisLabel('', t('Services')) - ->setXAxis(new \Icinga\Chart\Unit\StaticAxis()); + ->setXAxis(new \Icinga\Chart\Unit\StaticAxis()) + ->setAxisMin(null, 0); $this->view->chart->drawBars( array( @@ -190,7 +191,9 @@ class Monitoring_ChartController extends Controller ); } $this->view->chart = new GridChart(); - $this->view->chart->setAxisLabel('', t('Hosts'))->setXAxis(new StaticAxis()); + $this->view->chart->setAxisLabel('', t('Hosts')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0); $this->view->chart->drawBars( array( 'label' => t('Up'), From 4b8bb99fa1afcc69b14c526f0abcdfc502fc51e1 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 22 Jul 2014 11:26:35 +0200 Subject: [PATCH 079/184] Fix svg text rotation in firefox Use the SVG transform attribute instead of the writing-mode attribute to support firefox. --- library/Icinga/Chart/Axis.php | 37 ++--------- library/Icinga/Chart/Primitive/Text.php | 10 --- library/Icinga/Chart/Render/Rotator.php | 86 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 42 deletions(-) create mode 100644 library/Icinga/Chart/Render/Rotator.php diff --git a/library/Icinga/Chart/Axis.php b/library/Icinga/Chart/Axis.php index db1a82a83..00a5f5679 100644 --- a/library/Icinga/Chart/Axis.php +++ b/library/Icinga/Chart/Axis.php @@ -9,6 +9,7 @@ use Icinga\Chart\Primitive\Drawable; use Icinga\Chart\Primitive\Line; use Icinga\Chart\Primitive\Text; use Icinga\Chart\Render\RenderContext; +use Icinga\Chart\Render\Rotator; use Icinga\Chart\Unit\AxisUnit; use Icinga\Chart\Unit\CalendarUnit; use Icinga\Chart\Unit\LinearUnit; @@ -188,11 +189,11 @@ class Axis implements Drawable $labelField->setFontSize('2.5em'); } + if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) { + $labelField = new Rotator($labelField, 45); + } $labelField = $labelField->toSvg($ctx); - if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) { - $labelField = $this->rotate($ctx, $labelField, 45); - } $group->appendChild($labelField); if ($this->drawYGrid) { @@ -214,34 +215,6 @@ class Axis implements Drawable } } - /** - * Rotate the given element. - * - * @param RenderContext $ctx The rendering context - * @param DOMElement $el The element to rotate - * @param $degrees The rotation degrees - * - * @return DOMElement - */ - private function rotate(RenderContext $ctx, DOMElement $el, $degrees) - { - // Create a box containing the rotated element relative to the original text position - $container = $ctx->getDocument()->createElement('g'); - $x = $el->getAttribute('x'); - $y = $el->getAttribute('y'); - $container->setAttribute('transform', 'translate(' . $x . ',' . $y . ')'); - $el->removeAttribute('x'); - $el->removeAttribute('y'); - - // Create a rotated box containing the text - $rotate = $ctx->getDocument()->createElement('g'); - $rotate->setAttribute('transform', 'rotate(' . $degrees . ')'); - $rotate->appendChild($el); - - $container->appendChild($rotate); - return $container; - } - /** * Render the vertical axis * @@ -275,9 +248,9 @@ class Axis implements Drawable if ($this->yLabel) { $axisLabel = new Text(-8, 50, $this->yLabel); $axisLabel->setFontSize('2em') - ->setAdditionalStyle(Text::ORIENTATION_VERTICAL) ->setFontWeight('bold') ->setAlignment(Text::ALIGN_MIDDLE); + $axisLabel = new Rotator($axisLabel, 90); $group->appendChild($axisLabel->toSvg($ctx)); } diff --git a/library/Icinga/Chart/Primitive/Text.php b/library/Icinga/Chart/Primitive/Text.php index 5521d6e27..72bed552b 100644 --- a/library/Icinga/Chart/Primitive/Text.php +++ b/library/Icinga/Chart/Primitive/Text.php @@ -30,16 +30,6 @@ class Text extends Styleable implements Drawable */ const ALIGN_MIDDLE = 'middle'; - /** - * Normal left to right orientation - */ - const ORIENTATION_HORIZONTAL = ""; - - /** - * Top down orientation (rotated by 90°) - */ - const ORIENTATION_VERTICAL = "writing-mode: tb;"; - /** * The x position of the Text * diff --git a/library/Icinga/Chart/Render/Rotator.php b/library/Icinga/Chart/Render/Rotator.php new file mode 100644 index 000000000..934b784b6 --- /dev/null +++ b/library/Icinga/Chart/Render/Rotator.php @@ -0,0 +1,86 @@ +element = $element; + $this->degrees = $degrees; + } + + /** + * Rotate the given element. + * + * @param RenderContext $ctx The rendering context + * @param DOMElement $el The element to rotate + * @param $degrees The amount of degrees + * + * @return DOMElement The rotated DOMElement + */ + private function rotate(RenderContext $ctx, DOMElement $el, $degrees) + { + // Create a box containing the rotated element relative to the original element position + $container = $ctx->getDocument()->createElement('g'); + $x = $el->getAttribute('x'); + $y = $el->getAttribute('y'); + $container->setAttribute('transform', 'translate(' . $x . ',' . $y . ')'); + $el->removeAttribute('x'); + $el->removeAttribute('y'); + + // Put the element into a rotated group + //$rotate = $ctx->getDocument()->createElement('g'); + $el->setAttribute('transform', 'rotate(' . $degrees . ')'); + //$rotate->appendChild($el); + + $container->appendChild($el); + return $container; + } + + /** + * Create the SVG representation from this Drawable + * + * @param RenderContext $ctx The context to use for rendering + * + * @return DOMElement The SVG Element + */ + public function toSvg(RenderContext $ctx) + { + $el = $this->element->toSvg($ctx); + return $this->rotate($ctx, $el, $this->degrees); + } +} \ No newline at end of file From 0eaaaf2f29617337cf49cdae304ff41ff05d9e8e Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 22 Jul 2014 11:32:52 +0200 Subject: [PATCH 080/184] Make bar charts thicker --- library/Icinga/Chart/Graph/BarGraph.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Chart/Graph/BarGraph.php b/library/Icinga/Chart/Graph/BarGraph.php index 8bde864ce..968e4c0f7 100644 --- a/library/Icinga/Chart/Graph/BarGraph.php +++ b/library/Icinga/Chart/Graph/BarGraph.php @@ -69,7 +69,7 @@ class BarGraph extends Styleable implements Drawable $group = $doc->createElement('g'); $idx = 0; foreach ($this->dataSet as $point) { - $rect = new Rect($point[0] - 1, $point[1], 2, 100 - $point[1]); + $rect = new Rect($point[0] - 2, $point[1], 4, 100 - $point[1]); $rect->setFill($this->fill); $rect->setStrokeWidth($this->strokeWidth); $rect->setStrokeColor('black'); From cb27a3d0f1a2749ff604495e0c1830ae5267402f Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 22 Jul 2014 17:57:40 +0200 Subject: [PATCH 081/184] Do not use height and width in bar charts, to avoid layout errors --- config/dashboard/dashboard.ini | 4 ---- .../application/views/scripts/chart/hostgroup.phtml | 2 +- .../application/views/scripts/chart/servicegroup.phtml | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/config/dashboard/dashboard.ini b/config/dashboard/dashboard.ini index 8d500f341..022b34be5 100644 --- a/config/dashboard/dashboard.ini +++ b/config/dashboard/dashboard.ini @@ -24,13 +24,9 @@ title = "Landing page" [Landing.Hostgroups] url = "monitoring/chart/hostgroup" -height = 540 -width = 540 [Landing.Servicegroups] url = "monitoring/chart/servicegroup" -height = 540 -width = 540 [Landing.Unhandled Problem Services] url = "monitoring/list/services" diff --git a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml index 2bdbe428c..1f98bb8db 100644 --- a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml +++ b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml @@ -1,5 +1,5 @@ -
      +
      render(); ?> diff --git a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml index 2bdbe428c..1f98bb8db 100644 --- a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml +++ b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml @@ -1,5 +1,5 @@ -
      +
      render(); ?> From 9af89e6ccfc32b54d643dc308f0c3a9e32eb1129 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Wed, 23 Jul 2014 11:28:45 +0200 Subject: [PATCH 082/184] doc module: style h[1-6], table and code Headers have different sizes Tables have borders Table headers are left-aligned Codes are displayed inline (except inside pre) and have scrollbars if necessary refs #6484 fixes #6632 --- .../views/scripts/icingaweb/chapter.phtml | 2 ++ .../views/scripts/module/chapter.phtml | 2 ++ modules/doc/public/css/module.less | 36 +++++++++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/modules/doc/application/views/scripts/icingaweb/chapter.phtml b/modules/doc/application/views/scripts/icingaweb/chapter.phtml index 328d0bebe..2ab744ebd 100644 --- a/modules/doc/application/views/scripts/icingaweb/chapter.phtml +++ b/modules/doc/application/views/scripts/icingaweb/chapter.phtml @@ -2,6 +2,7 @@ $urlHelper = $this->getHelper('Url'); $view = $this; ?> +
      [^>]*?\s+)?href="#(?P[^"]+)"/im', function($match) use ($toc, $urlHelper, $view) { @@ -36,3 +37,4 @@ $view = $this; }, $chapterHtml ); ?> +
      diff --git a/modules/doc/application/views/scripts/module/chapter.phtml b/modules/doc/application/views/scripts/module/chapter.phtml index 59a6ecb33..3a809209f 100644 --- a/modules/doc/application/views/scripts/module/chapter.phtml +++ b/modules/doc/application/views/scripts/module/chapter.phtml @@ -2,6 +2,7 @@ $urlHelper = $this->getHelper('Url'); $view = $this; ?> +
      [^>]*?\s+)?href="#(?P[^"]+)"/im', function($match) use ($toc, $moduleName, $urlHelper, $view) { @@ -36,3 +37,4 @@ $view = $this; }, $chapterHtml ); ?> +
      diff --git a/modules/doc/public/css/module.less b/modules/doc/public/css/module.less index 45d97e5c6..52fff5c32 100644 --- a/modules/doc/public/css/module.less +++ b/modules/doc/public/css/module.less @@ -1,9 +1,33 @@ -.toc { - float: left; - width: 33.333% +// W3C Recommendation (except h4) +h1 { font-size: 2em !important; } +h2 { font-size: 1.5em !important; } +h3 { font-size: 1.17em !important; } +h4 { font-size: 1em !important; } +h5 { font-size: .83em !important; } +h6 { font-size: .75em !important; } + +div.chapter { + padding-left: 5px; } -.doc { - float: left; - width: 66.667% +table th { + text-align: left; +} + +table th, +table td { + border: solid 1px lightgray; + padding-left: 5px; + padding-right: 5px; +} + +code { + width: 100%; + overflow-x: auto; + padding: 0.2em; + display: inline; +} + +pre > code { + display: inline-block; } From 2ff43abcf5860f4002d4fbb2aee930b22ab93ea6 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Wed, 23 Jul 2014 12:12:05 +0200 Subject: [PATCH 083/184] Do not use htmlspecialchars in widgets refs #6759 --- library/Icinga/Web/Widget/AbstractWidget.php | 2 +- library/Icinga/Web/Widget/AlertMessageBox.php | 2 +- library/Icinga/Web/Widget/Chart/InlinePie.php | 16 ++++++++-------- library/Icinga/Web/Widget/Tabs.php | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Web/Widget/AbstractWidget.php b/library/Icinga/Web/Widget/AbstractWidget.php index 37fb7ad36..962bcc210 100644 --- a/library/Icinga/Web/Widget/AbstractWidget.php +++ b/library/Icinga/Web/Widget/AbstractWidget.php @@ -118,7 +118,7 @@ abstract class AbstractWidget try { $html = $this->render($this->view()); } catch (Exception $e) { - return htmlspecialchars($e->getMessage()); + return $this->escape($e->getMessage()); } return (string) $html; } diff --git a/library/Icinga/Web/Widget/AlertMessageBox.php b/library/Icinga/Web/Widget/AlertMessageBox.php index 09016f490..69113912e 100644 --- a/library/Icinga/Web/Widget/AlertMessageBox.php +++ b/library/Icinga/Web/Widget/AlertMessageBox.php @@ -124,7 +124,7 @@ class AlertMessageBox extends AbstractWidget $alert = $this->states[$level]; $html .= '
      ' . $this->view()->icon($alert['icon']) . - '' . htmlspecialchars($message->getMessage()) . '' . + '' . $this->escape($message->getMessage()) . '' . '
      '; } return $html; diff --git a/library/Icinga/Web/Widget/Chart/InlinePie.php b/library/Icinga/Web/Widget/Chart/InlinePie.php index e4621ba09..8a36dbf01 100644 --- a/library/Icinga/Web/Widget/Chart/InlinePie.php +++ b/library/Icinga/Web/Widget/Chart/InlinePie.php @@ -24,7 +24,7 @@ class InlinePie extends AbstractWidget const NUMBER_FORMAT_TIME = 'time'; const NUMBER_FORMAT_BYTES = 'bytes'; const NUMBER_FORMAT_RATIO = 'ratio'; - + /** * The template string used for rendering this widget * The template string used for rendering this widget @@ -137,7 +137,7 @@ EOD; * @var string */ private $tooltipFormat = '{{title}}
      {{label}}: {{formatted}} ({{percent}}%)'; - + /** * The number format used to render numeric values in tooltips * @@ -212,7 +212,7 @@ EOD; { $this->format = $format; } - + /** * A format string used to render the content of the piechart tooltips * @@ -346,11 +346,11 @@ EOD; { $template = $this->template; $template = str_replace('{url}', $this->url, $template); - + // style $template = str_replace('{width}', $this->width, $template); $template = str_replace('{height}', $this->height, $template); - $template = str_replace('{title}', htmlspecialchars($this->title), $template); + $template = str_replace('{title}', $this->escape($this->title), $template); $template = str_replace('{style}', $this->style, $template); $template = str_replace('{colors}', implode(',', $this->colors), $template); $template = str_replace('{borderWidth}', $this->borderWidth, $template); @@ -370,9 +370,9 @@ EOD; foreach ($this->data as $key => $value) { $formatted[$key] = $this->formatValue($value); } - $template = str_replace('{data}', htmlspecialchars(implode(',', $data)), $template); - $template = str_replace('{formatted}', htmlspecialchars(implode('|', $formatted)), $template); - $template = str_replace('{labels}', htmlspecialchars($this->createLabelString()), $template); + $template = str_replace('{data}', $this->escape(implode(',', $data)), $template); + $template = str_replace('{formatted}', $this->escape(implode('|', $formatted)), $template); + $template = str_replace('{labels}', $this->escape($this->createLabelString()), $template); $template = str_replace('{tooltipFormat}', $this->tooltipFormat, $template); return $template; } diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index cd480ba32..25f88533b 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -259,7 +259,7 @@ EOT; try { $html = $this->render(Icinga::app()->getViewRenderer()->view); } catch (Exception $e) { - return htmlspecialchars($e->getMessage()); + return $this->escape($e->getMessage()); } return $html; } From 607aa9b203377126b399667cd0b10c80d75e04b7 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Wed, 23 Jul 2014 12:12:49 +0200 Subject: [PATCH 084/184] Do not use htmlspecialchars in view scripts fixes #6759 --- .../application/views/helpers/Perfdata.php | 6 +++--- .../views/scripts/list/contacts.phtml | 8 ++++---- .../application/views/scripts/show/contact.phtml | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php index f4cffaea7..193048334 100644 --- a/modules/monitoring/application/views/helpers/Perfdata.php +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -36,9 +36,9 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract } $pieChart->setStyle('margin: 0.2em 0.5em 0.2em 0.5em;'); $table[] = '' . $pieChart->render() - . htmlspecialchars($perfdata->getLabel()) + . $this->escape($perfdata->getLabel()) . ' ' - . htmlspecialchars($this->formatPerfdataValue($perfdata)) . + . $this->escape($this->formatPerfdataValue($perfdata)) . ' '; } } @@ -89,7 +89,7 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract protected function createInlinePie(Perfdata $perfdata) { $pieChart = new InlinePie($this->calculatePieChartData($perfdata), $perfdata->getLabel()); - $pieChart->setLabel(htmlspecialchars($perfdata->getLabel())); + $pieChart->setLabel($this->escape($perfdata->getLabel())); $pieChart->setHideEmptyLabel(); //$pieChart->setHeight(32)->setWidth(32); diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml index 81a061973..9fc05de8e 100644 --- a/modules/monitoring/application/views/scripts/list/contacts.phtml +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -26,23 +26,23 @@ $contactHelper = $this->getHelper('ContactFlags');
      %2$s', t('Email'), - htmlspecialchars($contact->contact_email) + $this->escape($contact->contact_email) ) ?>
      contact_pager): ?>
      : - contact_pager) ?> + escape($contact->contact_pager) ?>
      : - contact_notify_service_timeperiod) ?> + escape($contact->contact_notify_service_timeperiod) ?>
      : - contact_notify_host_timeperiod) ?> + escape($contact->contact_notify_host_timeperiod) ?>
      diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml index 609bbbc69..a7b691eb4 100644 --- a/modules/monitoring/application/views/scripts/show/contact.phtml +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -7,8 +7,8 @@ $contactHelper = $this->getHelper('ContactFlags'); - contact_name) ?> (contact_alias) + escape($contact->contact_name) ?> (escape($contact->contact_alias) ?>) @@ -18,30 +18,30 @@ $contactHelper = $this->getHelper('ContactFlags'); %1$s', - htmlspecialchars($contact->contact_email) + $this->escape($contact->contact_email) ); ?> contact_pager): ?> - contact_pager) ?> + escape($contact->contact_pager) ?> - contactFlags($contact, 'service')) ?> + escape($contactHelper->contactFlags($contact, 'service')) ?> - contactFlags($contact, 'host')) ?> + escape($contactHelper->contactFlags($contact, 'host')) ?> - contact_notify_service_timeperiod) ?> + escape($contact->contact_notify_service_timeperiod) ?> - contact_notify_host_timeperiod) ?> + escape($contact->contact_notify_host_timeperiod) ?> From a68ccecb28bf5907cada08a2261176f628e20fbc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 23 Jul 2014 12:28:04 +0200 Subject: [PATCH 085/184] Revert "Do not use htmlspecialchars in widgets" This reverts commit 2ff43abcf5860f4002d4fbb2aee930b22ab93ea6. `Widget::escape()' does not exist. But `Widget::view()::espcae()' does. --- library/Icinga/Web/Widget/AbstractWidget.php | 2 +- library/Icinga/Web/Widget/AlertMessageBox.php | 2 +- library/Icinga/Web/Widget/Chart/InlinePie.php | 16 ++++++++-------- library/Icinga/Web/Widget/Tabs.php | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Web/Widget/AbstractWidget.php b/library/Icinga/Web/Widget/AbstractWidget.php index 962bcc210..37fb7ad36 100644 --- a/library/Icinga/Web/Widget/AbstractWidget.php +++ b/library/Icinga/Web/Widget/AbstractWidget.php @@ -118,7 +118,7 @@ abstract class AbstractWidget try { $html = $this->render($this->view()); } catch (Exception $e) { - return $this->escape($e->getMessage()); + return htmlspecialchars($e->getMessage()); } return (string) $html; } diff --git a/library/Icinga/Web/Widget/AlertMessageBox.php b/library/Icinga/Web/Widget/AlertMessageBox.php index 69113912e..09016f490 100644 --- a/library/Icinga/Web/Widget/AlertMessageBox.php +++ b/library/Icinga/Web/Widget/AlertMessageBox.php @@ -124,7 +124,7 @@ class AlertMessageBox extends AbstractWidget $alert = $this->states[$level]; $html .= '
      ' . $this->view()->icon($alert['icon']) . - '' . $this->escape($message->getMessage()) . '' . + '' . htmlspecialchars($message->getMessage()) . '' . '
      '; } return $html; diff --git a/library/Icinga/Web/Widget/Chart/InlinePie.php b/library/Icinga/Web/Widget/Chart/InlinePie.php index 8a36dbf01..e4621ba09 100644 --- a/library/Icinga/Web/Widget/Chart/InlinePie.php +++ b/library/Icinga/Web/Widget/Chart/InlinePie.php @@ -24,7 +24,7 @@ class InlinePie extends AbstractWidget const NUMBER_FORMAT_TIME = 'time'; const NUMBER_FORMAT_BYTES = 'bytes'; const NUMBER_FORMAT_RATIO = 'ratio'; - + /** * The template string used for rendering this widget * The template string used for rendering this widget @@ -137,7 +137,7 @@ EOD; * @var string */ private $tooltipFormat = '{{title}}
      {{label}}: {{formatted}} ({{percent}}%)'; - + /** * The number format used to render numeric values in tooltips * @@ -212,7 +212,7 @@ EOD; { $this->format = $format; } - + /** * A format string used to render the content of the piechart tooltips * @@ -346,11 +346,11 @@ EOD; { $template = $this->template; $template = str_replace('{url}', $this->url, $template); - + // style $template = str_replace('{width}', $this->width, $template); $template = str_replace('{height}', $this->height, $template); - $template = str_replace('{title}', $this->escape($this->title), $template); + $template = str_replace('{title}', htmlspecialchars($this->title), $template); $template = str_replace('{style}', $this->style, $template); $template = str_replace('{colors}', implode(',', $this->colors), $template); $template = str_replace('{borderWidth}', $this->borderWidth, $template); @@ -370,9 +370,9 @@ EOD; foreach ($this->data as $key => $value) { $formatted[$key] = $this->formatValue($value); } - $template = str_replace('{data}', $this->escape(implode(',', $data)), $template); - $template = str_replace('{formatted}', $this->escape(implode('|', $formatted)), $template); - $template = str_replace('{labels}', $this->escape($this->createLabelString()), $template); + $template = str_replace('{data}', htmlspecialchars(implode(',', $data)), $template); + $template = str_replace('{formatted}', htmlspecialchars(implode('|', $formatted)), $template); + $template = str_replace('{labels}', htmlspecialchars($this->createLabelString()), $template); $template = str_replace('{tooltipFormat}', $this->tooltipFormat, $template); return $template; } diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index 25f88533b..cd480ba32 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -259,7 +259,7 @@ EOT; try { $html = $this->render(Icinga::app()->getViewRenderer()->view); } catch (Exception $e) { - return $this->escape($e->getMessage()); + return htmlspecialchars($e->getMessage()); } return $html; } From 9269a0cbd51404142c127a822aaf912625976d85 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 23 Jul 2014 12:29:27 +0200 Subject: [PATCH 086/184] Revert "Do not use htmlspecialchars in view scripts" This reverts commit 607aa9b203377126b399667cd0b10c80d75e04b7. `Perfdata::escape()' does not exist. But `Perfdata::view::escape()' does. --- .../application/views/helpers/Perfdata.php | 6 +++--- .../views/scripts/list/contacts.phtml | 8 ++++---- .../application/views/scripts/show/contact.phtml | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php index 193048334..f4cffaea7 100644 --- a/modules/monitoring/application/views/helpers/Perfdata.php +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -36,9 +36,9 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract } $pieChart->setStyle('margin: 0.2em 0.5em 0.2em 0.5em;'); $table[] = '' . $pieChart->render() - . $this->escape($perfdata->getLabel()) + . htmlspecialchars($perfdata->getLabel()) . ' ' - . $this->escape($this->formatPerfdataValue($perfdata)) . + . htmlspecialchars($this->formatPerfdataValue($perfdata)) . ' '; } } @@ -89,7 +89,7 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract protected function createInlinePie(Perfdata $perfdata) { $pieChart = new InlinePie($this->calculatePieChartData($perfdata), $perfdata->getLabel()); - $pieChart->setLabel($this->escape($perfdata->getLabel())); + $pieChart->setLabel(htmlspecialchars($perfdata->getLabel())); $pieChart->setHideEmptyLabel(); //$pieChart->setHeight(32)->setWidth(32); diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml index 9fc05de8e..81a061973 100644 --- a/modules/monitoring/application/views/scripts/list/contacts.phtml +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -26,23 +26,23 @@ $contactHelper = $this->getHelper('ContactFlags');
      %2$s', t('Email'), - $this->escape($contact->contact_email) + htmlspecialchars($contact->contact_email) ) ?>
      contact_pager): ?>
      : - escape($contact->contact_pager) ?> + contact_pager) ?>
      : - escape($contact->contact_notify_service_timeperiod) ?> + contact_notify_service_timeperiod) ?>
      : - escape($contact->contact_notify_host_timeperiod) ?> + contact_notify_host_timeperiod) ?>
      diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml index a7b691eb4..609bbbc69 100644 --- a/modules/monitoring/application/views/scripts/show/contact.phtml +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -7,8 +7,8 @@ $contactHelper = $this->getHelper('ContactFlags'); - escape($contact->contact_name) ?> (escape($contact->contact_alias) + contact_name) ?> (contact_alias) ?>) @@ -18,30 +18,30 @@ $contactHelper = $this->getHelper('ContactFlags'); %1$s', - $this->escape($contact->contact_email) + htmlspecialchars($contact->contact_email) ); ?> contact_pager): ?> - escape($contact->contact_pager) ?> + contact_pager) ?> - escape($contactHelper->contactFlags($contact, 'service')) ?> + contactFlags($contact, 'service')) ?> - escape($contactHelper->contactFlags($contact, 'host')) ?> + contactFlags($contact, 'host')) ?> - escape($contact->contact_notify_service_timeperiod) ?> + contact_notify_service_timeperiod) ?> - escape($contact->contact_notify_host_timeperiod) ?> + contact_notify_host_timeperiod) ?> From a2809552f2ac509d69afcb99ab26fbecf7b0b7ae Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Wed, 23 Jul 2014 12:41:05 +0200 Subject: [PATCH 087/184] Do not use htmlspecialchars in view scripts fixes #6759 --- .../views/scripts/list/contacts.phtml | 8 ++++---- .../application/views/scripts/show/contact.phtml | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml index 81a061973..9fc05de8e 100644 --- a/modules/monitoring/application/views/scripts/list/contacts.phtml +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -26,23 +26,23 @@ $contactHelper = $this->getHelper('ContactFlags');
      %2$s', t('Email'), - htmlspecialchars($contact->contact_email) + $this->escape($contact->contact_email) ) ?>
      contact_pager): ?>
      : - contact_pager) ?> + escape($contact->contact_pager) ?>
      : - contact_notify_service_timeperiod) ?> + escape($contact->contact_notify_service_timeperiod) ?>
      : - contact_notify_host_timeperiod) ?> + escape($contact->contact_notify_host_timeperiod) ?>
      diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml index 609bbbc69..a7b691eb4 100644 --- a/modules/monitoring/application/views/scripts/show/contact.phtml +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -7,8 +7,8 @@ $contactHelper = $this->getHelper('ContactFlags'); - contact_name) ?> (contact_alias) + escape($contact->contact_name) ?> (escape($contact->contact_alias) ?>) @@ -18,30 +18,30 @@ $contactHelper = $this->getHelper('ContactFlags'); %1$s', - htmlspecialchars($contact->contact_email) + $this->escape($contact->contact_email) ); ?> contact_pager): ?> - contact_pager) ?> + escape($contact->contact_pager) ?> - contactFlags($contact, 'service')) ?> + escape($contactHelper->contactFlags($contact, 'service')) ?> - contactFlags($contact, 'host')) ?> + escape($contactHelper->contactFlags($contact, 'host')) ?> - contact_notify_service_timeperiod) ?> + escape($contact->contact_notify_service_timeperiod) ?> - contact_notify_host_timeperiod) ?> + escape($contact->contact_notify_host_timeperiod) ?> From a4b7204e2397fcb66ac262f608b0dfa22be48cda Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Wed, 23 Jul 2014 13:10:09 +0200 Subject: [PATCH 088/184] Contactgroups view: correct links to contacts detail pages fixes #6514 --- .../application/views/scripts/list/contactgroups.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/scripts/list/contactgroups.phtml b/modules/monitoring/application/views/scripts/list/contactgroups.phtml index 98661f4e6..c110bf011 100644 --- a/modules/monitoring/application/views/scripts/list/contactgroups.phtml +++ b/modules/monitoring/application/views/scripts/list/contactgroups.phtml @@ -22,7 +22,7 @@ foreach ($groupData as $groupName => $groupInfo): ?>
      escape($c->contact_alias) ?> From 507ac1c4d7ae1b04eeed1e806f95224ba2600dad Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:52:25 +0200 Subject: [PATCH 089/184] doc/MarkdownFileIterator: Nail down PHPDoc refs #4820 --- modules/doc/library/Doc/MarkdownFileIterator.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/doc/library/Doc/MarkdownFileIterator.php b/modules/doc/library/Doc/MarkdownFileIterator.php index 857d078ed..6f317ce6a 100644 --- a/modules/doc/library/Doc/MarkdownFileIterator.php +++ b/modules/doc/library/Doc/MarkdownFileIterator.php @@ -7,12 +7,12 @@ namespace Icinga\Module\Doc; use RecursiveFilterIterator; /** - * Iterator over Markdown files recursively + * Recursive iterator over Markdown files */ class MarkdownFileIterator extends RecursiveFilterIterator { /** - * Accept files with .md suffix + * Accept files with '.md' suffix * * @return bool Whether the current element of the iterator is acceptable * through this filter @@ -20,6 +20,7 @@ class MarkdownFileIterator extends RecursiveFilterIterator public function accept() { $current = $this->getInnerIterator()->current(); + /* @var $current \SplFileInfo */ if (! $current->isFile()) { return false; } From a0c331020ba4b9be51208091d93d81d21ba7c99f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:53:41 +0200 Subject: [PATCH 090/184] doc/DocException: Extend `RuntimeException' instead of `Exception' Errors in the documentation module can only be found on runtime. refs #4820 --- modules/doc/library/Doc/Exception/DocException.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/doc/library/Doc/Exception/DocException.php b/modules/doc/library/Doc/Exception/DocException.php index f684f1faa..374ea3e01 100644 --- a/modules/doc/library/Doc/Exception/DocException.php +++ b/modules/doc/library/Doc/Exception/DocException.php @@ -4,11 +4,9 @@ namespace Icinga\Module\Doc\Exception; -use Exception; +use RuntimeException; /** - * Exception thrown if an error in the documentation module occurs + * Exception thrown if an error in the documentation module's library occurs */ -class DocException extends Exception -{ -} +class DocException extends RuntimeException {} From 77f8a3d67dc0d49c213d3572c68c9cb0e98a80f5 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:55:54 +0200 Subject: [PATCH 091/184] doc/DocEmptyException: Remove unnecessary line breaks refs #4820 --- modules/doc/library/Doc/Exception/DocEmptyException.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/doc/library/Doc/Exception/DocEmptyException.php b/modules/doc/library/Doc/Exception/DocEmptyException.php index 032a01744..2869e2678 100644 --- a/modules/doc/library/Doc/Exception/DocEmptyException.php +++ b/modules/doc/library/Doc/Exception/DocEmptyException.php @@ -7,6 +7,4 @@ namespace Icinga\Module\Doc\Exception; /** * Exception thrown if a documentation directory is empty */ -class DocEmptyException extends DocException -{ -} +class DocEmptyException extends DocException {} From 71e81087b3c0f922b90c0c88e8f9af6a7af605f3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:57:46 +0200 Subject: [PATCH 092/184] doc/ChapterNotFoundException: Remove unnecessary line breaks refs #4820 --- .../doc/library/Doc/Exception/ChapterNotFoundException.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/doc/library/Doc/Exception/ChapterNotFoundException.php b/modules/doc/library/Doc/Exception/ChapterNotFoundException.php index 1e9bcf7e9..cd048a162 100644 --- a/modules/doc/library/Doc/Exception/ChapterNotFoundException.php +++ b/modules/doc/library/Doc/Exception/ChapterNotFoundException.php @@ -7,6 +7,4 @@ namespace Icinga\Module\Doc\Exception; /** * Exception thrown if a chapter was not found */ -class ChapterNotFoundException extends DocException -{ -} +class ChapterNotFoundException extends DocException {} From 4f8cbb99dd6b1fde47d8f28dd3b380409811f933 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:58:46 +0200 Subject: [PATCH 093/184] doc/lib: Add `NonEmptyFileIterator' for iterating over non-empty files refs #4820 --- .../doc/library/Doc/NonEmptyFileIterator.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 modules/doc/library/Doc/NonEmptyFileIterator.php diff --git a/modules/doc/library/Doc/NonEmptyFileIterator.php b/modules/doc/library/Doc/NonEmptyFileIterator.php new file mode 100644 index 000000000..71bf5acfa --- /dev/null +++ b/modules/doc/library/Doc/NonEmptyFileIterator.php @@ -0,0 +1,31 @@ +getInnerIterator()->current(); + /* @var $current \SplFileInfo */ + if (! $current->isFile() + || $current->getSize() === 0 + ) { + return false; + } + return true; + } +} From c325c09293959cdf8789ddad31b5bc3c917c61bc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 18:59:42 +0200 Subject: [PATCH 094/184] doc/DocIterator: Use `NonEmptyFileIterator' Considering empty files makes no sense. refs #4820 --- modules/doc/library/Doc/DocIterator.php | 71 ++++++++++++------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/modules/doc/library/Doc/DocIterator.php b/modules/doc/library/Doc/DocIterator.php index 1a229c918..43a9c7727 100644 --- a/modules/doc/library/Doc/DocIterator.php +++ b/modules/doc/library/Doc/DocIterator.php @@ -5,63 +5,58 @@ namespace Icinga\Module\Doc; use ArrayIterator; -use RunetimeException; - -class FileLockingIterator extends ArrayIterator -{ - public function next() - { - $this->current()->flock(LOCK_UN); - parent::next(); - } - - public function valid() - { - if (!parent::valid()) { - return false; - } - $fileInfo = $this->current(); - try { - $fileObject = $fileInfo->openFile(); - } catch (RuntimeException $e) { - throw new DocException($e->getMessage()); - } - if ($fileObject->flock(LOCK_SH) === false) { - throw new DocException('Couldn\'t get the lock'); - } - $this[$this->key()] = $fileObject; - return true; - } -} - use Countable; use IteratorAggregate; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; +/** + * Iterator over non-empty Markdown files ordered by the case insensitive "natural order" of file names + */ class DocIterator implements Countable, IteratorAggregate { - protected $fileInfos; + /** + * Ordered files + * + * @var array + */ + protected $fileInfo; + /** + * Create a new DocIterator + * + * @param string $path Path to the documentation + */ public function __construct($path) { - $iter = new RecursiveIteratorIterator( - new MarkdownFileIterator( - new RecursiveDirectoryIterator($path) + $it = new RecursiveIteratorIterator( + new NonEmptyFileIterator( + new MarkdownFileIterator( + new RecursiveDirectoryIterator($path) + ) ) ); - $fileInfos = iterator_to_array($iter); - natcasesort($fileInfos); - $this->fileInfos = $fileInfos; + // Unfortunately we have no chance to sort the iterator + $fileInfo = iterator_to_array($it); + natcasesort($fileInfo); + $this->fileInfo = $fileInfo; } + /** + * (non-PHPDoc) + * @see Countable::count() + */ public function count() { - return count($this->fileInfos); + return count($this->fileInfo); } + /** + * (non-PHPDoc) + * @see IteratorAggregate::getIterator() + */ public function getIterator() { - return new FileLockingIterator($this->fileInfos); + return new ArrayIterator($this->fileInfo); } } From c71086c748d1153c26a86570778fc4695be2f3af Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:00:50 +0200 Subject: [PATCH 095/184] doc/lib: Add `Section' In the future a documentation will be represented as tree of sections. refs #4820 --- modules/doc/library/Doc/Section.php | 143 ++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 modules/doc/library/Doc/Section.php diff --git a/modules/doc/library/Doc/Section.php b/modules/doc/library/Doc/Section.php new file mode 100644 index 000000000..57419dc0a --- /dev/null +++ b/modules/doc/library/Doc/Section.php @@ -0,0 +1,143 @@ +id = $id; + $this->title = $title; + $this->level = $level; + $this->nofollow = $nofollow; + $this->chapterTitle= $chapterTitle; + } + + /** + * Get the ID of the section + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get the title of the section + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get the header level + * + * @return int + */ + public function getLevel() + { + return $this->level; + } + + /** + * Whether to instruct search engines to not index the link to the section + * + * @return bool + */ + public function isNofollow() + { + return $this->nofollow; + } + + /** + * The title of the chapter the section is part of + * + * @return string + */ + public function getChapterTitle() + { + return $this->chapterTitle; + } + + /** + * Append content + * + * @param string $content + */ + public function appendContent($content) + { + $this->content[] = $content; + } + + /** + * Get the content of the section + * + * @return array + */ + public function getContent() + { + return $this->content; + } +} From bbcdcb4609cf90b80f1a53c6b51c0585524bfd9f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:05:37 +0200 Subject: [PATCH 096/184] lib: Add `Identifiable' interface for objects that are identifiable by an ID of any type --- library/Icinga/Data/Identifiable.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 library/Icinga/Data/Identifiable.php diff --git a/library/Icinga/Data/Identifiable.php b/library/Icinga/Data/Identifiable.php new file mode 100644 index 000000000..cfa727a1d --- /dev/null +++ b/library/Icinga/Data/Identifiable.php @@ -0,0 +1,18 @@ + Date: Mon, 28 Jul 2014 19:06:12 +0200 Subject: [PATCH 097/184] doc/lib: Add `DocTree' for representing a documentation as tree of sections refs #4820 --- modules/doc/library/Doc/DocTree.php | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 modules/doc/library/Doc/DocTree.php diff --git a/modules/doc/library/Doc/DocTree.php b/modules/doc/library/Doc/DocTree.php new file mode 100644 index 000000000..07a0c5279 --- /dev/null +++ b/modules/doc/library/Doc/DocTree.php @@ -0,0 +1,80 @@ +getId(); + if (isset($this->nodes[$rootId])) { + $rootId = uniqid($rootId); +// throw new LogicException( +// sprintf('Can\'t add root node: a root node with the id \'%s\' already exists', $rootId) +// ); + } + $this->nodes[$rootId] = $this->appendChild($root); + } + + /** + * Append a child node to a parent node + * + * @param Identifiable $child + * @param Identifiable $parent + * + * @throws LogicException If the the tree does not contain the parent node + */ + public function addChild(Identifiable $child, Identifiable $parent) + { + $childId = $child->getId(); + $parentId = $parent->getId(); + if (isset($this->nodes[$childId])) { + $childId = uniqid($childId); +// throw new LogicException( +// sprintf('Can\'t add child node: a child node with the id \'%s\' already exists', $childId) +// ); + } + if (! isset($this->nodes[$parentId])) { + throw new LogicException( + sprintf('Can\'t add child node: there\'s no parent node having the id \'%s\'', $parentId) + ); + } + $this->nodes[$childId] = $this->nodes[$parentId]->appendChild($child); + } + + /** + * Get a node + * + * @param mixed $id + * + * @return Node|null + */ + public function getNode($id) + { + if (! isset($this->nodes[$id])) { + return null; + } + return $this->nodes[$id]; + } +} From e26d360561b7e4f0edc5bc619d5803306ebb2937 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:07:13 +0200 Subject: [PATCH 098/184] lib/Node: Remove method `findNodeBy()' The new `DocTree' class provides `getNode()'. --- library/Icinga/Data/Tree/Node.php | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/library/Icinga/Data/Tree/Node.php b/library/Icinga/Data/Tree/Node.php index b8f9e8dad..b57b0fad4 100644 --- a/library/Icinga/Data/Tree/Node.php +++ b/library/Icinga/Data/Tree/Node.php @@ -4,9 +4,6 @@ namespace Icinga\Data\Tree; -use Exception; -use RecursiveIteratorIterator; -use RuntimeException; use SplDoublyLinkedList; class Node extends SplDoublyLinkedList implements NodeInterface @@ -79,31 +76,4 @@ class Node extends SplDoublyLinkedList implements NodeInterface } return $current; } - - /** - * Find the first child node by searching through nodes deeper than the immediate children using a custom function - * - * @param $callback - * - * @return NodeInterface|null - * @throws Exception - */ - public function findNodeBy($callback) - { - if (! is_callable($callback)) { - throw new RuntimeException('Callable expected'); - } - foreach (new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST) as $node) { - try { - $found = call_user_func($callback, $node); - } catch (Exception $e) { - // TODO(el): Log exception and return false instead? - throw $e; - } - if ($found) { - return $node; - } - } - return null; - } } From 134db3fc666690bd815f2c3f677ed40454afa639 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:09:04 +0200 Subject: [PATCH 099/184] doc/DocParser: Replace `getDoc()' and `getToc()' with `getDocTree()' refs #4820 --- modules/doc/library/Doc/DocParser.php | 229 ++++++++------------------ 1 file changed, 66 insertions(+), 163 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index c213c764a..b74a0b377 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -4,12 +4,8 @@ namespace Icinga\Module\Doc; -require_once 'IcingaVendor/Parsedown/Parsedown.php'; - -use Parsedown; -use Icinga\Data\Tree\Node; +use SplDoublyLinkedList; use Icinga\Exception\NotReadableError; -use Icinga\Module\Doc\Exception\ChapterNotFoundException; use Icinga\Module\Doc\Exception\DocEmptyException; use Icinga\Module\Doc\Exception\DocException; @@ -44,152 +40,26 @@ class DocParser public function __construct($path) { if (! is_dir($path)) { - throw new DocException('Doc directory `' . $path . '\' does not exist'); + throw new DocException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'does not exist') + ); } if (! is_readable($path)) { - throw new NotReadableError('Doc directory `' . $path . '\' is not readable'); + throw new DocException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'is not readable') + ); } $docIterator = new DocIterator($path); if ($docIterator->count() === 0) { - throw new DocEmptyException('Doc directory `' . $path . '\' is empty'); + throw new DocEmptyException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' + . mt('doc', 'does not contain any non-empty Markdown file (\'.md\' suffix') + ); } $this->path = $path; $this->docIterator = $docIterator; } - /** - * Retrieve the table of contents - * - * @return Node - */ - public function getToc() - { - $tocStack = array((object) array( - 'level' => 0, - 'node' => new Node() - )); - foreach ($this->docIterator as $fileObject) { - $line = null; - $currentChapterName = null; - while (! $fileObject->eof()) { - // Save last line for setext-style headers - $lastLine = $line; - $line = $fileObject->fgets(); - $header = $this->extractHeader($line, $lastLine); - if ($header !== null) { - list($header, $level) = $header; - $id = $this->extractHeaderId($header); - $nofollow = false; - $this->reduceToc($tocStack, $level); - if ($id === null) { - $path = array(); - foreach (array_slice($tocStack, 1) as $entity) { - $path[] = $entity->node->getValue()->title; - } - $path[] = $header; - $id = implode('-', $path); - $nofollow = true; - } - $id = urlencode(str_replace('.', '.', strip_tags($id))); - if ($currentChapterName === null) { - // The first header is the chapter's name - $currentChapterName = $id; - $id = null; - } - $node = end($tocStack)->node->appendChild( - (object) array( - 'id' => $id, - 'title' => $header, - 'nofollow' => $nofollow, - 'chapterName' => $currentChapterName - ) - ); - $tocStack[] = (object) array( - 'level' => $level, - 'node' => $node - ); - } - } - } - return $tocStack[0]->node; - } - - /** - * Retrieve a chapter - * - * @param string $chapterName - * - * @return string - * @throws ChapterNotFoundException If the chapter was not found - */ - public function getChapter($chapterName) - { - $tocStack = array((object) array( - 'level' => 0, - 'node' => new Node() - )); - foreach ($this->docIterator as $fileObject) { - $line = null; - $currentChapterName = null; - $chapter = array(); - while (! $fileObject->eof()) { - // Save last line for setext-style headers - $lastLine = $line; - $line = $fileObject->fgets(); - $header = $this->extractHeader($line, $lastLine); - if ($header !== null) { - list($header, $level) = $header; - $id = $this->extractHeaderId($header); - $this->reduceToc($tocStack, $level); - if ($id === null) { - $path = array(); - foreach (array_slice($tocStack, 1) as $entity) { - $path[] = $entity->node->getValue()->title; - } - $path[] = $header; - $id = implode('-', $path); - } - $id = urlencode(str_replace('.', '.', strip_tags($id))); - if ($currentChapterName === null) { - $currentChapterName = $id; - $id = null; - } - $node = end($tocStack)->node->appendChild( - (object) array( - 'title' => $header - ) - ); - $tocStack[] = (object) array( - 'level' => $level, - 'node' => $node - ); - $line = '' . PHP_EOL . $line; - } - $chapter[] = $line; - } - if ($currentChapterName === $chapterName) { - return preg_replace_callback( - '#
      (.*?)\
      #s', - array($this, 'highlight'), - Parsedown::instance()->text(implode('', $chapter)) - ); - } - } - throw new ChapterNotFoundException('Chapter \'' . $chapterName . '\' not found'); - } - - /** - * Syntax highlighting for PHP code - * - * @param $match - * - * @return string - */ - protected function highlight($match) - { - return highlight_string(htmlspecialchars_decode($match[1]), true); - } - /** * Extract atx- or setext-style headers from the given lines * @@ -208,7 +78,7 @@ class DocParser && $line[0] === '#' && preg_match('/^#+/', $line, $match) === 1 ) { - // Atx-style + // Atx $level = strlen($match[0]); $header = trim(substr($line, $level)); if (! $header) { @@ -233,36 +103,69 @@ class DocParser if ($header === null) { return null; } - return array($header, $level); - } - - /** - * Extract header id in an a or a span tag - * - * @param string &$header - * - * @return id|null - */ - protected function extractHeaderId(&$header) - { if ($header[0] === '<' - && preg_match('#(?:<(?Pa|span) id="(?P.+)">)#u', $header, $match) + && preg_match('#(?:<(?Pa|span) (?:id|name)="(?P.+)">)\s*#u', $header, $match) ) { $header = str_replace($match[0], '', $header); - return $match['id']; + $id = $match['id']; + } else { + $id = null; } - return null; + return array($header, $id, $level); } /** - * Reduce the toc stack to the given level + * Get the documentation tree * - * @param array &$tocStack - * @param int $level + * @return DocTree */ - protected function reduceToc(array &$tocStack, $level) { - while (end($tocStack)->level >= $level) { - array_pop($tocStack); + public function getDocTree() + { + $tree = new DocTree(); + $stack = new SplDoublyLinkedList(); + foreach ($this->docIterator as $fileInfo) { + /* @var $file \SplFileInfo */ + $file = $fileInfo->openFile(); + /* @var $file \SplFileObject */ + $lastLine = null; + foreach ($file as $line) { + $header = $this->extractHeader($line, $lastLine); + if ($header !== null) { + list($header, $id, $level) = $header; // When overwriting the variable to extract, it has to be + // list()'s first parameter since list() assigns the values + // starting with the right-most parameter + while (! $stack->isEmpty() && $stack->top()->getLevel() >= $level) { + $stack->pop(); + } + if ($id === null) { + $path = array(); + foreach ($stack as $section) { + /* @var $section Section */ + $path[] = $section->getTitle(); + } + $path[] = $header; + $id = implode('-', $path); + $nofollow = true; + } else { + $nofollow = false; + } + if ($stack->isEmpty()) { + $chapterName = $header; + $section = new Section($id, $header, $level, $nofollow, $chapterName); + $tree->addRoot($section); + } else { + $chapterName = $stack->bottom()->getTitle(); + $section = new Section($id, $header, $level, $nofollow, $chapterName); + $tree->addChild($section, $stack->top()); + } + $stack->push($section); + } else { + $stack->top()->appendContent($line); + } + // Save last line for setext-style headers + $lastLine = $line; + } } + return $tree; } } From 00337330628593c47bc3d8c68cbf07a70c8990bb Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:09:55 +0200 Subject: [PATCH 100/184] doc/lib: Add `Renderer' as base class for toc and section renderer refs #4820 --- modules/doc/library/Doc/Renderer.php | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 modules/doc/library/Doc/Renderer.php diff --git a/modules/doc/library/Doc/Renderer.php b/modules/doc/library/Doc/Renderer.php new file mode 100644 index 000000000..0aebb89b9 --- /dev/null +++ b/modules/doc/library/Doc/Renderer.php @@ -0,0 +1,75 @@ + Date: Mon, 28 Jul 2014 19:10:40 +0200 Subject: [PATCH 101/184] doc/lib: Add `TocRenderer' for rendering instances of `DocTree' as toc refs #4820 --- modules/doc/library/Doc/TocRenderer.php | 109 ++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 modules/doc/library/Doc/TocRenderer.php diff --git a/modules/doc/library/Doc/TocRenderer.php b/modules/doc/library/Doc/TocRenderer.php new file mode 100644 index 000000000..8264c2d8e --- /dev/null +++ b/modules/doc/library/Doc/TocRenderer.php @@ -0,0 +1,109 @@ +url = $url; + $this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams); + } + + public function beginIteration() + { + $this->content[] = ''; + } + + public function beginChildren() + { + $this->content[] = '
        '; + } + + public function endChildren() + { + $this->content[] = '
    • '; + } + + /** + * Render the toc + * + * @param View $view + * @param Zend_View_Helper_Url $zendUrlHelper + * + * @return string + */ + public function render(View $view, Zend_View_Helper_Url $zendUrlHelper) + { + foreach ($this as $node) { + $section = $node->getValue(); + /* @var $section \Icinga\Module\Doc\Section */ + $path = $zendUrlHelper->url( + array_merge( + $this->urlParams, + array( + 'chapterName' => $this->encodeUrlParam($section->getChapterTitle()) + ) + ), + $this->url, + false, + false + ); + $url = $view->url($path); + $url->setAnchor($this->encodeAnchor($section->getId())); + $this->content[] = sprintf( + '
    • %s', + $section->isNofollow() ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $view->escape($section->getTitle()) + ); + if (! $this->getInnerIterator()->current()->hasChildren()) { + $this->content[] = '
    • '; + } + } + return implode("\n", $this->content); + } +} From 79f6130e3c510c96e827b5b3b51d3fc44c2e5e3b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:11:15 +0200 Subject: [PATCH 102/184] doc/lib: Add `SectionRenderer' for rendering instances of `DocTree' as sections refs #4820 --- modules/doc/library/Doc/SectionRenderer.php | 192 ++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 modules/doc/library/Doc/SectionRenderer.php diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php new file mode 100644 index 000000000..012d50678 --- /dev/null +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -0,0 +1,192 @@ +docTree = $docTree; + $this->view = $view; + $this->zendUrlHelper = $zendUrlHelper; + $this->url = $url; + $this->urlParams = $urlParams; + } + + public function render($match) + { + $node = $this->docTree->getNode(Renderer::decodeAnchor($match['fragment'])); + /* @var $node \Icinga\Data\Tree\Node */ + if ($node === null) { + return $match[0]; + } + $section = $node->getValue(); + /* @var $section \Icinga\Module\Doc\Section */ + $path = $this->zendUrlHelper->url( + array_merge( + $this->urlParams, + array( + 'chapterName' => SectionRenderer::encodeUrlParam($section->getChapterTitle()) + ) + ), + $this->url, + false, + false + ); + $url = $this->view->url($path); + $url->setAnchor(SectionRenderer::encodeAnchor($section->getId())); + return sprintf( + 'isNofollow() ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl() + ); + } +} + +/** + * Section renderer + */ +class SectionRenderer extends Renderer +{ + /** + * The documentation tree + * + * @var DocTree + */ + protected $docTree; + + /** + * The URL to replace links with + * + * @var string + */ + protected $url; + + /** + * Additional URL parameters + * + * @var array + */ + protected $urlParams; + + /** + * Parsedown instance + * + * @var Parsedown + */ + protected $parsedown; + + /** + * Content + * + * @var array + */ + protected $content = array(); + + /** + * Create a new section renderer + * + * @param DocTree $docTree The documentation tree + * @param string|null $chapterTitle If not null, the chapter title to filter for + * @param string $url The URL to replace links with + * @param array $urlParams Additional URL parameters + * + * @throws ChapterNotFoundException If the chapter to filter for was not found + */ + public function __construct(DocTree $docTree, $chapterTitle, $url, array $urlParams) + { + if ($chapterTitle !== null) { + $filter = new SectionFilterIterator($docTree, $chapterTitle); + if ($filter->count() === 0) { + throw new ChapterNotFoundException( + mt('doc', 'Chapter') . ' \'' . $chapterTitle . '\' ' . mt('doc', 'not found') + ); + } + parent::__construct( + $filter, + RecursiveIteratorIterator::SELF_FIRST + ); + } else { + parent::__construct($docTree, RecursiveIteratorIterator::SELF_FIRST); + } + $this->docTree = $docTree; + $this->url = $url; + $this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams); + $this->parsedown = Parsedown::instance(); + } + + /** + * Syntax highlighting for PHP code + * + * @param $match + * + * @return string + */ + protected function highlightPhp($match) + { + return '
      ' . highlight_string(htmlspecialchars_decode($match[1]), true) . '
      '; + } + + /** + * Render the section + * + * @param View $view + * @param Zend_View_Helper_Url $zendUrlHelper + * @return string + */ + public function render(View $view, Zend_View_Helper_Url $zendUrlHelper) + { + $callback = new Callback($this->docTree, $view, $zendUrlHelper, $this->url, $this->urlParams); + $content = array(); + foreach ($this as $node) { + $section = $node->getValue(); + /* @var $section \Icinga\Module\Doc\Section */ + $content[] = sprintf( + '
      %3$s', + Renderer::encodeAnchor($section->getId()), + $section->getLevel(), + $view->escape($section->getTitle()) + ); + $html = preg_replace_callback( + '#
      (.*?)
      #s', + array($this, 'highlightPhp'), + $this->parsedown->text(implode('', $section->getContent())) + ); + $content[] = preg_replace_callback( + '/[^>]*?\s+)?href="#(?P[^"]+)"/', + array($callback, 'render'), + $html + ); + } + return implode("\n", $content); + } +} From 88312e6284b56941daf19f4cc64f43fbbe9dd134 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:11:59 +0200 Subject: [PATCH 103/184] doc/lib: Add `SectionFilterIterator' for iterating over sections that are part of a particular chapter refs #4820 --- .../doc/library/Doc/SectionFilterIterator.php | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 modules/doc/library/Doc/SectionFilterIterator.php diff --git a/modules/doc/library/Doc/SectionFilterIterator.php b/modules/doc/library/Doc/SectionFilterIterator.php new file mode 100644 index 000000000..9b5d2c013 --- /dev/null +++ b/modules/doc/library/Doc/SectionFilterIterator.php @@ -0,0 +1,68 @@ +chapterTitle = $chapterTitle; + } + + /** + * Accept sections that are part of the given chapter + * + * @return bool Whether the current element of the iterator is acceptable + * through this filter + */ + public function accept() + { + $section = $this->getInnerIterator()->current()->getValue(); + /* @var $section \Icinga\Module\Doc\Section */ + if ($section->getChapterTitle() === $this->chapterTitle) { + return true; + } + return false; + } + + /** + * (non-PHPDoc) + * @see RecursiveFilterIterator::getChildren() + */ + public function getChildren() + { + return new static($this->getInnerIterator()->getChildren(), $this->chapterTitle); + } + + /** + * (non-PHPDoc) + * @see Countable::count() + */ + public function count() + { + return iterator_count($this); + } +} From edc89d6ad67709a9e8d5f55373d87b133e3a0cbf Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:12:35 +0200 Subject: [PATCH 104/184] lib: Remove `NodeRenderer' `NodeRenderer' is superseded by `TocRenderer'. --- library/Icinga/Data/Tree/NodeRenderer.php | 58 ----------------------- 1 file changed, 58 deletions(-) delete mode 100644 library/Icinga/Data/Tree/NodeRenderer.php diff --git a/library/Icinga/Data/Tree/NodeRenderer.php b/library/Icinga/Data/Tree/NodeRenderer.php deleted file mode 100644 index 4cebb7e01..000000000 --- a/library/Icinga/Data/Tree/NodeRenderer.php +++ /dev/null @@ -1,58 +0,0 @@ -content[] = '
        '; - } - - public function endIteration() - { - $this->content[] = '
      '; - } - - public function beginChildren() - { - $this->content[] = '
        '; - } - - public function endChildren() - { - $this->content[] = '
      '; - } - - public function render($callback) - { - if (! is_callable($callback)) { - throw new RuntimeException('Callable expected'); - } - foreach ($this as $node) { - try { - $content = call_user_func($callback, $node); - } catch (Exception $e) { - throw new RuntimeException($e); - } - $this->content[] = $content; - } - return implode("\n", $this->content); - } -} From ce4fa1fd3d0531888905cfc5a6a9292e8037c547 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:14:50 +0200 Subject: [PATCH 105/184] Vagrant/doc module: Add menu link to the documentation module refs #4820 --- .vagrant-puppet/manifests/default.pp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.vagrant-puppet/manifests/default.pp b/.vagrant-puppet/manifests/default.pp index 68affd347..121b7813f 100644 --- a/.vagrant-puppet/manifests/default.pp +++ b/.vagrant-puppet/manifests/default.pp @@ -792,3 +792,15 @@ file { '/etc/bash_completion.d/icingacli': require => Exec['install bash-completion'] } +file { '/etc/icingaweb/modules/doc/': + ensure => 'directory', + owner => 'apache', + group => 'apache' +} + +file { '/etc/icingaweb/modules/doc/menu.ini': + source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini', + owner => 'apache', + group => 'apache', +} + From 9eb4b62cd0c9b712f2ea910d2cfad94030d3845b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:15:44 +0200 Subject: [PATCH 106/184] doc: Register PDF route refs #4820 --- modules/doc/run.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/doc/run.php b/modules/doc/run.php index 592d85ede..eaab1cb05 100644 --- a/modules/doc/run.php +++ b/modules/doc/run.php @@ -34,7 +34,17 @@ $docModuleToc = new Zend_Controller_Router_Route( ) ); +$docModulePdf = new Zend_Controller_Router_Route( + 'doc/module/:moduleName/pdf', + array( + 'controller' => 'module', + 'action' => 'pdf', + 'module' => 'doc' + ) +); + $this->addRoute('doc/module/chapter', $docModuleChapter); $this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter); $this->addRoute('doc/module/toc', $docModuleToc); +$this->addRoute('doc/module/pdf', $docModulePdf); From 788e3eb6fc09e26b2dd814ad1108da50ec0182ea Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:17:03 +0200 Subject: [PATCH 107/184] doc: No longer use separate view scripts refs #4820 --- .../controllers/IcingawebController.php | 19 +++-- .../controllers/IndexController.php | 4 +- .../controllers/ModuleController.php | 69 +++++++++++++++---- .../application/views/scripts/chapter.phtml | 3 + .../views/scripts/icingaweb/chapter.phtml | 40 ----------- .../views/scripts/icingaweb/toc.phtml | 23 ------- .../views/scripts/index/index.phtml | 5 +- .../views/scripts/module/chapter.phtml | 40 ----------- .../views/scripts/module/index.phtml | 2 +- .../views/scripts/module/toc.phtml | 25 ------- .../doc/application/views/scripts/pdf.phtml | 7 ++ .../doc/application/views/scripts/toc.phtml | 6 ++ modules/doc/library/Doc/DocController.php | 58 ++++++++++++---- 13 files changed, 138 insertions(+), 163 deletions(-) create mode 100644 modules/doc/application/views/scripts/chapter.phtml delete mode 100644 modules/doc/application/views/scripts/icingaweb/chapter.phtml delete mode 100644 modules/doc/application/views/scripts/icingaweb/toc.phtml delete mode 100644 modules/doc/application/views/scripts/module/chapter.phtml delete mode 100644 modules/doc/application/views/scripts/module/toc.phtml create mode 100644 modules/doc/application/views/scripts/pdf.phtml create mode 100644 modules/doc/application/views/scripts/toc.phtml diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 092de4025..813439169 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -9,11 +9,11 @@ use Icinga\Module\Doc\DocController; class Doc_IcingawebController extends DocController { /** - * View toc of Icinga Web 2's documentation + * View the toc of Icinga Web 2's documentation */ public function tocAction() { - $this->populateToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2'); + $this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter'); } /** @@ -25,8 +25,19 @@ class Doc_IcingawebController extends DocController { $chapterName = $this->getParam('chapterName'); if ($chapterName === null) { - throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); + throw new Zend_Controller_Action_Exception( + $this->translate('Missing parameter \'chapterName\''), + 404 + ); } - $this->populateChapter($chapterName, Icinga::app()->getApplicationDir('/../doc')); + $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterName, 'doc/icingaweb/chapter'); + } + + /** + * View Icinga Web 2's documentation as PDF + */ + public function pdfAction() + { + $this->renderPdf(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter'); } } diff --git a/modules/doc/application/controllers/IndexController.php b/modules/doc/application/controllers/IndexController.php index 63b5e8cdf..c83cfabab 100644 --- a/modules/doc/application/controllers/IndexController.php +++ b/modules/doc/application/controllers/IndexController.php @@ -6,7 +6,5 @@ use Icinga\Module\Doc\DocController; class Doc_IndexController extends DocController { - public function indexAction() - { - } + public function indexAction() {} } diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 2ce1b1e73..b04a56400 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -30,24 +30,36 @@ class Doc_ModuleController extends DocController * * @param $moduleName * - * @throws Zend_Controller_Action_Exception + * @throws Zend_Controller_Action_Exception If the required parameter 'moduleName' is empty or either if the + * given module is neither installed nor enabled */ protected function assertModuleEnabled($moduleName) { - if ($moduleName === null) { - throw new Zend_Controller_Action_Exception('Missing parameter "moduleName"', 404); + if (empty($moduleName)) { + throw new Zend_Controller_Action_Exception( + $this->translate('Missing parameter \'moduleName\''), + 404 + ); } $moduleManager = Icinga::app()->getModuleManager(); if (! $moduleManager->hasInstalled($moduleName)) { - throw new Zend_Controller_Action_Exception('Module ' . $moduleName . ' is not installed', 404); + throw new Zend_Controller_Action_Exception( + $this->translate('Module') . ' \'' . $moduleName . '\' ' . $this->translate('is not installed'), + 404 + ); } if (! $moduleManager->hasEnabled($moduleName)) { - throw new Zend_Controller_Action_Exception('Module ' . $moduleName. ' is not enabled', 404); + throw new Zend_Controller_Action_Exception( + $this->translate('Module') . ' \'' . $moduleName . '\' ' . $this->translate('is not enabled'), + 404 + ); } } /** - * View toc of a module's documentation + * View the toc of a module's documentation + * + * @see assertModuleEnabled() */ public function tocAction() { @@ -55,7 +67,12 @@ class Doc_ModuleController extends DocController $this->assertModuleEnabled($moduleName); $moduleManager = Icinga::app()->getModuleManager(); try { - $this->populateToc($moduleManager->getModuleDir($moduleName, '/doc'), $moduleName); + $this->renderToc( + $moduleManager->getModuleDir($moduleName, '/doc'), + $moduleName, + 'doc/module/chapter', + array('moduleName' => $moduleName) + ); } catch (DocException $e) { throw new Zend_Controller_Action_Exception($e->getMessage(), 404); } @@ -65,22 +82,50 @@ class Doc_ModuleController extends DocController /** * View a chapter of a module's documentation * - * @throws Zend_Controller_Action_Exception + * @throws Zend_Controller_Action_Exception If the required parameter 'chapterName' is missing or if an error in + * the documentation module's library occurs + * @see assertModuleEnabled() */ public function chapterAction() { $moduleName = $this->getParam('moduleName'); $this->assertModuleEnabled($moduleName); - $chapterName = $this->getParam('chapterName'); - if ($chapterName === null) { - throw new Zend_Controller_Action_Exception('Missing parameter "chapterName"', 404); + $chapterTitle = $this->getParam('chapterName'); + if ($chapterTitle === null) { + throw new Zend_Controller_Action_Exception( + $this->translate('Missing parameter \'chapterName\''), + 404 + ); } $moduleManager = Icinga::app()->getModuleManager(); try { - $this->populateChapter($chapterName, $moduleManager->getModuleDir($moduleName, '/doc')); + $this->renderChapter( + $moduleManager->getModuleDir($moduleName, '/doc'), + $chapterTitle, + 'doc/module/chapter', + array('moduleName' => $moduleName) + ); } catch (DocException $e) { throw new Zend_Controller_Action_Exception($e->getMessage(), 404); } $this->view->moduleName = $moduleName; } + + /** + * View a module's documentation as PDF + * + * @see assertModuleEnabled() + */ + public function pdfAction() + { + $moduleName = $this->getParam('moduleName'); + $this->assertModuleEnabled($moduleName); + $moduleManager = Icinga::app()->getModuleManager(); + $this->renderPdf( + $moduleManager->getModuleDir($moduleName, '/doc'), + $moduleName, + 'doc/module/chapter', + array('moduleName' => $moduleName) + ); + } } diff --git a/modules/doc/application/views/scripts/chapter.phtml b/modules/doc/application/views/scripts/chapter.phtml new file mode 100644 index 000000000..7657d69fb --- /dev/null +++ b/modules/doc/application/views/scripts/chapter.phtml @@ -0,0 +1,3 @@ +
      + render($this, $this->getHelper('Url')); ?> +
      diff --git a/modules/doc/application/views/scripts/icingaweb/chapter.phtml b/modules/doc/application/views/scripts/icingaweb/chapter.phtml deleted file mode 100644 index 2ab744ebd..000000000 --- a/modules/doc/application/views/scripts/icingaweb/chapter.phtml +++ /dev/null @@ -1,40 +0,0 @@ -getHelper('Url'); -$view = $this; -?> -
      -[^>]*?\s+)?href="#(?P[^"]+)"/im', - function($match) use ($toc, $urlHelper, $view) { - if (($node = $toc->findNodeBy(function ($node) use ($match) { - $section = $node->getValue(); - if (($section->id === null && $section->chapterName === $match['fragment']) - || $section->id === $match['fragment'] - ) { - return true; - } - return false; - }))) { - $section = $node->getValue(); - $path = $urlHelper->url( - array('chapterName' => $section->chapterName), - 'doc/icingaweb/chapter', - false, - false - ); - $url = $view->url($path); - if ($section->id) { - $url->setAnchor($section->id); - } - return sprintf( - 'nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl() - ); - } - return $match[0]; - }, - $chapterHtml -); ?> -
      diff --git a/modules/doc/application/views/scripts/icingaweb/toc.phtml b/modules/doc/application/views/scripts/icingaweb/toc.phtml deleted file mode 100644 index bf91ffc77..000000000 --- a/modules/doc/application/views/scripts/icingaweb/toc.phtml +++ /dev/null @@ -1,23 +0,0 @@ -
      -

      documentation

      -
      -
      diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml index 3cebdca69..e4218bee2 100644 --- a/modules/doc/application/views/scripts/index/index.phtml +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -1,5 +1,6 @@ -

      Available documentations

      +
      +

      translate('Available documentations'); ?>

      diff --git a/modules/doc/application/views/scripts/module/chapter.phtml b/modules/doc/application/views/scripts/module/chapter.phtml deleted file mode 100644 index 3a809209f..000000000 --- a/modules/doc/application/views/scripts/module/chapter.phtml +++ /dev/null @@ -1,40 +0,0 @@ -getHelper('Url'); -$view = $this; -?> -
      -[^>]*?\s+)?href="#(?P[^"]+)"/im', - function($match) use ($toc, $moduleName, $urlHelper, $view) { - if (($node = $toc->findNodeBy(function ($node) use ($match) { - $section = $node->getValue(); - if (($section->id === null && $section->chapterName === $match['fragment']) - || $section->id === $match['fragment'] - ) { - return true; - } - return false; - }))) { - $section = $node->getValue(); - $path = $urlHelper->url( - array('moduleName' => $moduleName, 'chapterName' => $section->chapterName), - 'doc/module/chapter', - false, - false - ); - $url = $view->url($path); - if ($section->id) { - $url->setAnchor($section->id); - } - return sprintf( - 'nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl() - ); - } - return $match[0]; - }, - $chapterHtml -); ?> -
      diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml index 0755748c7..cc184016f 100644 --- a/modules/doc/application/views/scripts/module/index.phtml +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -1,4 +1,4 @@ -

      Module documentations

      +

      translate('Module documentations'); ?>

      • diff --git a/modules/doc/application/views/scripts/module/toc.phtml b/modules/doc/application/views/scripts/module/toc.phtml deleted file mode 100644 index ec33c507c..000000000 --- a/modules/doc/application/views/scripts/module/toc.phtml +++ /dev/null @@ -1,25 +0,0 @@ -
        -

        documentation

        -
        -
        - getHelper('Url'); - $view = $this; - ?> - render(function ($node) use ($urlHelper, $view, $moduleName) { - $section = $node->getValue(); - $path = $urlHelper->url( - array('moduleName' => $moduleName, 'chapterName' => $section->chapterName), 'doc/module/chapter', false, false - ); - $url = $view->url($path); - if ($section->id) { - $url->setAnchor($section->id); - } - return sprintf( - '
      • %s
      • ', - $section->nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl(), - $section->title - ); - }); ?> - diff --git a/modules/doc/application/views/scripts/pdf.phtml b/modules/doc/application/views/scripts/pdf.phtml new file mode 100644 index 000000000..72d77f3c0 --- /dev/null +++ b/modules/doc/application/views/scripts/pdf.phtml @@ -0,0 +1,7 @@ +

        translate('Documentation'); ?>

        +
        + render($this, $this->getHelper('Url')); ?> +
        +
        + render($this, $this->getHelper('Url')); ?> +
        diff --git a/modules/doc/application/views/scripts/toc.phtml b/modules/doc/application/views/scripts/toc.phtml new file mode 100644 index 000000000..8ffe2820d --- /dev/null +++ b/modules/doc/application/views/scripts/toc.phtml @@ -0,0 +1,6 @@ +
        +

        translate('Documentation'); ?>

        +
        +
        + render($this, $this->getHelper('Url')); ?> +
        diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 88fd7476e..de2a7bac9 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -4,35 +4,67 @@ namespace Icinga\Module\Doc; -use Icinga\Data\Tree\NodeRenderer; use Icinga\Web\Controller\ModuleActionController; class DocController extends ModuleActionController { /** - * Populate a chapter + * Render a chapter * - * @param string $chapterName Name of the chapter - * @param string $path Path to the documentation + * @param string $path Path to the documentation + * @param string $chapterTitle Title of the chapter + * @param string $url + * @param array $urlParams */ - protected function populateChapter($chapterName, $path) + protected function renderChapter($path, $chapterTitle, $url, array $urlParams = array()) { $parser = new DocParser($path); - $this->view->chapterHtml = $parser->getChapter($chapterName); - $this->view->toc = $parser->getToc(); + $this->view->sectionRenderer = new SectionRenderer( + $parser->getDocTree(), + SectionRenderer::decodeUrlParam($chapterTitle), + $url, + $urlParams + ); + $this->_helper->viewRenderer('chapter', null, true); } /** - * Populate toc + * Render a toc * - * @param string $path Path to the documentation - * @param string $name Name of the documentation + * @param string $path Path to the documentation + * @param string $name Name of the documentation + * @param string $url + * @param array $urlParams */ - protected function populateToc($path, $name) + protected function renderToc($path, $name, $url, array $urlParams = array()) { $parser = new DocParser($path); - $toc = $parser->getToc(); - $this->view->tocRenderer = new NodeRenderer($toc); + $this->view->tocRenderer = new TocRenderer($parser->getDocTree(), $url, $urlParams); $this->view->docName = $name; + $this->_helper->viewRenderer('toc', null, true); + } + + /** + * Render a pdf + * + * @param string $path Path to the documentation + * @param string $name Name of the documentation + * @param string $url + * @param array $urlParams + */ + protected function renderPdf($path, $name, $url, array $urlParams = array()) + { + $parser = new DocParser($path); + $docTree = $parser->getDocTree(); + $this->view->tocRenderer = new TocRenderer($docTree, $url, $urlParams); + $this->view->sectionRenderer = new SectionRenderer( + $docTree, + null, + $url, + $urlParams + ); + $this->view->docName = $name; + $this->_helper->viewRenderer('pdf', null, true); + $this->_request->setParam('format', 'pdf'); } } From a0867ce33afbcba1233e78918cd6473022b5164d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Jul 2014 19:26:39 +0200 Subject: [PATCH 108/184] doc module/Vagrant: Add menu.ini refs #4820 --- .vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini diff --git a/.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini b/.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini new file mode 100644 index 000000000..86889b239 --- /dev/null +++ b/.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini @@ -0,0 +1,5 @@ +[Documentation] +title = "Documentation" +icon = "img/icons/comment.png" +url = "doc" +priority = 80 From 8b9d446d2ed766f9c8793d6751969f642ca77940 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Tue, 29 Jul 2014 10:42:43 +0200 Subject: [PATCH 109/184] Autologin: Remove deprecated autologin methods Remove methods from manager because autologin is now handled with special backends (AutoLoginBackend). The session is used to store the status about a remote user authentication to send a 401 header to the client upon logout. refs #6461 --- .../controllers/AuthenticationController.php | 9 +++++- library/Icinga/Authentication/Manager.php | 31 ------------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index bbb475989..47825c210 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -14,6 +14,7 @@ use Icinga\Exception\AuthenticationException; use Icinga\Exception\NotReadableError; use Icinga\Exception\ConfigurationError; use Icinga\User; +use Icinga\Web\Session; use Icinga\Web\Url; /** @@ -67,6 +68,9 @@ class AuthenticationController extends ActionController $authenticated = $backend->authenticate($user); if ($authenticated === true) { $auth->setAuthenticated($user); + $session = Session::getSession()->getNamespace('authentication'); + $session->set('is_remote_user', true); + $session->write(); $this->rerenderLayout()->redirectNow($redirectUrl); } } @@ -131,9 +135,12 @@ class AuthenticationController extends ActionController public function logoutAction() { $auth = $this->Auth(); + + $session = Session::getSession()->getNamespace('authentication'); + $auth->removeAuthorization(); - if ($auth->isAuthenticatedFromRemoteUser()) { + if ($session->get('is_remote_user', false) === true) { $this->_helper->layout->setLayout('login'); $this->_response->setHttpResponseCode(401); } else { diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index 01964ef00..a0465d306 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -204,35 +204,4 @@ class Manager { return $this->user->getGroups(); } - - /** - * Tries to authenticate the user from the session, and then from the REMOTE_USER superglobal, that can be set by - * an external authentication provider. - */ - public function authenticateFromRemoteUser() - { - if (array_key_exists('REMOTE_USER', $_SERVER)) { - $this->fromRemoteUser = true; - } - $this->authenticateFromSession(); - if ($this->user !== null) { - if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) { - // Remote user has changed, clear all sessions - $this->removeAuthorization(); - } - return; - } - if (array_key_exists('REMOTE_USER', $_SERVER) && $_SERVER["REMOTE_USER"]) { - $this->user = new User($_SERVER["REMOTE_USER"]); - $this->persistCurrentUser(); - } - } - - /** - * If the session was established from the REMOTE_USER server variable. - */ - public function isAuthenticatedFromRemoteUser() - { - return $this->fromRemoteUser; - } } From 55d3818ebb19379c586d5045a2a0ec39e95f50e9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 11:10:06 +0200 Subject: [PATCH 110/184] doc/SectionRenderer: Support images refs #4820 --- modules/doc/library/Doc/SectionRenderer.php | 28 ++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index 012d50678..c7e6c6ffb 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -6,10 +6,13 @@ namespace Icinga\Module\Doc; require_once 'IcingaVendor/Parsedown/Parsedown.php'; -use Icinga\Module\Doc\Exception\ChapterNotFoundException; +use DOMDocument; +use DOMXPath; use RecursiveIteratorIterator; use Parsedown; use Zend_View_Helper_Url; +use Icinga\Module\Doc\Exception\ChapterNotFoundException; +use Icinga\Web\Url; use Icinga\Web\View; /** @@ -156,6 +159,24 @@ class SectionRenderer extends Renderer return '
        ' . highlight_string(htmlspecialchars_decode($match[1]), true) . '
        '; } + /** + * Replace img src tags + * + * @param $match + * + * @return string + */ + protected function replaceImg($match) + { + $doc = new DOMDocument(); + $doc->loadHTML($match[0]); + $xpath = new DOMXPath($doc); + $img = $xpath->query('//img[1]')->item(0); + /* @var $img \DOMElement */ + $img->setAttribute('src', Url::fromPath($img->getAttribute('src'))->getAbsoluteUrl()); + return substr_replace($doc->saveXML($img), '', -2, 1); // Replace '/>' with '>' + } + /** * Render the section * @@ -181,6 +202,11 @@ class SectionRenderer extends Renderer array($this, 'highlightPhp'), $this->parsedown->text(implode('', $section->getContent())) ); + $html = preg_replace_callback( + '/]+>/', + array($this, 'replaceImg'), + $html + ); $content[] = preg_replace_callback( '/[^>]*?\s+)?href="#(?P[^"]+)"/', array($callback, 'render'), From 38a6df91b9e0f2fd3b5210b70b16a67b04856b66 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 11:10:49 +0200 Subject: [PATCH 111/184] doc/DocParser: Rename 'chapterName' to 'chapterTitle' refs #4820 --- modules/doc/library/Doc/DocParser.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index b74a0b377..12ec373fc 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -150,12 +150,12 @@ class DocParser $nofollow = false; } if ($stack->isEmpty()) { - $chapterName = $header; - $section = new Section($id, $header, $level, $nofollow, $chapterName); + $chapterTitle = $header; + $section = new Section($id, $header, $level, $nofollow, $chapterTitle); $tree->addRoot($section); } else { - $chapterName = $stack->bottom()->getTitle(); - $section = new Section($id, $header, $level, $nofollow, $chapterName); + $chapterTitle = $stack->bottom()->getTitle(); + $section = new Section($id, $header, $level, $nofollow, $chapterTitle); $tree->addChild($section, $stack->top()); } $stack->push($section); From 3cafc8910a8ee5daae4400b48f630b8820ef6d09 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 11:12:06 +0200 Subject: [PATCH 112/184] doc/DocParser: Rename 'header' to 'title' refs #4820 --- modules/doc/library/Doc/DocParser.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 12ec373fc..d543f9f91 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -131,9 +131,7 @@ class DocParser foreach ($file as $line) { $header = $this->extractHeader($line, $lastLine); if ($header !== null) { - list($header, $id, $level) = $header; // When overwriting the variable to extract, it has to be - // list()'s first parameter since list() assigns the values - // starting with the right-most parameter + list($title, $id, $level) = $header; while (! $stack->isEmpty() && $stack->top()->getLevel() >= $level) { $stack->pop(); } @@ -143,19 +141,19 @@ class DocParser /* @var $section Section */ $path[] = $section->getTitle(); } - $path[] = $header; + $path[] = $title; $id = implode('-', $path); $nofollow = true; } else { $nofollow = false; } if ($stack->isEmpty()) { - $chapterTitle = $header; - $section = new Section($id, $header, $level, $nofollow, $chapterTitle); + $chapterTitle = $title; + $section = new Section($id, $title, $level, $nofollow, $chapterTitle); $tree->addRoot($section); } else { $chapterTitle = $stack->bottom()->getTitle(); - $section = new Section($id, $header, $level, $nofollow, $chapterTitle); + $section = new Section($id, $title, $level, $nofollow, $chapterTitle); $tree->addChild($section, $stack->top()); } $stack->push($section); From d05e3bfcca5a18bf862ef69af07a5014db3f997b Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Tue, 29 Jul 2014 11:11:52 +0200 Subject: [PATCH 113/184] Autologin: Fix javascript logout code Window onload event handler comes to late. Change part of the code to use jQuery. refs #6461 --- .../views/scripts/authentication/logout.phtml | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/application/views/scripts/authentication/logout.phtml b/application/views/scripts/authentication/logout.phtml index eb0d6dc44..9b03d190f 100644 --- a/application/views/scripts/authentication/logout.phtml +++ b/application/views/scripts/authentication/logout.phtml @@ -7,10 +7,7 @@ in every further request until the browser was closed. To allow logout and to allow the user to change the logged-in user this JavaScript provides a workaround to force a new authentication prompt in most browsers. --> - -
        -
        -
        +

        -
        -
        -
        -
        - +
        -
        - From f6b9b13654f7f6e8d43ccd3c8b4b76d57b809ce2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 11:19:40 +0200 Subject: [PATCH 114/184] doc: Rename 'chapterName' to 'chapterTitle' refs #4820 --- .../doc/application/controllers/IcingawebController.php | 8 ++++---- modules/doc/application/controllers/ModuleController.php | 6 +++--- modules/doc/library/Doc/SectionRenderer.php | 2 +- modules/doc/library/Doc/TocRenderer.php | 2 +- modules/doc/run.php | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 813439169..55f60d36e 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -23,14 +23,14 @@ class Doc_IcingawebController extends DocController */ public function chapterAction() { - $chapterName = $this->getParam('chapterName'); - if ($chapterName === null) { + $chapterTitle = $this->getParam('chapterTitle'); + if ($chapterTitle === null) { throw new Zend_Controller_Action_Exception( - $this->translate('Missing parameter \'chapterName\''), + $this->translate('Missing parameter \'chapterTitle\''), 404 ); } - $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterName, 'doc/icingaweb/chapter'); + $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterTitle, 'doc/icingaweb/chapter'); } /** diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index b04a56400..3a8bfc6af 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -82,7 +82,7 @@ class Doc_ModuleController extends DocController /** * View a chapter of a module's documentation * - * @throws Zend_Controller_Action_Exception If the required parameter 'chapterName' is missing or if an error in + * @throws Zend_Controller_Action_Exception If the required parameter 'chapterTitle' is missing or if an error in * the documentation module's library occurs * @see assertModuleEnabled() */ @@ -90,10 +90,10 @@ class Doc_ModuleController extends DocController { $moduleName = $this->getParam('moduleName'); $this->assertModuleEnabled($moduleName); - $chapterTitle = $this->getParam('chapterName'); + $chapterTitle = $this->getParam('chapterTitle'); if ($chapterTitle === null) { throw new Zend_Controller_Action_Exception( - $this->translate('Missing parameter \'chapterName\''), + $this->translate('Missing parameter \'chapterTitle\''), 404 ); } diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index c7e6c6ffb..31143e3c8 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -57,7 +57,7 @@ class Callback array_merge( $this->urlParams, array( - 'chapterName' => SectionRenderer::encodeUrlParam($section->getChapterTitle()) + 'chapterTitle' => SectionRenderer::encodeUrlParam($section->getChapterTitle()) ) ), $this->url, diff --git a/modules/doc/library/Doc/TocRenderer.php b/modules/doc/library/Doc/TocRenderer.php index 8264c2d8e..f018c556d 100644 --- a/modules/doc/library/Doc/TocRenderer.php +++ b/modules/doc/library/Doc/TocRenderer.php @@ -85,7 +85,7 @@ class TocRenderer extends Renderer array_merge( $this->urlParams, array( - 'chapterName' => $this->encodeUrlParam($section->getChapterTitle()) + 'chapterTitle' => $this->encodeUrlParam($section->getChapterTitle()) ) ), $this->url, diff --git a/modules/doc/run.php b/modules/doc/run.php index eaab1cb05..959b56095 100644 --- a/modules/doc/run.php +++ b/modules/doc/run.php @@ -8,7 +8,7 @@ if (Icinga::app()->isCli()) { } $docModuleChapter = new Zend_Controller_Router_Route( - 'doc/module/:moduleName/chapter/:chapterName', + 'doc/module/:moduleName/chapter/:chapterTitle', array( 'controller' => 'module', 'action' => 'chapter', @@ -17,7 +17,7 @@ $docModuleChapter = new Zend_Controller_Router_Route( ); $docIcingaWebChapter = new Zend_Controller_Router_Route( - 'doc/icingaweb/chapter/:chapterName', + 'doc/icingaweb/chapter/:chapterTitle', array( 'controller' => 'icingaweb', 'action' => 'chapter', From f9e8ad3d0b7cb9002976647a5bfcf60fb6dbd45c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 11:45:16 +0200 Subject: [PATCH 115/184] doc: Remove 'locale' directory Since #6432 has been fixed, the empty locale directory can be removed. refs #4820 --- modules/doc/application/locale/.gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 modules/doc/application/locale/.gitkeep diff --git a/modules/doc/application/locale/.gitkeep b/modules/doc/application/locale/.gitkeep deleted file mode 100644 index 9fc47527c..000000000 --- a/modules/doc/application/locale/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -Remove this file and the locale directory once #6432 has been fixed. From 64954e99244f26cc6b6dccc7d60a253c105bd550 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Tue, 29 Jul 2014 12:06:43 +0200 Subject: [PATCH 116/184] Autologin: Test logged session against remote user fixes #6462 --- library/Icinga/Authentication/Manager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index a0465d306..7009d51ab 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -117,6 +117,13 @@ class Manager public function authenticateFromSession() { $this->user = Session::getSession()->get('user'); + + if ($this->user + && array_key_exists('REMOTE_USER', $_SERVER) + && $this->user->getUsername() !== $_SERVER['REMOTE_USER'] + ) { + $this->removeAuthorization(); + } } /** From 0b1aee8a35a1b8fd0f88843bf2d12f997db7a951 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 29 Jul 2014 13:04:20 +0200 Subject: [PATCH 117/184] Vagrant: Fix Icinga 2 MySQL creation refs #6741 --- .vagrant-puppet/manifests/default.pp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.vagrant-puppet/manifests/default.pp b/.vagrant-puppet/manifests/default.pp index 68affd347..c05743719 100644 --- a/.vagrant-puppet/manifests/default.pp +++ b/.vagrant-puppet/manifests/default.pp @@ -5,8 +5,8 @@ include openldap Exec { path => '/bin:/usr/bin:/sbin' } -$icingaVersion = '1.11.2' -$icinga2Version = '2.0.0' +$icingaVersion = '1.11.5' +$icinga2Version = '2.0.1' exec { 'create-mysql-icinga-db': unless => 'mysql -uicinga -picinga icinga', @@ -421,11 +421,10 @@ package { 'icinga2-ido-mysql': exec { 'populate-icinga2-mysql-db': unless => 'mysql -uicinga2 -picinga2 icinga2 -e "SELECT * FROM icinga_dbversion;" &> /dev/null', - command => "mysql -uroot icinga2 < /usr/share/doc/icinga2-ido-mysql-$icinga2Version/schema/mysql.sql", + command => 'mysql -uroot icinga2 < /usr/share/doc/icinga2-ido-mysql-$(rpm -q icinga2-ido-mysql | cut -d\'-\' -f4)/schema/mysql.sql', require => [ Exec['create-mysql-icinga2-db'], Package['icinga2-ido-mysql'] ] } - file { '/etc/icinga2/features-available/ido-mysql.conf': source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icinga2/features-available/ido-mysql.conf', owner => 'icinga', From 294728ac47df056121500ffe341c71a3de662ad5 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Jul 2014 17:50:44 +0200 Subject: [PATCH 118/184] Revert "Autologin: Test logged session against remote user" This reverts commit 64954e99244f26cc6b6dccc7d60a253c105bd550. If the strip_username_regex is configured on the autologin backend and applies on a user's name, the authenticated user's username does never match the REMOTE_USER server variable. Thus the application will logout/login on every request which results in a redirect loop. refs #6462 --- library/Icinga/Authentication/Manager.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index 7009d51ab..a0465d306 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -117,13 +117,6 @@ class Manager public function authenticateFromSession() { $this->user = Session::getSession()->get('user'); - - if ($this->user - && array_key_exists('REMOTE_USER', $_SERVER) - && $this->user->getUsername() !== $_SERVER['REMOTE_USER'] - ) { - $this->removeAuthorization(); - } } /** From e2c761a7aa9a7fe6f670862ddaf9945e983cf2ff Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 30 Jul 2014 12:35:55 +0200 Subject: [PATCH 119/184] AutoLogin/Logout: Remove own session namespace Store data in the user and implement interface to left backends store remote information. fixes #6461 --- .../controllers/AuthenticationController.php | 9 +--- .../Backend/AutoLoginBackend.php | 1 + library/Icinga/Authentication/Manager.php | 6 --- library/Icinga/User.php | 43 +++++++++++++++++++ 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index 47825c210..7d4864d5a 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -68,9 +68,6 @@ class AuthenticationController extends ActionController $authenticated = $backend->authenticate($user); if ($authenticated === true) { $auth->setAuthenticated($user); - $session = Session::getSession()->getNamespace('authentication'); - $session->set('is_remote_user', true); - $session->write(); $this->rerenderLayout()->redirectNow($redirectUrl); } } @@ -135,12 +132,10 @@ class AuthenticationController extends ActionController public function logoutAction() { $auth = $this->Auth(); - - $session = Session::getSession()->getNamespace('authentication'); - + $isRemoteUser = $auth->getUser()->isRemoteUser(); $auth->removeAuthorization(); - if ($session->get('is_remote_user', false) === true) { + if ($isRemoteUser === true) { $this->_helper->layout->setLayout('login'); $this->_response->setHttpResponseCode(401); } else { diff --git a/library/Icinga/Authentication/Backend/AutoLoginBackend.php b/library/Icinga/Authentication/Backend/AutoLoginBackend.php index d793b50dd..16373bb6c 100644 --- a/library/Icinga/Authentication/Backend/AutoLoginBackend.php +++ b/library/Icinga/Authentication/Backend/AutoLoginBackend.php @@ -53,6 +53,7 @@ class AutoLoginBackend extends UserBackend { if (isset($_SERVER['REMOTE_USER'])) { $username = $_SERVER['REMOTE_USER']; + $user->setRemoteUserInformation($username, 'REMOTE_USER'); if ($this->stripUsernameRegexp !== null) { $stripped = preg_replace($this->stripUsernameRegexp, '', $username); if ($stripped !== false) { diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index a0465d306..3f381eaf9 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -30,12 +30,6 @@ class Manager */ private $user; - /** - * If the user was authenticated from the REMOTE_USER server variable - * - * @var Boolean - */ - private $fromRemoteUser = false; private function __construct() { diff --git a/library/Icinga/User.php b/library/Icinga/User.php index 848877850..13f62881a 100644 --- a/library/Icinga/User.php +++ b/library/Icinga/User.php @@ -58,6 +58,18 @@ class User */ protected $additionalInformation = array(); + /** + * Information if the user is external authenticated + * + * Keys: + * + * 0: origin username + * 1: origin field name + * + * @var array + */ + protected $remoteUserInformation = array(); + /** * Set of permissions * @@ -401,4 +413,35 @@ class User { $this->messages = null; } + + /** + * Set additional remote user information + * + * @param stirng $username + * @param string $field + */ + public function setRemoteUserInformation($username, $field) + { + $this->remoteUserInformation = array($username, $field); + } + + /** + * Get additional remote user information + * + * @return array + */ + public function getRemoteUserInformation() + { + return $this->remoteUserInformation; + } + + /** + * Return true if user has remote user information set + * + * @return bool + */ + public function isRemoteUser() + { + return (count($this->remoteUserInformation)) ? true : false; + } } From 56a29354d3e49c954b12aaefac101d5a589b648e Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 30 Jul 2014 12:54:08 +0200 Subject: [PATCH 120/184] AutoLogin: Check the remote username against logged in user fixes #6462 --- library/Icinga/Authentication/Manager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index 3f381eaf9..ec49aa416 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -111,6 +111,13 @@ class Manager public function authenticateFromSession() { $this->user = Session::getSession()->get('user'); + + if ($this->user !== null && $this->user->isRemoteUser() === true) { + list($originUsername, $field) = $this->user->getRemoteUserInformation(); + if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) { + $this->removeAuthorization(); + } + } } /** From 3e4944c60a25e8c1015704cb1d87db051fd9c422 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 30 Jul 2014 14:04:57 +0200 Subject: [PATCH 121/184] Vagrant: Update monitoring-plugins/livestatus Move all hardcoded versions to the header too. fixes #6818 --- .vagrant-puppet/manifests/default.pp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.vagrant-puppet/manifests/default.pp b/.vagrant-puppet/manifests/default.pp index c05743719..3f77ab870 100644 --- a/.vagrant-puppet/manifests/default.pp +++ b/.vagrant-puppet/manifests/default.pp @@ -7,6 +7,10 @@ Exec { path => '/bin:/usr/bin:/sbin' } $icingaVersion = '1.11.5' $icinga2Version = '2.0.1' +$pluginVersion = '2.0' +$livestatusVersion = '1.2.4p5' +$phantomjsVersion = '1.9.1' +$casperjsVersion = '1.0.2' exec { 'create-mysql-icinga-db': unless => 'mysql -uicinga -picinga icinga', @@ -207,8 +211,8 @@ exec { 'icinga-htpasswd': } cmmi { 'icinga-plugins': - url => 'https://www.monitoring-plugins.org/download/nagios-plugins-1.5.tar.gz', - output => 'nagios-plugins-1.5.tar.gz', + url => "https://www.monitoring-plugins.org/download/monitoring-plugins-${pluginVersion}.tar.gz", + output => "monitoring-plugins-${pluginVersion}.tar.gz", flags => '--prefix=/usr/lib64/nagios/plugins \ --with-nagios-user=icinga --with-nagios-group=icinga \ --with-cgiurl=/icinga-mysql/cgi-bin', @@ -218,8 +222,8 @@ cmmi { 'icinga-plugins': } cmmi { 'mk-livestatus': - url => 'http://mathias-kettner.de/download/mk-livestatus-1.2.2p1.tar.gz', - output => 'mk-livestatus-1.2.2p1.tar.gz', + url => "http://mathias-kettner.de/download/mk-livestatus-${livestatusVersion}.tar.gz", + output => "mk-livestatus-${livestatusVersion}.tar.gz", flags => '--prefix=/usr/local/icinga-mysql --exec-prefix=/usr/local/icinga-mysql', creates => '/usr/local/icinga-mysql/lib/mk-livestatus', make => 'make && make install', @@ -262,14 +266,14 @@ exec { 'populate-openldap': } class { 'phantomjs': - url => 'https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2', - output => 'phantomjs-1.9.1-linux-x86_64.tar.bz2', + url => "https://phantomjs.googlecode.com/files/phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2", + output => "phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2", creates => '/usr/local/phantomjs' } class { 'casperjs': - url => 'https://github.com/n1k0/casperjs/tarball/1.0.2', - output => 'casperjs-1.0.2.tar.gz', + url => "https://github.com/n1k0/casperjs/tarball/${casperjsVersion}", + output => "casperjs-${casperjsVersion}.tar.gz", creates => '/usr/local/casperjs' } From 51484ebf88ec71b50acf60727b4a442543b9a9b0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 31 Jul 2014 16:52:34 +0200 Subject: [PATCH 122/184] monitoring/Perfdata: ignore invalid min/max key=23;0;0;0;0 and key=23;;;; lead to division by zero exceptions This should fix this by ignoring empty strings for min/max (formerly it got "converted" to float(0)) and also ignores min == max. fixes #6828 --- .../monitoring/library/Monitoring/Plugin/Perfdata.php | 11 +++++++++-- .../php/library/Monitoring/Plugin/PerfdataTest.php | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php index a6eaddabd..6af3cde17 100644 --- a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php +++ b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php @@ -190,6 +190,9 @@ class Perfdata if ($this->maxValue !== null) { $minValue = $this->minValue !== null ? $this->minValue : 0; + if ($this->maxValue - $minValue === 0.0) { + return null; + } if ($this->value > $minValue) { return (($this->value - $minValue) / ($this->maxValue - $minValue)) * 100; @@ -267,9 +270,13 @@ class Perfdata switch (count($parts)) { case 5: - $this->maxValue = self::convert($parts[4], $this->unit); + if ($parts[4] !== '') { + $this->maxValue = self::convert($parts[4], $this->unit); + } case 4: - $this->minValue = self::convert($parts[3], $this->unit); + if ($parts[3] !== '') { + $this->minValue = self::convert($parts[3], $this->unit); + } case 3: // TODO(#6123): Tresholds have the same UOM and need to be converted as well! $this->criticalThreshold = trim($parts[2]) ? trim($parts[2]) : null; diff --git a/modules/monitoring/test/php/library/Monitoring/Plugin/PerfdataTest.php b/modules/monitoring/test/php/library/Monitoring/Plugin/PerfdataTest.php index 2d8a98b73..9cc132eb2 100644 --- a/modules/monitoring/test/php/library/Monitoring/Plugin/PerfdataTest.php +++ b/modules/monitoring/test/php/library/Monitoring/Plugin/PerfdataTest.php @@ -347,6 +347,14 @@ class PerfdataTest extends BaseTestCase Perfdata::fromString('test=25;;;50;100')->getPercentage(), 'Perfdata objects do return a percentage though their value is lower than it\'s allowed minimum' ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;')->getPercentage(), + 'Perfdata objects do not ignore empty max values when returning percentages' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;0')->getPercentage(), + 'Perfdata objects do not ignore impossible min/max combinations when returning percentages' + ); } /** From 8f729bfb6545cd07b659e9bf84ac20ab30de87cb Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 31 Jul 2014 17:04:26 +0200 Subject: [PATCH 123/184] ActionController: set a default window title In case a controller action didn't set a view title this fix restores the default title on XHR requests. fixes #6797 --- library/Icinga/Web/Controller/ActionController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index b28bddc37..1c4111e4b 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -362,6 +362,8 @@ class ActionController extends Zend_Controller_Action 'X-Icinga-Title', rawurlencode($this->view->title . ' :: Icinga Web') ); + } else { + $resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web')); } if ($this->rerenderLayout) { From 96925487bddab8b9471d5ffe1e5042f76e140acc Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 31 Jul 2014 17:24:56 +0200 Subject: [PATCH 124/184] CLI/monitoring/list: fix DataView-related exception This uses the renamed DataView serviceStatus instead of the former status and fixes the problem that CLI still assumed to have fetch and count function on the DataView. fixes #6777 --- modules/monitoring/application/clicommands/ListCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/application/clicommands/ListCommand.php b/modules/monitoring/application/clicommands/ListCommand.php index 42a377763..e6b86f4ef 100644 --- a/modules/monitoring/application/clicommands/ListCommand.php +++ b/modules/monitoring/application/clicommands/ListCommand.php @@ -72,6 +72,7 @@ class ListCommand extends Command protected function showFormatted($query, $format, $columns) { + $query = $query->getQuery(); switch($format) { case 'json': echo json_encode($query->fetchAll()); @@ -155,7 +156,7 @@ class ListCommand extends Command 'service_perfdata', 'service_last_state_change' ); - $query = $this->getQuery('status', $columns) + $query = $this->getQuery('serviceStatus', $columns) ->order('host_name'); echo $this->renderStatusQuery($query); } @@ -167,6 +168,7 @@ class ListCommand extends Command $screen = $this->screen; $utils = new CliUtils($screen); $maxCols = $screen->getColumns(); + $query = $query->getQuery(); $rows = $query->fetchAll(); $count = $query->count(); $count = count($rows); From d813bad2f8c9c58f5ad5646269a3268284c951fc Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 11 Aug 2014 17:21:43 +0200 Subject: [PATCH 125/184] Update README.md with general & install info We don't want users to install it in production environments but we certainly also want to prevent them from doing so by calling `vagrant up`. Therefore the native method described in doc/installation.md works for now until there are proper installation methods (packages, etc). Add support hints, and updated Icinga 2 paths as well. fixes #6880 --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5974d5de0..16eb1444a 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,59 @@ ## Table of Contents -1. [Vagrant - Virtual development environment](#vagrant) +0. [General Information](#general information) +1. [Installation](#installation) +2. [Support](#support) +3. [Vagrant - Virtual development environment](#vagrant) + +## General Information + +`Icinga Web 2` is the next generation monitoring web interface, framework +and CLI tool developed by the [Icinga Project](https://www.icinga.org/community/team/). + +Responsive and fast, rewritten from scratch supporting multiple backends and +providing a CLI tool. Compatible with Icinga Core 2.x and 1.x. + +Check the Icinga website for some [insights](https://www.icinga.org/icinga/screenshots/icinga-web-2/). + +> **Note** +> +> `Icinga Web 2` is still in development and not meant for production deployment. +> Watch the [development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap) +> and [Icinga website](https://www.icinga.org/) for release schedule updates! + +## Installation + +Please navigate to [doc/installation.md](doc/installation.md) for updated details. + +## Support + +Please head over to the [community support channels](https://www.icinga.org/icinga/faq/get-help/) +in case of questions, bugs, etc. + +Please make sure to provide the following details: + +* OS, distribution, version +* PHP and/or MySQL/PostgreSQL version +* Which browser and its version +* Screenshot and problem description + ## Vagrant -> **Note** that the deployment of the virtual machine is tested against Vagrant starting with version 1.1. +### Requirements + +* Vagrant 1.2+ +* Virtualbox 4.2.16+ +* a fairly powerful hardware (quad core, 4gb ram, fast hdd) + +> **Note** +> +> The deployment of the virtual machine is tested against Vagrant starting with version 1.2. > Unfortunately older versions will not work. +### General + The Icinga Web 2 project ships with a Vagrant virtual machine that integrates the source code with various services and example data in a controlled environment. This enables developers and users to test Livestatus, status.dat, @@ -17,7 +63,9 @@ have to do is install Vagrant and run: vagrant up -> **Note** that the first boot of the vm takes a fairly long time because +> **Note** +> +> The first boot of the vm takes a fairly long time because > you'll download a plain CentOS base box and Vagrant will automatically > provision the environment on the first go. @@ -68,7 +116,7 @@ After you should be able to browse [localhost:8080/icingaweb](http://localhost:8 **Installed files**: * `/usr/share/icinga/htpasswd.users` account information for logging into the Icinga classic web interface for both icinga instances -* `/usr/lib64/nagios/plugins` Nagios Plugins for both icinga instances +* `/usr/lib64/nagios/plugins` Monitoring Plugins for all Icinga instances #### Icinga with IDOUtils using a MySQL database @@ -196,12 +244,13 @@ code style issues. #### Icinga 2 -**Installation path**: `/usr/local/icinga2` +Installed from the Icinga [snapshot package repository](http://packages.icinga.org/epel/). +The configuration is located in `/etc/icinga2`. **Example usage**: - cd /usr/local/icinga2 - ./sbin/icinga2 -c etc/icinga2/icinga2.conf.dist + /etc/init.d/icinga2 (start|stop|restart|reload) + ## Log into Icinga Web 2 @@ -211,3 +260,9 @@ If you've configure LDAP as authentication backend (which is the default) use th > **Password**: password Have a look at [LDAP example data](#ldap example data) for more accounts. + +Using MySQL as backend: + +> **Username**: icingaadmin +> **Password**: icinga + From 143db976ef7cb36625b2676afe8a18e997d4e668 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 12 Aug 2014 09:56:43 +0200 Subject: [PATCH 126/184] Properly support Apache 2.2 and 2.4 Can be done by using directive. Tested on fc20, el7 and Wheezy. refs #6771 --- etc/apache/icingaweb.conf.in | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/etc/apache/icingaweb.conf.in b/etc/apache/icingaweb.conf.in index 0451abf5b..3884aea5a 100644 --- a/etc/apache/icingaweb.conf.in +++ b/etc/apache/icingaweb.conf.in @@ -3,10 +3,19 @@ Alias @web_path@ "@prefix@/public" Options SymLinksIfOwnerMatch AllowOverride None - Order allow,deny - Allow from all - # new directive needed in Apache 2.4.3+ - #Require all granted + + + # Apache 2.4 + + Require all granted + + + + + # Apache 2.2 + Order allow,deny + Allow from all + SetEnv ICINGAWEB_CONFIGDIR @icingaweb_config_path@ From 7958a81975130bf21f58fd572c1ae2dfb07c53c4 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 12 Aug 2014 12:49:03 +0200 Subject: [PATCH 127/184] Fix that a form's submit button is not being sent when the method is GET fixes #6890 --- public/js/icinga/events.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 8dcd0f516..6c6978577 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -355,12 +355,20 @@ $target = self.getLinkTargetFor($form); if (method === 'GET') { - url = icinga.utils.addUrlParams(url, $form.serializeObject()); + var dataObj = $form.serializeObject(); + + if (typeof autosubmit === 'undefined' || ! autosubmit) { + if ($button.length && $button.attr('name') !== 'undefined') { + dataObj[$button.attr('name')] = $button.attr('value'); + } + } + + url = icinga.utils.addUrlParams(url, dataObj); } else { data = $form.serializeArray(); if (typeof autosubmit === 'undefined' || ! autosubmit) { - if ($button.length) { + if ($button.length && $button.attr('name') !== 'undefined') { data.push({ name: $button.attr('name'), value: $button.attr('value') From 9dabc6dc9c93a5393c8cd77bc14b1fdb1af45e42 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 12 Aug 2014 13:10:45 +0200 Subject: [PATCH 128/184] Update rpm builds refs #6401 refs #6403 --- .gitignore | 3 + icingaweb2.spec | 77 +++++++--------- packages/rhel/README | 57 ------------ packages/rhel/etc/httpd/conf.d/icingaweb.conf | 12 --- .../rhel/usr/share/icingaweb/public/.htaccess | 7 -- .../rhel/usr/share/icingaweb/public/index.php | 6 -- packages/rpm/README.md | 88 +++++++++++++++++++ packages/rpm/etc/httpd/conf.d/icingaweb.conf | 32 +++++++ packages/rpm/etc/icingaweb/authentication.ini | 6 ++ .../icingaweb/modules/monitoring/backends.ini | 8 ++ .../modules/monitoring/instances.ini | 2 + packages/rpm/etc/icingaweb/resources.ini | 28 ++++++ packages/rpm/usr/bin/icingacli | 6 ++ 13 files changed, 206 insertions(+), 126 deletions(-) delete mode 100644 packages/rhel/README delete mode 100644 packages/rhel/etc/httpd/conf.d/icingaweb.conf delete mode 100644 packages/rhel/usr/share/icingaweb/public/.htaccess delete mode 100644 packages/rhel/usr/share/icingaweb/public/index.php create mode 100644 packages/rpm/README.md create mode 100644 packages/rpm/etc/httpd/conf.d/icingaweb.conf create mode 100644 packages/rpm/etc/icingaweb/authentication.ini create mode 100644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini create mode 100644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini create mode 100644 packages/rpm/etc/icingaweb/resources.ini create mode 100755 packages/rpm/usr/bin/icingacli diff --git a/.gitignore b/.gitignore index ef44d81c9..1444ecfb2 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ config/preferences/*.ini # Application logfiles var/log/*.log +# Packaging /debian +*.tar.gz +*.komodoproject diff --git a/icingaweb2.spec b/icingaweb2.spec index b1cb3d2e5..6d9109f02 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -3,14 +3,14 @@ # Upstream: The icinga devel team # ExcludeDist: el4 el3 -%define revision 0 +%define revision 1 %define configdir %{_sysconfdir}/icingaweb %define sharedir %{_datadir}/icingaweb %define prefixdir %{_datadir}/icingaweb %define logdir %{sharedir}/log %define usermodparam -a -G -#%define logdir %{_localstatedir}/log/icingaweb +%define logdir %{_localstatedir}/log/icingaweb %if "%{_vendor}" == "suse" %define phpname php5 @@ -37,15 +37,15 @@ %define apacheconfdir %{_sysconfdir}/apache2/conf.d %define apacheuser wwwrun %define apachegroup www -%define extcmdfile1x %{_localstatedir}/icinga/rw/icinga.cmd -%define livestatussocket1x %{_localstatedir}/icinga/rw/live +%define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd +%define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus %endif %if "%{_vendor}" == "redhat" %define apacheconfdir %{_sysconfdir}/httpd/conf.d %define apacheuser apache %define apachegroup apache -%define extcmdfile-1x %{_localstatedir}/spool/icinga/cmd/icinga.cmd -%define livestatussocket1x %{_localstatedir}/spool/icinga/cmd/live +%define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd +%define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus %endif Summary: Open Source host, service and network monitoring Web UI @@ -109,8 +109,8 @@ Requires: php-Icinga %description -IcingaWeb for Icinga 2 or Icinga 1.x using status data, -IDOUtils or Livestatus as backend provider. +Icinga Web 2 for Icinga 2 or Icinga 1.x using multiple backends +for example DB IDO. %package -n icingacli Summary: Icinga CLI @@ -130,58 +130,54 @@ Requires: %{phpzendname} %description -n php-Icinga -Icinga Web 2 PHP Libraries shared with icingacli. - - +Icinga Web 2 PHP Libraries required by the web frontend and cli tool. %prep -#%setup -q -n %{name}-%{version} -%setup -q -n %{name} +#VERSION=0.0.1; git archive --format=tar --prefix=icingaweb2-$VERSION/ HEAD | gzip >icingaweb2-$VERSION.tar.gz +%setup -q -n %{name}-%{version} %build -cat > README.RHEL.SUSE <<"EOF" -IcingaWeb for RHEL and SUSE -=========================== - -Please check ./doc/installation.md -for requirements and database setup. -EOF - %install [ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot} # prepare configuration for sub packages # install rhel apache config -install -D -m0644 packages/rhel/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf +install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf # install public, library, modules %{__mkdir} -p %{buildroot}/%{sharedir} %{__mkdir} -p %{buildroot}/%{logdir} +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules +%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring %{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules %{__cp} -r application library modules public %{buildroot}/%{sharedir}/ -# install index.php -install -m0644 packages/rhel/usr/share/icingaweb/public/index.php %{buildroot}/%{sharedir}/public/index.php - -# use the vagrant config for configuration for now - TODO -%{__cp} -r .vagrant-puppet/files/etc/icingaweb %{buildroot}/%{_sysconfdir}/ - -# we use the default 'icinga' database -sed -i 's/icinga2/icinga/g' %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini +## config +# use the default menu.ini for application and monitoring mobule +install -D -m0644 config/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/menu.ini +install -D -m0644 config/modules/monitoring/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/menu.ini +# authentication is db only +install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini +# custom resource paths +install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini +# dashboard +install -D -m0644 config/dashboard/dashboard.ini %{buildroot}/%{_sysconfdir}/icingaweb/dashboard/dashboard.ini +# monitoring module (icinga2) +install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/backends.ini +install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/instances.ini # enable the monitoring module by default ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring +## config # install icingacli -install -D -m0755 bin/icingacli %{buildroot}/usr/bin/icingacli - -# install sql schema files as example - -# delete all *.in files +install -D -m0755 packages/rpm/usr/bin/icingacli %{buildroot}/usr/bin/icingacli %pre # Add apacheuser in the icingacmd group @@ -196,9 +192,6 @@ if [ $? -eq 0 ]; then %{_sbindir}/usermod %{usermodparam} icingacmd %{apacheuser} fi -# uncomment if building from git -# %{__rm} -rf %{buildroot}%{_datadir}/icinga2-web/.git - %preun %post @@ -209,14 +202,13 @@ fi %files # main dirs %defattr(-,root,root) -%doc etc/schema doc packages/rhel/README +%doc etc/schema doc packages/rpm/README.md %attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/public %attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/modules # configs %defattr(-,root,root) %config(noreplace) %attr(-,root,root) %{apacheconfdir}/icingaweb.conf -%dir %{configdir} -%config(noreplace) %attr(775,%{apacheuser},%{apachegroup}) %{configdir} +%config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir} # logs %attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir} @@ -228,6 +220,3 @@ fi %attr(0755,root,root) /usr/bin/icingacli %changelog -* Tue May 11 2014 Michael Friedrich - 0.0.1-1 -- initial creation - diff --git a/packages/rhel/README b/packages/rhel/README deleted file mode 100644 index 4e04875bb..000000000 --- a/packages/rhel/README +++ /dev/null @@ -1,57 +0,0 @@ -Requirements -============ - -Disabled SELinux for sending commands via external command pipe -provided by Icinga (2) Core. - -# setenforce 0 - - -Webinterface Login -================== - -The default credentials using the internal MySQL database are - -icingaadmin:icinga - - -Internal MySQL DB Setup -======================= - -# mysql -u root -p - CREATE USER `icingaweb`@`localhost` IDENTIFIED BY 'icingaweb'; - CREATE DATABASE `icingaweb`; - GRANT ALL PRIVILEGES ON `icingaweb`.* TO `icingaweb`@`localhost`; - FLUSH PRIVILEGES; - quit - -# mysql icingaweb < /usr/share/doc/icingaweb2-*/schema/accounts.mysql.sql -# mysql icingaweb < /usr/share/doc/icingaweb2-*/schema/preferences.mysql.sql - - -Modules -======= - -The monitoring module is enabled by default. - - -Backend configuration -===================== - -/etc/icingaweb/resources.ini - -contains the database backend information. By default -the Icinga IDO DB is used by the monitoring module in - -/etc/icingaweb/modules/monitoring/backends.ini - -The external command pipe is required for sending commands -and configured for Icinga 2 in - -/etc/icingaweb/modules/monitoring/instances.ini - - -Support -======= - -Please use one of the listed support channels at https://support.icinga.org diff --git a/packages/rhel/etc/httpd/conf.d/icingaweb.conf b/packages/rhel/etc/httpd/conf.d/icingaweb.conf deleted file mode 100644 index 47d408a67..000000000 --- a/packages/rhel/etc/httpd/conf.d/icingaweb.conf +++ /dev/null @@ -1,12 +0,0 @@ -Alias /icingaweb /usr/share/icingaweb/public - - Options -Indexes - - AllowOverride All - - Order allow,deny - Allow from all - - EnableSendfile Off - - diff --git a/packages/rhel/usr/share/icingaweb/public/.htaccess b/packages/rhel/usr/share/icingaweb/public/.htaccess deleted file mode 100644 index 8588fbbe1..000000000 --- a/packages/rhel/usr/share/icingaweb/public/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ -RewriteEngine on -RewriteBase /icingaweb -RewriteCond %{REQUEST_FILENAME} -s [OR] -RewriteCond %{REQUEST_FILENAME} -l [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^.*$ - [NC,L] -RewriteRule ^.*$ index.php [NC,L] diff --git a/packages/rhel/usr/share/icingaweb/public/index.php b/packages/rhel/usr/share/icingaweb/public/index.php deleted file mode 100644 index be6847bc8..000000000 --- a/packages/rhel/usr/share/icingaweb/public/index.php +++ /dev/null @@ -1,6 +0,0 @@ - + Options SymLinksIfOwnerMatch + AllowOverride None + + + # Apache 2.4 + + Require all granted + + + + + # Apache 2.2 + Order allow,deny + Allow from all + + + SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb + + EnableSendfile Off + + RewriteEngine on + RewriteBase /icingaweb/ + RewriteCond %{REQUEST_FILENAME} -s [OR] + RewriteCond %{REQUEST_FILENAME} -l [OR] + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^.*$ - [NC,L] + RewriteRule ^.*$ index.php [NC,L] + + diff --git a/packages/rpm/etc/icingaweb/authentication.ini b/packages/rpm/etc/icingaweb/authentication.ini new file mode 100644 index 000000000..3ec9c033a --- /dev/null +++ b/packages/rpm/etc/icingaweb/authentication.ini @@ -0,0 +1,6 @@ +[autologin] +backend = autologin + +[internal_db_authentication] +backend = db +resource = internal_db diff --git a/packages/rpm/etc/icingaweb/modules/monitoring/backends.ini b/packages/rpm/etc/icingaweb/modules/monitoring/backends.ini new file mode 100644 index 000000000..6dd67d4b5 --- /dev/null +++ b/packages/rpm/etc/icingaweb/modules/monitoring/backends.ini @@ -0,0 +1,8 @@ +[localdb] +type = ido +resource = "ido" + +[locallive] +disabled = "1" +type = livestatus +resource = livestatus diff --git a/packages/rpm/etc/icingaweb/modules/monitoring/instances.ini b/packages/rpm/etc/icingaweb/modules/monitoring/instances.ini new file mode 100644 index 000000000..037baa8b9 --- /dev/null +++ b/packages/rpm/etc/icingaweb/modules/monitoring/instances.ini @@ -0,0 +1,2 @@ +[icinga] +path = "/var/run/icinga2/cmd/icinga2.cmd" diff --git a/packages/rpm/etc/icingaweb/resources.ini b/packages/rpm/etc/icingaweb/resources.ini new file mode 100644 index 000000000..1c1e62eb1 --- /dev/null +++ b/packages/rpm/etc/icingaweb/resources.ini @@ -0,0 +1,28 @@ +[internal_db] +type = db +db = mysql +host = localhost +port = 3306 +password = icingaweb +username = icingaweb +dbname = icingaweb + +[ido] +type = db +db = mysql +host = localhost +port = 3306 +password = icinga +username = icinga +dbname = icinga + +[livestatus] +type = livestatus +socket = /var/run/icinga2/cmd/livestatus + +[logfile] +type = file +filename = "/var/log/icingaweb/icingaweb.log" +fields = "/^(?[0-9]{4}(-[0-9]{2}){2}T[0-9]{2}(:[0-9]{2}){2}(\\+[0-9]{2}:[0-9]{2})?) - (?[A-Za-z]+) - (?.*)$/" +; format: PCRE +; diff --git a/packages/rpm/usr/bin/icingacli b/packages/rpm/usr/bin/icingacli new file mode 100755 index 000000000..03ed43116 --- /dev/null +++ b/packages/rpm/usr/bin/icingacli @@ -0,0 +1,6 @@ +#!/usr/bin/php +dispatch(); From 57db6e429b645d8fbdab95119f01426b06eb8534 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 12 Aug 2014 15:24:55 +0200 Subject: [PATCH 129/184] Fix README, update spec header refs #6401 refs #6404 --- icingaweb2.spec | 29 +++++++++++++++++++++++++---- packages/rpm/README.md | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/icingaweb2.spec b/icingaweb2.spec index 6d9109f02..0da22b8ea 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -1,7 +1,28 @@ -# $Id$ -# Authority: The icinga devel team -# Upstream: The icinga devel team -# ExcludeDist: el4 el3 +#/** +# * This file is part of Icinga Web 2. +# * +# * Icinga Web 2 - Head for multiple monitoring backends. +# * Copyright (C) 2014 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 2014 Icinga Development Team +# * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 +# * @author Icinga Development Team +# * +# */ %define revision 1 diff --git a/packages/rpm/README.md b/packages/rpm/README.md index b136a6b19..d218a51eb 100644 --- a/packages/rpm/README.md +++ b/packages/rpm/README.md @@ -5,6 +5,7 @@ package (RHEL/CentOS/Fedora, SLES/OpenSUSE). ## Requirements +* EPEL/OBS Repository for Zend Framework * Apache 2.2+ * PHP 5.3+, Zend Framework, PHP PDO MySQL/PostgreSQL, PHP LDAP (optional) * MySQL or PostgreSQL for internal DB From b5d6481c1288ee55e1f45735ad03c6d32ea2c4a7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 13 Aug 2014 15:52:33 +0200 Subject: [PATCH 130/184] Make it possible to translate the process info view refs #6610 --- .../views/scripts/process/info.phtml | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/modules/monitoring/application/views/scripts/process/info.phtml b/modules/monitoring/application/views/scripts/process/info.phtml index 350966c49..8fb9bd0e3 100644 --- a/modules/monitoring/application/views/scripts/process/info.phtml +++ b/modules/monitoring/application/views/scripts/process/info.phtml @@ -10,28 +10,33 @@ $cf = $this->getHelper('CommandForm');

        Backend backendName; ?> -is_currently_running === '1' ? sprintf('has been running with PID %d ', $ps->process_id) . $this->prefixedTimeSince($ps->program_start_time) : 'is not running'; ?>. +is_currently_running === '1' + ? sprintf( + $this->translate('has been running with PID %d '), + $ps->process_id + ) . $this->prefixedTimeSince($ps->program_start_time) + : $this->translate('is not running'); ?>. - + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - +
        Last status updatetranslate('Last status update'); ?> timeSince($ps->status_update_time) ?> ago
        Last check commandtranslate('Last check command'); ?> timeSince($ps->last_command_check) ?> ago
        Global host event handlerglobal_host_event_handler ? $ps->global_host_event_handler : 'Not set' ?>translate('Global host event handler'); ?>global_host_event_handler ? $ps->global_host_event_handler : $this->translate('Not set'); ?>
        Global service event handlerglobal_service_event_handler ? $ps->global_service_event_handler : 'Not set' ?>translate('Global service event handler'); ?>global_service_event_handler ? $ps->global_service_event_handler : $this->translate('Not set'); ?>
        Notifications enabledtranslate('Notifications enabled'); ?> toggleSubmitForm( '', $ps->notifications_enabled, @@ -41,15 +46,18 @@ $cf = $this->getHelper('CommandForm'); array('global' => '1') ) ?> notifications_enabled === '1'): ?> - Temporarily disable + + translate('Temporarily disable'); ?> + disable_notif_expire_time): ?> -Will be re-enabled in timeUntil($ps->disable_notif_expire_time) ?> +translate('Will be re-enabled in %s'), '' . $this->timeUntil($ps->disable_notif_expire_time) . ''); ?>
        Execute active service checkstranslate('Execute active service checks'); ?> toggleSubmitForm( '', $ps->active_service_checks_enabled, @@ -60,7 +68,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Accept passive service checkstranslate('Accept passive service checks'); ?> toggleSubmitForm( '', $ps->passive_service_checks_enabled, @@ -71,7 +79,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Execute active host checkstranslate('Execute active host checks'); ?> toggleSubmitForm( '', $ps->active_host_checks_enabled, @@ -82,7 +90,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Accept passive host checkstranslate('Accept passive host checks'); ?> toggleSubmitForm( '', $ps->passive_host_checks_enabled, @@ -93,7 +101,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Eventhandlers enabledtranslate('Eventhandlers enabled'); ?> toggleSubmitForm( '', $ps->event_handlers_enabled, @@ -104,7 +112,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Obsessing over host checkstranslate('Obsessing over host checks'); ?> toggleSubmitForm( '', $ps->obsess_over_hosts, @@ -115,7 +123,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Obsessing over service checkstranslate('Obsessing over service checks'); ?> toggleSubmitForm( '', $ps->obsess_over_services, @@ -126,7 +134,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Flap detection enabledtranslate('Flap detection enabled'); ?> toggleSubmitForm( '', $ps->flap_detection_enabled, @@ -137,7 +145,7 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Process performance datatranslate('Process performance data'); ?> toggleSubmitForm( '', $ps->process_performance_data, @@ -148,17 +156,17 @@ Will be re-enabled in timeUntil($ps->disable_notif_expire_tim ) ?>
        Monitoring Processtranslate('Monitoring Process'); ?> labelSubmitForm( - 'Restart', - 'Restart the monitoring process', + $this->translate('Restart'), + $this->translate('Restart the monitoring process'), '', 'restartprocess' ) ?> + diff --git a/public/img/favicon.png b/public/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..6d6f713986903f1ab05631fdeba40bd5aa433d27 GIT binary patch literal 451 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XECYN(Tvg;1{{R1Pq^mcne}b>4*T;_^qr)SvUA<~=Z8NdI zzooJ1#PQ=#9zV`VP20V5XIXK{+SRM)&z%d@a^;b83(yeZk|4ieAQufVY^uG(!N9=C z=;`7ZQgN$iGAG|*1s;}-XL)!2`hWb|l*JtmQQjVmY@2g(KK>GHYIio|Es>mXc-OVJ z!DBE1_kBYs!znukC5cxl;Rk|kQyjlnFh)dbXs3$^ zD`*~`^x%h7lC*-dM)QmBwl%^V6ryVx#|1*4HKL{#+L2&`L>w3hB3ij zIa_Tn>mj)XTuaW*4CJ*?Kkri*azLM9V(UYK&9 zwcsv;%}&1TsVdFx3Mz%mbaGqF6;4mzeD4M0UIvSY$CXTT*WIx&3jJi#sJ*s5%Kvxp gzw3L>e-uk#C{W~+XO_F201PVzPgg&ebxsLQ0Ka>@YXATM literal 0 HcmV?d00001 From f67d273bbdca6a77fc2ee66e936a6b90869b3a66 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 10:14:46 +0200 Subject: [PATCH 157/184] AuthenticationController: handle redirect parameter This is a form field instead of a get parameter right now. fixes #6584 --- .../controllers/AuthenticationController.php | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index 7d4864d5a..a6a5345ed 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -35,12 +35,17 @@ class AuthenticationController extends ActionController public function loginAction() { $auth = $this->Auth(); - $this->view->form = new LoginForm(); - $this->view->form->setRequest($this->_request); + $this->view->form = $form = new LoginForm(); + $form->setRequest($this->_request); $this->view->title = $this->translate('Icingaweb Login'); try { - $redirectUrl = Url::fromPath($this->params->get('redirect', 'dashboard')); + $redirectUrl = $this->view->form->getValue('redirect'); + if ($redirectUrl) { + $redirectUrl = Url::fromPath($redirectUrl); + } else { + $redirectUrl = Url::fromPath('dashboard'); + } if ($auth->isAuthenticated()) { $this->rerenderLayout()->redirectNow($redirectUrl); @@ -72,12 +77,20 @@ class AuthenticationController extends ActionController } } } - } elseif ($this->view->form->isSubmittedAndValid()) { - $user = new User($this->view->form->getValue('username')); - $password = $this->view->form->getValue('password'); + } elseif ($form->isSubmittedAndValid()) { + $user = new User($form->getValue('username')); + $password = $form->getValue('password'); $backendsTried = 0; $backendsWithError = 0; + $redirectUrl = $form->getValue('redirect'); + + if ($redirectUrl) { + $redirectUrl = Url::fromPath($redirectUrl); + } else { + $redirectUrl = Url::fromPath('dashboard'); + } + foreach ($chain as $backend) { if ($backend instanceof AutoLoginBackend) { continue; @@ -112,14 +125,14 @@ class AuthenticationController extends ActionController ); } if ($backendsWithError) { - $this->view->form->addNote( + $form->addNote( $this->translate( 'Note that not all authentication backends are available for authentication because they' . ' have errors. Please check the system log or Icinga Web 2 log for more information' ) ); } - $this->view->form->getElement('password')->addError($this->translate('Incorrect username or password')); + $form->getElement('password')->addError($this->translate('Incorrect username or password')); } } catch (Exception $e) { $this->view->errorInfo = $e->getMessage(); From 5c52e447f5d01df93c8e101848193384a9373e2f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 10:26:38 +0200 Subject: [PATCH 158/184] doc: upper case first character of a documentation's title refs #4820 --- modules/doc/application/views/scripts/toc.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/application/views/scripts/toc.phtml b/modules/doc/application/views/scripts/toc.phtml index 8ffe2820d..7e92a6b26 100644 --- a/modules/doc/application/views/scripts/toc.phtml +++ b/modules/doc/application/views/scripts/toc.phtml @@ -1,5 +1,5 @@
        -

        translate('Documentation'); ?>

        +

        translate(sprintf('%s Documentation', ucfirst($docName))); ?>

        render($this, $this->getHelper('Url')); ?> From a38d71f17c652720f5887fc8cfe843edd7e305c3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 10:27:26 +0200 Subject: [PATCH 159/184] Cli\Command: provide Config() This makes it feel more like action controllers fixes #6954 --- library/Icinga/Cli/Command.php | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/library/Icinga/Cli/Command.php b/library/Icinga/Cli/Command.php index 081be318c..acb2462e4 100644 --- a/library/Icinga/Cli/Command.php +++ b/library/Icinga/Cli/Command.php @@ -7,6 +7,7 @@ namespace Icinga\Cli; use Icinga\Cli\Screen; use Icinga\Util\Translator; use Icinga\Cli\Params; +use Icinga\Application\Config; use Icinga\Application\ApplicationBootstrap as App; use Exception; @@ -23,6 +24,10 @@ abstract class Command protected $commandName; protected $actionName; + private $config; + + private $configs; + protected $defaultActionName = 'default'; public function __construct(App $app, $moduleName, $commandName, $actionName, $initialize = true) @@ -41,6 +46,51 @@ abstract class Command } } + public function Config($file = null) + { + if ($this->isModule()) { + return $this->getModuleConfig($file); + } else { + return $this->getMainConfig($file); + } + } + + private function getModuleConfig($file = null) + { + if ($file === null) { + if ($this->config === null) { + $this->config = Config::module($this->moduleName); + } + return $this->config; + } else { + if (! array_key_exists($file, $this->configs)) { + $this->configs[$file] = Config::module($this->moduleName, $file); + } + return $this->configs[$file]; + } + } + + private function getMainConfig($file = null) + { + if ($file === null) { + if ($this->config === null) { + $this->config = Config::app(); + } + return $this->config; + } else { + if (! array_key_exists($file, $this->configs)) { + $this->configs[$file] = Config::module($module, $file); + } + return $this->configs[$file]; + } + return $this->config; + } + + public function isModule() + { + return substr(get_class($this), 0, 14) === 'Icinga\\Module\\'; + } + public function setParams(Params $params) { $this->params = $params; From c69af6c5d2f72e6c3d93bc94703935ce7426dbf1 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 19 Aug 2014 11:10:01 +0200 Subject: [PATCH 160/184] Move "modules" to "System" menu resolves #6934 --- .vagrant-puppet/files/etc/icingaweb/menu.ini | 7 ++++++- application/controllers/ConfigController.php | 3 --- config/menu.ini | 7 ++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.vagrant-puppet/files/etc/icingaweb/menu.ini b/.vagrant-puppet/files/etc/icingaweb/menu.ini index f01ebaecc..d6f342655 100644 --- a/.vagrant-puppet/files/etc/icingaweb/menu.ini +++ b/.vagrant-puppet/files/etc/icingaweb/menu.ini @@ -18,10 +18,15 @@ title = "Configuration" url = "config" priority = 300 +[System.Modules] +title = "Modules" +url = "config/modules" +priority = 400 + [System.ApplicationLog] title = "Application log" url = "list/applicationlog" -priority = 400 +priority = 500 [Logout] url = "authentication/logout" diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 31739c30e..df189f47f 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -48,9 +48,6 @@ class ConfigController extends BaseConfigController ))->add('logging', array( 'title' => 'Logging', 'url' => 'config/logging' - ))->add('modules', array( - 'title' => 'Modules', - 'url' => 'config/modules' )); } diff --git a/config/menu.ini b/config/menu.ini index 65959c93a..075f4230e 100644 --- a/config/menu.ini +++ b/config/menu.ini @@ -18,10 +18,15 @@ title = "Configuration" url = "config" priority = 300 +[System.Modules] +title = "Modules" +url = "config/modules" +priority = 400 + [System.ApplicationLog] title = "Application log" url = "list/applicationlog" -priority = 400 +priority = 500 [Logout] url = "authentication/logout" From 127e4f444f7536f1bba601c46e2a097bfa03f42c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 11:30:56 +0200 Subject: [PATCH 161/184] doc: show prev chapter, index and next chapter links refs #4820 --- .../controllers/IcingawebController.php | 7 +- .../controllers/ModuleController.php | 1 + .../doc/application/views/scripts/toc.phtml | 2 +- modules/doc/library/Doc/DocController.php | 5 +- modules/doc/library/Doc/SectionRenderer.php | 79 ++++++++++++++++++- modules/doc/library/Doc/TocRenderer.php | 2 +- modules/doc/public/css/module.less | 19 +++++ 7 files changed, 108 insertions(+), 7 deletions(-) diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 117d6eb0e..967a2b768 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -30,7 +30,12 @@ class Doc_IcingawebController extends DocController 404 ); } - $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterId, 'doc/icingaweb/chapter'); + $this->renderChapter( + Icinga::app()->getApplicationDir('/../doc'), + $chapterId, + 'doc/icingaweb/toc', + 'doc/icingaweb/chapter' + ); } /** diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 7429dbdf2..19418a800 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -102,6 +102,7 @@ class Doc_ModuleController extends DocController $this->renderChapter( $moduleManager->getModuleDir($moduleName, '/doc'), $chapterId, + $this->_helper->url->url(array('moduleName' => $moduleName), 'doc/module/toc'), 'doc/module/chapter', array('moduleName' => $moduleName) ); diff --git a/modules/doc/application/views/scripts/toc.phtml b/modules/doc/application/views/scripts/toc.phtml index 7e92a6b26..3f6c6b37c 100644 --- a/modules/doc/application/views/scripts/toc.phtml +++ b/modules/doc/application/views/scripts/toc.phtml @@ -1,6 +1,6 @@

        translate(sprintf('%s Documentation', ucfirst($docName))); ?>

        -
        +
        render($this, $this->getHelper('Url')); ?>
        diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 16824ce0c..88beeee96 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -13,15 +13,17 @@ class DocController extends ModuleActionController * * @param string $path Path to the documentation * @param string $chapterId ID of the chapter + * @param string $tocUrl * @param string $url * @param array $urlParams */ - protected function renderChapter($path, $chapterId, $url, array $urlParams = array()) + protected function renderChapter($path, $chapterId, $tocUrl, $url, array $urlParams = array()) { $parser = new DocParser($path); $this->view->sectionRenderer = new SectionRenderer( $parser->getDocTree(), SectionRenderer::decodeUrlParam($chapterId), + $tocUrl, $url, $urlParams ); @@ -60,6 +62,7 @@ class DocController extends ModuleActionController $this->view->sectionRenderer = new SectionRenderer( $docTree, null, + null, $url, $urlParams ); diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index bc7bfb742..0f925bafb 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -87,6 +87,8 @@ class SectionRenderer extends Renderer */ protected $docTree; + protected $tocUrl; + /** * The URL to replace links with * @@ -120,12 +122,13 @@ class SectionRenderer extends Renderer * * @param DocTree $docTree The documentation tree * @param string|null $chapterId If not null, the chapter ID to filter for + * @param string $tocUrl * @param string $url The URL to replace links with * @param array $urlParams Additional URL parameters * * @throws ChapterNotFoundException If the chapter to filter for was not found */ - public function __construct(DocTree $docTree, $chapterId, $url, array $urlParams) + public function __construct(DocTree $docTree, $chapterId, $tocUrl, $url, array $urlParams) { if ($chapterId !== null) { $filter = new SectionFilterIterator($docTree, $chapterId); @@ -142,6 +145,7 @@ class SectionRenderer extends Renderer parent::__construct($docTree, RecursiveIteratorIterator::SELF_FIRST); } $this->docTree = $docTree; + $this->tocUrl = $tocUrl; $this->url = $url; $this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams); $this->parsedown = Parsedown::instance(); @@ -182,9 +186,11 @@ class SectionRenderer extends Renderer * * @param View $view * @param Zend_View_Helper_Url $zendUrlHelper + * @param bool $renderNavigation + * * @return string */ - public function render(View $view, Zend_View_Helper_Url $zendUrlHelper) + public function render(View $view, Zend_View_Helper_Url $zendUrlHelper, $renderNavigation = true) { $callback = new Callback($this->docTree, $view, $zendUrlHelper, $this->url, $this->urlParams); $content = array(); @@ -192,7 +198,7 @@ class SectionRenderer extends Renderer $section = $node->getValue(); /* @var $section \Icinga\Module\Doc\Section */ $content[] = sprintf( - ' %3$s', + '%3$s', Renderer::encodeAnchor($section->getId()), $section->getLevel(), $view->escape($section->getTitle()) @@ -213,6 +219,73 @@ class SectionRenderer extends Renderer $html ); } + if ($renderNavigation) { + foreach ($this->docTree as $chapter) { + if ($chapter->getValue()->getId() === $section->getChapterId()) { + $content[] = ''; + break; + } + } + } return implode("\n", $content); } } diff --git a/modules/doc/library/Doc/TocRenderer.php b/modules/doc/library/Doc/TocRenderer.php index b4ad4228c..4061e80e3 100644 --- a/modules/doc/library/Doc/TocRenderer.php +++ b/modules/doc/library/Doc/TocRenderer.php @@ -9,7 +9,7 @@ use Zend_View_Helper_Url; use Icinga\Web\View; /** - * toc renderer + * TOC renderer */ class TocRenderer extends Renderer { diff --git a/modules/doc/public/css/module.less b/modules/doc/public/css/module.less index 52fff5c32..2676d0f24 100644 --- a/modules/doc/public/css/module.less +++ b/modules/doc/public/css/module.less @@ -31,3 +31,22 @@ code { pre > code { display: inline-block; } + +div.chapter > ul.navigation { + margin: 0; + padding: 0.4em; + text-align: center; + background-color: #888; + + li { + list-style: none; + display: inline; + margin: 0.2em; + padding: 0; + + a { + color: #fff; + text-decoration: none; + } + } +} From 0d3fb9e7a55e66a4cd381acae0004592237f60e4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 11:35:51 +0200 Subject: [PATCH 162/184] LoginForm: explicitely remove renderLayout Should prevent against rare race conditions. refs #6584 --- application/forms/Authentication/LoginForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index f7fc9f22e..0eff8006c 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -17,7 +17,7 @@ class LoginForm extends Form */ protected function create() { - $url = Url::fromRequest(); + $url = Url::fromRequest()->without('renderLayout'); $this->setName('form_login'); $this->addElement('text', 'username', array( From 9ce99bf2800fc54c3cef55678e91bf0c73689829 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:22:36 +0200 Subject: [PATCH 163/184] js/helpers: add $.hasAttr Need this for another fix and there is no such jQuery function. As this seems to also be inconsistant across browsers I thought this would definitively be worth a custom jQuery plugin. --- public/js/helpers.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/js/helpers.js b/public/js/helpers.js index 082689e40..416bf1185 100644 --- a/public/js/helpers.js +++ b/public/js/helpers.js @@ -95,6 +95,13 @@ if (!Function.prototype.bind) { 'use strict'; + /* Whether a HTML tag has a specific attribute */ + $.fn.hasAttr = function(name) { + // We have inconsistent behaviour across browsers (false VS undef) + var val = this.attr(name); + return typeof val !== 'undefined' && val !== false; + }; + /* Get class list */ $.fn.classes = function (callback) { From f40ada6c81ef25e1179e899eea1281fe6b132eba Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:23:51 +0200 Subject: [PATCH 164/184] js/ui: check whether a link tag has a type The new favicon didn't have such, resulting in JS error when reloading CSS at runtime. --- public/js/icinga/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index e9f3d741c..8ff3733f0 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -100,7 +100,7 @@ icinga.logger.info('Reloading CSS'); $('link').each(function() { var $oldLink = $(this); - if ($oldLink.attr('type').indexOf('css') > -1) { + if ($oldLink.hasAttr('type') && $oldLink.attr('type').indexOf('css') > -1) { var $newLink = $oldLink.clone().attr( 'href', icinga.utils.addUrlParams( From 7334716d156b21047e824a806a1cf84a657c06fb Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:25:14 +0200 Subject: [PATCH 165/184] favicon: add link type The favicon link tag was missing a type attribute. --- application/layouts/scripts/layout.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml index 765873c02..32c4879b4 100644 --- a/application/layouts/scripts/layout.phtml +++ b/application/layouts/scripts/layout.phtml @@ -44,7 +44,7 @@ $iframeClass = $isIframe ? ' iframe' : ''; - + From db3ef8fbd0c598032e3422ff302007c36e60c42a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:28:24 +0200 Subject: [PATCH 166/184] ActionController: remove redirectToLogin default The redirectOnLogin function now doesn't have any predefined default. This also leads to the behaviour that "Logout" will not redirect you anywhere on a new login while "forcibly being logged out" (e.g. by logging out in another browser tab) will bring you back to where you came from. --- .../Web/Controller/ActionController.php | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index d8e340907..13809e83d 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -254,18 +254,23 @@ class ActionController extends Zend_Controller_Action * * @throws \Exception */ - protected function redirectToLogin($afterLogin = '/dashboard') + protected function redirectToLogin($afterLogin = null) { - if (! $afterLogin instanceof Url) { - $afterLogin = Url::fromPath($afterLogin); - } - if ($this->isXhr()) { - $redir = '__SELF__'; - } else { - // TODO: Ignore /? - $redir = $afterLogin->getRelativeUrl(); + $redir = null; + if ($afterLogin !== null) { + if (! $afterLogin instanceof Url) { + $afterLogin = Url::fromPath($afterLogin); + } + if ($this->isXhr()) { + $redir = '__SELF__'; + } else { + // TODO: Ignore /? + $redir = $afterLogin->getRelativeUrl(); + } } + $url = Url::fromPath('authentication/login'); + if ($redir) { $url->setParam('redirect', $redir); } From 3f65f3447d1255284ffb34f7e91dc961389b42e8 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:39:53 +0200 Subject: [PATCH 167/184] Web\UrlTest: test should use %20 instead of + We MUST use rawurlencode everywhere to avoid potential conflicts with URLs created on JS side. refs #6604 --- test/php/library/Icinga/Web/UrlTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index 0eff54e5c..183f33369 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -21,7 +21,7 @@ class UrlTest extends BaseTestCase $url = Url::fromRequest(); $this->assertEquals( '/path/to/my/test/url.html?param1=value1&param2=value2', - $url->getAbsoluteUrl(), + $url->getAbsoluteUrl('&'), 'Url::fromRequest does not reassemble the correct url from the global request' ); } @@ -119,7 +119,7 @@ class UrlTest extends BaseTestCase */ public function testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters() { - $url = Url::fromPath('/my/test/url.html?param1=%25arg1¶m2=arg+2' + $url = Url::fromPath('/my/test/url.html?param1=%25arg1¶m2=arg%202' . '¶m3[]=1¶m3[]=2¶m3[]=3¶m4[key1]=val1¶m4[key2]=val2'); $this->assertEquals( From cd825a4e6b5e2c13cc76f038f22ade6c12f30be7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 12:45:42 +0200 Subject: [PATCH 168/184] tests/ScrollingStyle: adjust, pagination shortened Pagination has been "shortened" some time ago to waste less space, tests now fit the current implementation. fixes #6594 fixes #6595 --- .../Paginator/ScrollingStyle/SlidingWithBorderTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/php/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorderTest.php b/test/php/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorderTest.php index d9eb0b25b..a77dcb553 100644 --- a/test/php/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorderTest.php +++ b/test/php/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorderTest.php @@ -19,8 +19,8 @@ class SlidingwithborderTest extends BaseTestCase $pages = $scrollingStyle->getPages($paginator); $this->assertInternalType('array', $pages); - $this->assertCount(13, $pages); - $this->assertEquals('...', $pages[11]); + $this->assertCount(10, $pages); + $this->assertEquals('...', $pages[8]); } public function testGetPages3() @@ -31,9 +31,9 @@ class SlidingwithborderTest extends BaseTestCase $pages = $scrollingStyle->getPages($paginator); $this->assertInternalType('array', $pages); - $this->assertCount(16, $pages); + $this->assertCount(10, $pages); $this->assertEquals('...', $pages[3]); - $this->assertEquals('...', $pages[14]); + $this->assertEquals('...', $pages[12]); } protected function getPaginatorAdapter() From 2f54ff4797cc321261288e28658c3ef993f44710 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 19 Aug 2014 13:17:31 +0200 Subject: [PATCH 169/184] Fix the unrelated tabs resolves #6934 --- application/controllers/ConfigController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index df189f47f..45be1d3fd 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -67,7 +67,7 @@ class ConfigController extends BaseConfigController $form->setConfiguration(IcingaConfig::app()); $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { - if (!$this->writeConfigFile($form->getConfig(), 'config')) { + if (!$this->writeConfigFile($form->getConfig(), 'config')) { return; } Notification::success('New configuration has successfully been stored'); @@ -104,6 +104,11 @@ class ConfigController extends BaseConfigController */ public function modulesAction() { + $this->view->tabs = Widget::create('tabs')->add('modules', array( + 'title' => 'Modules', + 'url' => 'config/modules' + )); + $this->view->tabs->activate('modules'); $this->view->modules = Icinga::app()->getModuleManager()->select() ->from('modules') From d35e0816c96619f6292bce4e8dd8d36220d970d8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 13:20:46 +0200 Subject: [PATCH 170/184] doc: use `sprintf' in exceptions refs #4820 --- .../application/controllers/ModuleController.php | 4 ++-- modules/doc/library/Doc/DocParser.php | 13 +++++++++---- modules/doc/library/Doc/DocTree.php | 2 +- modules/doc/library/Doc/SectionRenderer.php | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 19418a800..26dac8626 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -44,13 +44,13 @@ class Doc_ModuleController extends DocController $moduleManager = Icinga::app()->getModuleManager(); if (! $moduleManager->hasInstalled($moduleName)) { throw new Zend_Controller_Action_Exception( - $this->translate('Module') . ' \'' . $moduleName . '\' ' . $this->translate('is not installed'), + $this->translate(sprintf('Module \'%s\' is not installed', $moduleName)), 404 ); } if (! $moduleManager->hasEnabled($moduleName)) { throw new Zend_Controller_Action_Exception( - $this->translate('Module') . ' \'' . $moduleName . '\' ' . $this->translate('is not enabled'), + $this->translate(sprintf('Module \'%s\' is not enabled', $moduleName)), 404 ); } diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 8fa34ba77..e0311e6ba 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -41,19 +41,24 @@ class DocParser { if (! is_dir($path)) { throw new DocException( - mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'does not exist') + mt('doc', sprintf('Documentation directory \'%s\' does not exist', $path)) ); } if (! is_readable($path)) { throw new DocException( - mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'is not readable') + mt('doc', sprintf('Documentation directory \'%s\' is not readable', $path)) ); } $docIterator = new DocIterator($path); if ($docIterator->count() === 0) { throw new DocEmptyException( - mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' - . mt('doc', 'does not contain any non-empty Markdown file (\'.md\' suffix') + mt( + 'doc', + sprintf( + 'Documentation directory \'%s\' does not contain any non-empty Markdown file (\'.md\' suffix)', + $path + ) + ) ); } $this->path = $path; diff --git a/modules/doc/library/Doc/DocTree.php b/modules/doc/library/Doc/DocTree.php index 07a0c5279..4223d8e99 100644 --- a/modules/doc/library/Doc/DocTree.php +++ b/modules/doc/library/Doc/DocTree.php @@ -57,7 +57,7 @@ class DocTree extends Node } if (! isset($this->nodes[$parentId])) { throw new LogicException( - sprintf('Can\'t add child node: there\'s no parent node having the id \'%s\'', $parentId) + mt('doc', sprintf('Can\'t add child node: there\'s no parent node having the id \'%s\'', $parentId)) ); } $this->nodes[$childId] = $this->nodes[$parentId]->appendChild($child); diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index 0f925bafb..fab6d66c4 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -134,7 +134,7 @@ class SectionRenderer extends Renderer $filter = new SectionFilterIterator($docTree, $chapterId); if ($filter->count() === 0) { throw new ChapterNotFoundException( - mt('doc', 'Chapter') . ' \'' . $chapterId . '\' ' . mt('doc', 'not found') + mt('doc', sprintf('Chapter \'%s\' not found', $chapterId)) ); } parent::__construct( From 55b3a5384a5e2ed89a974eadc6b454ac14ea5ab8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 13:38:18 +0200 Subject: [PATCH 171/184] doc: set page title refs #4820 --- modules/doc/application/views/scripts/toc.phtml | 2 +- modules/doc/library/Doc/DocController.php | 3 +++ modules/doc/library/Doc/SectionRenderer.php | 15 ++++++++------- modules/doc/public/css/module.less | 10 ++++++++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/modules/doc/application/views/scripts/toc.phtml b/modules/doc/application/views/scripts/toc.phtml index 3f6c6b37c..ca6283d67 100644 --- a/modules/doc/application/views/scripts/toc.phtml +++ b/modules/doc/application/views/scripts/toc.phtml @@ -1,5 +1,5 @@
        -

        translate(sprintf('%s Documentation', ucfirst($docName))); ?>

        +

        render($this, $this->getHelper('Url')); ?> diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 88beeee96..66895bfb4 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -27,6 +27,7 @@ class DocController extends ModuleActionController $url, $urlParams ); + $this->view->title = $chapterId; $this->_helper->viewRenderer('chapter', null, true); } @@ -42,7 +43,9 @@ class DocController extends ModuleActionController { $parser = new DocParser($path); $this->view->tocRenderer = new TocRenderer($parser->getDocTree(), $url, $urlParams); + $name = ucfirst($name); $this->view->docName = $name; + $this->view->title = $this->translate(sprintf('%s Documentation', $name)); $this->_helper->viewRenderer('toc', null, true); } diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index fab6d66c4..6281b6d76 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -222,7 +222,7 @@ class SectionRenderer extends Renderer if ($renderNavigation) { foreach ($this->docTree as $chapter) { if ($chapter->getValue()->getId() === $section->getChapterId()) { - $content[] = ''; + $content = array_merge($navigation, $content, $navigation); break; } } diff --git a/modules/doc/public/css/module.less b/modules/doc/public/css/module.less index 2676d0f24..d6d0d2a94 100644 --- a/modules/doc/public/css/module.less +++ b/modules/doc/public/css/module.less @@ -48,5 +48,15 @@ div.chapter > ul.navigation { color: #fff; text-decoration: none; } + + &.prev { + padding-right: 0.6em; + border-right: 2px solid #fff; + } + + &.next { + padding-left: 0.6em; + border-left: 2px solid #fff; + } } } From f52b3f7b8c5757c961db958aad9d361e18b40957 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 13:38:34 +0200 Subject: [PATCH 172/184] doc/config: add menu.ini refs #4820 --- config/modules/doc/menu.ini | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 config/modules/doc/menu.ini diff --git a/config/modules/doc/menu.ini b/config/modules/doc/menu.ini new file mode 100644 index 000000000..86889b239 --- /dev/null +++ b/config/modules/doc/menu.ini @@ -0,0 +1,5 @@ +[Documentation] +title = "Documentation" +icon = "img/icons/comment.png" +url = "doc" +priority = 80 From 703509946886e76c5916bfbdc5bee551bbacad75 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 19 Aug 2014 14:42:28 +0200 Subject: [PATCH 173/184] Fix the host_unhandled_service_count error fixes #6833 --- modules/monitoring/application/controllers/ListController.php | 1 - modules/monitoring/application/controllers/MultiController.php | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index f0cdb2767..7e96ef459 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -94,7 +94,6 @@ class Monitoring_ListController extends Controller 'host_last_check', 'host_last_state_change' => $stateChangeColumn, 'host_notifications_enabled', - // 'host_unhandled_service_count', 'host_unhandled_services', 'host_action_url', 'host_notes_url', diff --git a/modules/monitoring/application/controllers/MultiController.php b/modules/monitoring/application/controllers/MultiController.php index a623dd88b..6459edd59 100644 --- a/modules/monitoring/application/controllers/MultiController.php +++ b/modules/monitoring/application/controllers/MultiController.php @@ -24,7 +24,6 @@ class Monitoring_MultiController extends Controller array( 'host_name', 'host_in_downtime', - 'host_unhandled_service_count', 'host_passive_checks_enabled', 'host_obsessing', 'host_state', From f49a34625e17a189fe528eb7e963d51defd655a2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 14:53:56 +0200 Subject: [PATCH 174/184] Url\Params: check for QUERY_STRING before using it fixes #6957 --- library/Icinga/Web/UrlParams.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/UrlParams.php b/library/Icinga/Web/UrlParams.php index fa1b2a7f3..bdf776401 100644 --- a/library/Icinga/Web/UrlParams.php +++ b/library/Icinga/Web/UrlParams.php @@ -352,7 +352,7 @@ class UrlParams public static function fromQueryString($queryString = null) { if ($queryString === null) { - $queryString = $_SERVER['QUERY_STRING']; + $queryString = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; } $params = new static(); $params->parseQueryString($queryString); From 8c02f140c3937bba60cc0d6fff2a2270de68a995 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 16:22:22 +0200 Subject: [PATCH 175/184] doc: fix translate calls w/ sprintf --- .../application/controllers/ModuleController.php | 4 ++-- modules/doc/library/Doc/DocController.php | 2 +- modules/doc/library/Doc/DocParser.php | 16 ++++++++-------- modules/doc/library/Doc/DocTree.php | 2 +- modules/doc/library/Doc/SectionRenderer.php | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 26dac8626..40913368c 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -44,13 +44,13 @@ class Doc_ModuleController extends DocController $moduleManager = Icinga::app()->getModuleManager(); if (! $moduleManager->hasInstalled($moduleName)) { throw new Zend_Controller_Action_Exception( - $this->translate(sprintf('Module \'%s\' is not installed', $moduleName)), + sprintf($this->translate('Module \'%s\' is not installed'), $moduleName), 404 ); } if (! $moduleManager->hasEnabled($moduleName)) { throw new Zend_Controller_Action_Exception( - $this->translate(sprintf('Module \'%s\' is not enabled', $moduleName)), + sprintf($this->translate('Module \'%s\' is not enabled'), $moduleName), 404 ); } diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index 66895bfb4..42f8dce9b 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -45,7 +45,7 @@ class DocController extends ModuleActionController $this->view->tocRenderer = new TocRenderer($parser->getDocTree(), $url, $urlParams); $name = ucfirst($name); $this->view->docName = $name; - $this->view->title = $this->translate(sprintf('%s Documentation', $name)); + $this->view->title = sprintf($this->translate('%s Documentation'), $name); $this->_helper->viewRenderer('toc', null, true); } diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index e0311e6ba..c63532dc1 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -41,23 +41,23 @@ class DocParser { if (! is_dir($path)) { throw new DocException( - mt('doc', sprintf('Documentation directory \'%s\' does not exist', $path)) + sprintf(mt('doc', 'Documentation directory \'%s\' does not exist'), $path) ); } if (! is_readable($path)) { throw new DocException( - mt('doc', sprintf('Documentation directory \'%s\' is not readable', $path)) + sprintf(mt('doc', 'Documentation directory \'%s\' is not readable'), $path) ); } $docIterator = new DocIterator($path); if ($docIterator->count() === 0) { throw new DocEmptyException( - mt( - 'doc', - sprintf( - 'Documentation directory \'%s\' does not contain any non-empty Markdown file (\'.md\' suffix)', - $path - ) + sprintf( + mt( + 'doc', + 'Documentation directory \'%s\' does not contain any non-empty Markdown file (\'.md\' suffix)' + ), + $path ) ); } diff --git a/modules/doc/library/Doc/DocTree.php b/modules/doc/library/Doc/DocTree.php index 4223d8e99..1b112649c 100644 --- a/modules/doc/library/Doc/DocTree.php +++ b/modules/doc/library/Doc/DocTree.php @@ -57,7 +57,7 @@ class DocTree extends Node } if (! isset($this->nodes[$parentId])) { throw new LogicException( - mt('doc', sprintf('Can\'t add child node: there\'s no parent node having the id \'%s\'', $parentId)) + sprintf(mt('doc', 'Can\'t add child node: there\'s no parent node having the id \'%s\''), $parentId) ); } $this->nodes[$childId] = $this->nodes[$parentId]->appendChild($child); diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index 6281b6d76..938e5ed7b 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -134,7 +134,7 @@ class SectionRenderer extends Renderer $filter = new SectionFilterIterator($docTree, $chapterId); if ($filter->count() === 0) { throw new ChapterNotFoundException( - mt('doc', sprintf('Chapter \'%s\' not found', $chapterId)) + sprintf(mt('doc', 'Chapter \'%s\' not found'), $chapterId) ); } parent::__construct( From 995355ec30f0879b26bebf82b88636369dfc2685 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 19 Aug 2014 16:29:11 +0200 Subject: [PATCH 176/184] lib/Limiter: fix translate w/ sprintf --- library/Icinga/Web/Widget/Limiter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Widget/Limiter.php b/library/Icinga/Web/Widget/Limiter.php index 9c6a726ba..5afa7e4b8 100644 --- a/library/Icinga/Web/Widget/Limiter.php +++ b/library/Icinga/Web/Widget/Limiter.php @@ -81,7 +81,7 @@ class Limiter extends AbstractWidget $this->url->setParam('limit', $limit), null, array( - 'title' => t(sprintf('Show %s rows on one page', $caption)) + 'title' => sprintf(t('Show %s rows on one page'), $caption) ) ); } From 3573908071898eee6c54d6a3f7a46de85349bb5b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 19 Aug 2014 17:03:35 +0200 Subject: [PATCH 177/184] Let downtime view look more list-like and fix downtime end calculation refs #6637 --- .../controllers/ListController.php | 5 +- .../views/scripts/list/downtimes.phtml | 181 +++++++++++------- .../Backend/Ido/Query/DowntimeQuery.php | 14 +- .../library/Monitoring/DataView/Downtime.php | 2 + 4 files changed, 135 insertions(+), 67 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index f0cdb2767..6c4cc0101 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -222,6 +222,7 @@ class Monitoring_ListController extends Controller 'author' => 'downtime_author', 'start' => 'downtime_start', 'scheduled_start' => 'downtime_scheduled_start', + 'scheduled_end' => 'downtime_scheduled_end', 'end' => 'downtime_end', 'duration' => 'downtime_duration', 'is_flexible' => 'downtime_is_flexible', @@ -229,7 +230,9 @@ class Monitoring_ListController extends Controller 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', 'host' => 'downtime_host', - 'service' => 'downtime_service' + 'service' => 'downtime_service', + 'host_state' => 'downtime_host_state', + 'service_state' => 'downtime_service_state' ))->order('downtime_is_in_effect', 'DESC') ->order('downtime_scheduled_start', 'DESC'); diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index 995a66001..babb76074 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -1,70 +1,121 @@ getHelper('CommandForm'); ?> -
        -tabs ?> -
        -sortControl->render($this); ?> -
        -paginationControl($downtimes, null, null, array('preserve' => $this->preserve)); ?> -
        -
        - - -downtimes as $downtime): ?> - - - - - - -
        - dateFormat()->formatDateTime($downtime->start); ?> - - dateFormat()->formatDateTime($downtime->end); ?> -
        - Duration: util()->showHourMin($downtime->duration); ?> -
        - The is_flexible): ?>flexiblefixed downtime is is_in_effect): ?>not in effect -
        - service)): ?> - service ?> - on host ?> - - host ?> - -
        - author ?>: comment ?> -
        - Entry Time: entry_time) ? $this->dateFormat()->formatDateTime((int) $downtime->entry_time) : ''; ?> - -
        - $downtime->id, - 'host' => $downtime->host - ); - if (isset($downtime->service)) { - $data['service'] = $downtime->service; - } - // echo $helper->iconSubmitForm( - // 'img/icons/remove.png', - echo $helper->labelSubmitForm( - 'X', - 'Remove Downtime', - 'link-like', - 'removedowntime', - $data - ); - ?> -
        +compact): ?> +
        + tabs->render($this); ?> +
        + translate('Sort by'); ?> sortControl->render($this); ?> +
        + widget('limiter', array('url' => $this->url, 'max' => $downtimes->count())); ?> + paginationControl($downtimes, null, null, array('preserve' => $this->preserve)); ?> +
        + + +
        + + translate('No downtimes matching the filter'); ?> +
        + + + + + + service)) { + $stateName = strtolower($this->util()->getServiceStateName($downtime->service_state)); + } else { + $stateName = strtolower($this->util()->getHostStateName($downtime->host_state)); + } + ?> + + + + $downtime->id, + 'host' => $downtime->host + ); + if (isset($downtime->service)) { + $data['service'] = $downtime->service; + } + ?> + + + + +
        + is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?> +
        + prefixedTimeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start); ?> +
        + service)): ?> + + service; ?> + + + translate('on'); ?> host; ?> + + + + host; ?> + + +
        + icon('comment.png'); ?> [author; ?>] comment; ?> +
        + + is_flexible): ?> + is_in_effect): ?> + translate('This flexible downtime was started on %s at %s and lasts for %s until %s at %s.'), + date('d.m.y', $downtime->start), + date('H:i', $downtime->start), + $this->format()->duration($downtime->duration), + date('d.m.y', $downtime->end), + date('H:i', $downtime->end) + ); ?> + + translate('This flexible downtime has been scheduled to start between %s - %s and to last for %s.'), + date('d.m.y H:i', $downtime->scheduled_start), + date('d.m.y H:i', $downtime->scheduled_end), + $this->format()->duration($downtime->duration) + ); ?> + + + is_in_effect): ?> + translate('This fixed downtime was started on %s at %s and expires on %s at %s.'), + date('d.m.y', $downtime->start), + date('H:i', $downtime->start), + date('d.m.y', $downtime->end), + date('H:i', $downtime->end) + ); ?> + + translate('This fixed downtime has been scheduled to start on %s at %s and to end on %s at %s.'), + date('d.m.y', $downtime->scheduled_start), + date('H:i', $downtime->scheduled_start), + date('d.m.y', $downtime->scheduled_end), + date('H:i', $downtime->scheduled_end) + ); ?> + + + +
        + labelSubmitForm( + 'X', + 'Remove Downtime', + 'link-like', + 'removedowntime', + $data + ); ?> +
        diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php index 324d72c4c..1f5e17c6c 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php @@ -24,13 +24,15 @@ class DowntimeQuery extends IdoQuery 'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)', 'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', 'downtime_start' => "UNIX_TIMESTAMP(CASE WHEN sd.trigger_time != '0000-00-00 00:00:00' then sd.trigger_time ELSE sd.scheduled_start_time END)", - 'downtime_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', + 'downtime_end' => 'CASE WHEN sd.is_fixed THEN UNIX_TIMESTAMP(sd.scheduled_end_time) ELSE UNIX_TIMESTAMP(sd.trigger_time) + sd.duration END', 'downtime_duration' => 'sd.duration', 'downtime_is_in_effect' => 'sd.is_in_effect', 'downtime_internal_id' => 'sd.internal_downtime_id', 'downtime_host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci', 'downtime_service' => 'so.name2 COLLATE latin1_general_ci', 'downtime_objecttype' => "CASE WHEN ho.object_id IS NOT NULL THEN 'host' ELSE CASE WHEN so.object_id IS NOT NULL THEN 'service' ELSE NULL END END", + 'downtime_host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', + 'downtime_service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END' ), ); @@ -53,6 +55,16 @@ class DowntimeQuery extends IdoQuery 'sd.object_id = so.object_id AND so.is_active = 1 AND so.objecttype_id = 2', array() ); + $this->select->joinLeft( + array('hs' => $this->prefix . 'hoststatus'), + 'ho.object_id = hs.host_object_id', + array() + ); + $this->select->joinLeft( + array('ss' => $this->prefix . 'servicestatus'), + 'so.object_id = ss.service_object_id', + array() + ); $this->joinedVirtualTables = array('downtime' => true); } } diff --git a/modules/monitoring/library/Monitoring/DataView/Downtime.php b/modules/monitoring/library/Monitoring/DataView/Downtime.php index 8bd97673a..214e1afb5 100644 --- a/modules/monitoring/library/Monitoring/DataView/Downtime.php +++ b/modules/monitoring/library/Monitoring/DataView/Downtime.php @@ -30,6 +30,8 @@ class Downtime extends DataView 'downtime_internal_id', 'downtime_host', 'downtime_service', + 'downtime_host_state', + 'downtiem_service_state' ); } From 6fe47bf5b0bae7b6654c08e3c74be73adf32f9d8 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 19 Aug 2014 17:30:56 +0200 Subject: [PATCH 178/184] Fix rows highlighting after refresh fixes #6705 --- modules/monitoring/application/views/scripts/list/hosts.phtml | 2 +- .../monitoring/application/views/scripts/list/services.phtml | 2 +- public/js/icinga/events.js | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index a6056e149..6af1181e4 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -109,7 +109,7 @@ if ($hosts->count() === 0) { - host_name ?> diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml index f2da63dca..9e9e8d725 100644 --- a/modules/monitoring/application/views/scripts/list/services.phtml +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -99,7 +99,7 @@ foreach ($services as $service): service_icon_image && ! preg_match('/[\'"]/', $service->service_icon_image)): ?> icon($this->resolveMacros($service->service_icon_image, $service)) ?> -service_display_name ?>showHost): ?> on host_name; ?> +service_display_name ?>showHost): ?> on host_name; ?> host_state != 0): ?> (monitoringState($service, 'host')); ?>) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 6c6978577..5328d20aa 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -134,6 +134,7 @@ // Select a table row $(document).on('click', 'table.multiselect tr[href]', { self: this }, this.rowSelected); + $(document).on('click', 'table.multiselect .select-click', { self: this }, this.rowSelected); $(document).on('click', 'button', { self: this }, this.submitForm); @@ -400,7 +401,7 @@ rowSelected: function(event) { var self = event.data.self; var icinga = self.icinga; - var $tr = $(this); + var $tr = $(this).closest('tr'); var $table = $tr.closest('table.multiselect'); var data = self.icinga.ui.getSelectionKeys($table); var url = $table.data('icinga-multiselect-url'); From ba748cbc4af084c02ffd719930b9b91f103db58e Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 19 Aug 2014 18:33:28 +0200 Subject: [PATCH 179/184] Refactor multiSelect and highlight refs #6705 --- .../views/scripts/list/hosts.phtml | 2 +- .../views/scripts/list/services.phtml | 2 +- public/js/icinga/events.js | 26 ++++++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index 6af1181e4..a6056e149 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -109,7 +109,7 @@ if ($hosts->count() === 0) { - compact ? $hostLink : $this->href( 'monitoring/show/host', array('host' => $host->host_name) ) ?>">host_name ?> diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml index 9e9e8d725..f2da63dca 100644 --- a/modules/monitoring/application/views/scripts/list/services.phtml +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -99,7 +99,7 @@ foreach ($services as $service): service_icon_image && ! preg_match('/[\'"]/', $service->service_icon_image)): ?> icon($this->resolveMacros($service->service_icon_image, $service)) ?> -service_display_name ?>showHost): ?> on host_name; ?> +service_display_name ?>showHost): ?> on host_name; ?> host_state != 0): ?> (monitoringState($service, 'host')); ?>) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 5328d20aa..790d245c3 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -134,7 +134,6 @@ // Select a table row $(document).on('click', 'table.multiselect tr[href]', { self: this }, this.rowSelected); - $(document).on('click', 'table.multiselect .select-click', { self: this }, this.rowSelected); $(document).on('click', 'button', { self: this }, this.submitForm); @@ -475,12 +474,20 @@ return true; } - // Ignore clicks on multiselect table inner links while key pressed - if ((event.ctrlKey || event.metaKey || event.shiftKey) && - ! $a.is('tr[href]') && $a.closest('table.multiselect').length > 0 && - $a.closest('tr[href]').length > 0) - { - return self.rowSelected.call($a.closest('tr[href]'), event); + // Special checks for link clicks in multiselect rows + if (! $a.is('tr[href]') && $a.closest('tr[href]').length > 0 && $a.closest('table.multiselect').length > 0) { + + // Forward clicks to ANY link with special key pressed to rowSelected + if (event.ctrlKey || event.metaKey || event.shiftKey) + { + return self.rowSelected.call($a.closest('tr[href]'), event); + } + + // Forward inner links matching the row URL to rowSelected + if ($a.attr('href') === $a.closest('tr[href]').attr('href')) + { + return self.rowSelected.call($a.closest('tr[href]'), event); + } } // Let remote links pass through @@ -494,6 +501,11 @@ return false; } + // ignore multiselect table row clicks + if ($a.closest('tr[href]').length > 0 && $a.closest('table.action').length > 0) { + return; + } + // ignore multiselect table row clicks if ($a.is('tr') && $a.closest('table.multiselect').length > 0) { return; From 736113c80cb78aabd16a05eecb9a6f1a92a805fd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 18:55:58 +0200 Subject: [PATCH 180/184] AuthenticationController: show friendlier hints... ...while not disclosing sensitive information. More to come once we have our setup wizard. fixes #6534 --- .../controllers/AuthenticationController.php | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index a6a5345ed..4b43ed71b 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -54,13 +54,9 @@ class AuthenticationController extends ActionController try { $config = Config::app('authentication'); } catch (NotReadableError $e) { - Logger::error( - new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e) - ); throw new ConfigurationError( - t( - 'No authentication methods available. Authentication configuration could not be loaded.' - . ' Please check the system log or Icinga Web 2 log for more information' + $this->translate( + 'Could not read your authentiction.ini, no authentication methods are available.' ) ); } @@ -110,25 +106,25 @@ class AuthenticationController extends ActionController } if ($backendsTried === 0) { throw new ConfigurationError( - t( - 'No authentication methods available. It seems that no authentication method has been set' - . ' up. Please check the system log or Icinga Web 2 log for more information' - ) + $this->translate( + 'No authentication methods available. Did you create' + . ' authentication.ini when installing Icinga Web 2?' + ) ); } if ($backendsTried === $backendsWithError) { throw new ConfigurationError( $this->translate( - 'No authentication methods available. It seems that all set up authentication methods have' - . ' errors. Please check the system log or Icinga Web 2 log for more information' + 'All configured authentication methods failed.' + . ' Please check the system log or Icinga Web 2 log for more information.' ) ); } if ($backendsWithError) { $form->addNote( $this->translate( - 'Note that not all authentication backends are available for authentication because they' - . ' have errors. Please check the system log or Icinga Web 2 log for more information' + 'Please note that not all authentication methods where available.' + . ' Check the system log or Icinga Web 2 log for more information.' ) ); } From 686152abf305bd7e4845abfb9edfb9240327ebd1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 19 Aug 2014 19:04:29 +0200 Subject: [PATCH 181/184] js/events: fix a small bug introduced right now Links on non-multiselect action tables stopped working. refs #6705 --- public/js/icinga/events.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 790d245c3..eefca7f47 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -400,7 +400,7 @@ rowSelected: function(event) { var self = event.data.self; var icinga = self.icinga; - var $tr = $(this).closest('tr'); + var $tr = $(this); var $table = $tr.closest('table.multiselect'); var data = self.icinga.ui.getSelectionKeys($table); var url = $table.data('icinga-multiselect-url'); @@ -501,11 +501,6 @@ return false; } - // ignore multiselect table row clicks - if ($a.closest('tr[href]').length > 0 && $a.closest('table.action').length > 0) { - return; - } - // ignore multiselect table row clicks if ($a.is('tr') && $a.closest('table.multiselect').length > 0) { return; From 538c6cf90bad5f471963fca41b8d4d12a233b284 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 20 Aug 2014 09:26:16 +0200 Subject: [PATCH 182/184] js/events: ignore button/submit clicks In case you clicked a (submit) button in an action row this also also triggered that row's click handler. As the event is going to be stopped there, this leads to "unsubmittable forms". Fixed. fixes #6963 --- public/js/icinga/events.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index eefca7f47..d04912d0f 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -501,6 +501,12 @@ return false; } + // Ignore form elements in action rows + if ($(event.target).is('input') || $(event.target).is('button')) { + return; + } + + // ignore multiselect table row clicks if ($a.is('tr') && $a.closest('table.multiselect').length > 0) { return; From 4eaf94cdf15cf17bcdb40288de8e912db53eb5fd Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 20 Aug 2014 11:33:22 +0200 Subject: [PATCH 183/184] Show only acknowledgement and user comments by default --- .vagrant-puppet/files/etc/icingaweb/modules/monitoring/menu.ini | 2 +- config/modules/monitoring/menu.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vagrant-puppet/files/etc/icingaweb/modules/monitoring/menu.ini b/.vagrant-puppet/files/etc/icingaweb/modules/monitoring/menu.ini index c66a611a9..c185fb87d 100644 --- a/.vagrant-puppet/files/etc/icingaweb/modules/monitoring/menu.ini +++ b/.vagrant-puppet/files/etc/icingaweb/modules/monitoring/menu.ini @@ -68,7 +68,7 @@ priority = 70 [Overview.Comments] title = "Comments" -url = "monitoring/list/comments" +url = "monitoring/list/comments?comment_type=(comment|ack)" priority = 70 [Overview.Contacts] diff --git a/config/modules/monitoring/menu.ini b/config/modules/monitoring/menu.ini index c66a611a9..c185fb87d 100644 --- a/config/modules/monitoring/menu.ini +++ b/config/modules/monitoring/menu.ini @@ -68,7 +68,7 @@ priority = 70 [Overview.Comments] title = "Comments" -url = "monitoring/list/comments" +url = "monitoring/list/comments?comment_type=(comment|ack)" priority = 70 [Overview.Contacts] From 4ceecf1146545c93789d9126e0272a87cf2811ed Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 20 Aug 2014 11:34:24 +0200 Subject: [PATCH 184/184] Let comment view look more standardized and add limit control refs #6637 --- .../views/scripts/list/comments.phtml | 182 ++++++++++-------- 1 file changed, 99 insertions(+), 83 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml index 3ed671fbe..645c35fb6 100644 --- a/modules/monitoring/application/views/scripts/list/comments.phtml +++ b/modules/monitoring/application/views/scripts/list/comments.phtml @@ -1,98 +1,114 @@ +getHelper('CommandForm'); + +?> + +compact): ?>
        -tabs ?> -
        -sortControl->render($this); ?> -
        -paginationControl($comments, null, null, array('preserve' => $this->preserve)); ?> + tabs->render($this); ?> +
        + translate('Sort by'); ?> sortControl->render($this); ?> +
        + widget('limiter', array('url' => $this->url, 'max' => $comments->count())); ?> + paginationControl($comments, null, null, array('preserve' => $this->preserve)); ?>
        +
        - - - + translate('No comments matching the filter') ?> + + -$cf = $this->getHelper('CommandForm'); - -if (count($comments) === 0) { - echo t('No comments matching the filter'); -} - -foreach ($comments as $comment): - -?> - - + + + +
        + + + type) { + switch ($comment->type) { case 'flapping': - $icon = 'flapping'; - $tooltip = 'Comment was caused by a flapping host or service.'; - break; + $icon = 'flapping'; + $title = $this->translate('Flapping'); + $tooltip = $this->translate('Comment was caused by a flapping host or service.'); + break; case 'comment': - $icon = 'user'; - $tooltip = 'Comment was created by an user.'; - break; + $icon = 'user'; + $title = $this->translate('User Comment'); + $tooltip = $this->translate('Comment was created by an user.'); + break; case 'downtime': - $icon = 'down'; - $tooltip = 'Comment was caused by a downtime.'; + $icon = 'down'; + $title = $this->translate('Downtime'); + $tooltip = $this->translate('Comment was caused by a downtime.'); case 'ack': - $icon = 'acknowledgement'; - $tooltip = 'Comment was caused by an acknowledgement.'; - } + $icon = 'acknowledgement'; + $title = $this->translate('Acknowledgement'); + $tooltip = $this->translate('Comment was caused by an acknowledgement.'); + } ?> - icon($icon . '.png', $tooltip) ?>
        - timeSince($comment->timestamp) ?> - - + + + $comment->id, + 'host' => $comment->host + ); + if ($comment->objecttype === 'service') { + $data['service'] = $comment->service; + } ?> - - - - - -
        - objecttype === 'service'): ?>icon('service.png', 'Service comment') ?> qlink( - $comment->service, - 'monitoring/show/service', - array( +
        + icon($icon . '.png', $tooltip) ?> +
        + escape($title); ?> +
        + prefixedTimeSince($comment->timestamp); ?> +
        + objecttype === 'service'): ?> + icon('service.png'); ?> + service; ?> + + + translate('on') . ' ' . $comment->host; ?> + + + icon('host.png'); ?> + host; ?> + + +
        + icon('comment.png'); ?> author) + ? '[' . $comment->author . '] ' + : ''; + ?>escape($comment->comment); ?> +
        + persistent + ? $this->translate('This comment is persistent.') + : $this->translate('This comment is not persistent.'); + ?> +
        + expiration ? sprintf( + $this->translate('This comment expires on %s at %s.'), + date('d.m.y', $comment-expiration), + date('H:i', $comment->expiration) + ) : $this->translate('This comment does not expire.'); ?> +
        - $comment->id, - 'host' => $comment->host - ); - - if ($comment->objecttype === 'service') { - $data['service'] = $comment->service; - } - - // echo $cf->iconSubmitForm( - // 'img/icons/remove.png', - echo $cf->labelSubmitForm( - 'X', - 'Remove comment', - 'link-like', - 'removecomment', - $data - ); - -?> -
        - - +
        + labelSubmitForm( + 'X', + $this->translate('Remove Comment'), + 'link-like', + 'removecomment', + $data + ); ?> +
        +
        \ No newline at end of file