From 6ff5a986dd9818b6ecb81786abe01d29beb28ef3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 26 May 2014 13:08:47 +0200 Subject: [PATCH 01/96] 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 09:26:53 +0200 Subject: [PATCH 02/96] 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 @@ <?php -// @codingStandardsIgnoreStart // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} use Icinga\Module\Doc\Controller as DocController; -use Icinga\Module\Doc\DocParser; - class Doc_IndexController extends DocController { - protected $parser; - - - public function init() - { - $module = null; - $this->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 @@ -<h1>Icinga 2 Documentation</h1> -<?= $this->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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 09:35:02 +0200 Subject: [PATCH 03/96] 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 @@ +<?php if ($docHtml === null): ?> + <p>No documentation available.</p> +<?php else: ?> + <div class="toc"> + <?= $this->partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) + ); + ?> + </div> + <div class="doc"> + <?= $docHtml ?> + </div> +<?php endif ?> From e6abe21fb8bb2ec444e9432a012abfbf3a9af457 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 09:36:14 +0200 Subject: [PATCH 04/96] 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 09:40:00 +0200 Subject: [PATCH 05/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +use Icinga\Module\Doc\Controller as DocController; + +class Doc_IcingawebController extends DocController +{ + public function indexAction() + { + $this->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 @@ +<h1>Icinga Web 2 Documentation</h1> +<?= $this->partial( + 'partials/docandtoc.phtml', + 'doc', + array( + 'docToc' => $docToc, + 'docHtml' => $docHtml + ) +); ?> From 99971e241c22f5a6c5d3a9665c5f310a2ad65422 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:03:18 +0200 Subject: [PATCH 06/96] 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( '#<pre><code class="language-php">(.*?)\</code></pre>#s', array($this, 'highlight'), From 74ea4d19b829738f66f6778536532257b44069c8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:04:36 +0200 Subject: [PATCH 07/96] 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 @@ +<h1><?= $docName ?> documentation</h1> <?php if ($docHtml === null): ?> - <p>No documentation available.</p> +<p>Documentation not available.</p> <?php else: ?> - <div class="toc"> - <?= $this->partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) - ); - ?> - </div> - <div class="doc"> - <?= $docHtml ?> - </div> +<div class="toc"> +<?= $this->partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) + ); +?> +</div> +<div class="doc"> +<?= $docHtml ?> +</div> <?php endif ?> 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:06:28 +0200 Subject: [PATCH 08/96] 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 @@ <?php -// @codingStandardsIgnoreStart // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} @@ -9,39 +8,29 @@ use Icinga\Module\Doc\Controller as DocController; class Doc_ModuleController extends DocController { /** - * Display module documentations index + * List available modules */ public function indexAction() { $this->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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:07:09 +0200 Subject: [PATCH 09/96] 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 @@ -<h1>Icinga Web 2 Documentation</h1> -<?= $this->partial( - 'partials/docandtoc.phtml', - 'doc', - array( - 'docToc' => $docToc, - 'docHtml' => $docHtml - ) -); ?> From 4001f1de62fb6bf0dc8d1e5df03c83ea350dedac Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:09:50 +0200 Subject: [PATCH 10/96] 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 @@ -<?php if ($html === null): ?> - <p>No documentation available.</p> -<?php else: ?> -<div class="content"> -<?= $html ?> -</div> -<?php endif ?> 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:11:03 +0200 Subject: [PATCH 11/96] 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 @@ +<h1>Available documentations</h1> +<ul> + <li><a href="<?= $this->href('doc/icingaweb'); ?>">Icinga Web 2</a></li> + <li><a href="<?= $this->href('doc/module/'); ?>">Module documentations</a></li> +</ul> From 5627c09b3771857bb3fe97cf4a8e07fec58de79b Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:11:28 +0200 Subject: [PATCH 12/96] 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 @@ <h1>Module documentations</h1> <ul> <?php foreach ($enabledModules as $module): ?> - <li><a href="<?= $this->href('doc/module/view', array('name' => $module)); ?>"><?= $module ?></a></li> + <li><a href="<?= $this->href('doc/module/' . $module); ?>"><?= $module ?></a></li> <?php endforeach ?> </ul> From 89bddb10e7cfe90fed4426228e9837d058cf8d16 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:16:58 +0200 Subject: [PATCH 13/96] 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:17:37 +0200 Subject: [PATCH 14/96] 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 <eric.lippmann@netways.de> Date: Fri, 23 May 2014 14:22:35 +0200 Subject: [PATCH 15/96] 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 <eric.lippmann@netways.de> Date: Mon, 26 May 2014 13:33:32 +0200 Subject: [PATCH 16/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 14:31:17 +0200 Subject: [PATCH 17/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 14:48:35 +0200 Subject: [PATCH 18/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 14:49:32 +0200 Subject: [PATCH 19/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 14:50:48 +0200 Subject: [PATCH 20/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 14:53:25 +0200 Subject: [PATCH 21/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:02:09 +0200 Subject: [PATCH 22/96] 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 @@ +<?= $chapterHtml ?> 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 @@ -<h1><?= $docName ?> documentation</h1> -<?php if ($docHtml === null): ?> -<p>Documentation not available.</p> -<?php else: ?> -<div class="toc"> -<?= $this->partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) - ); -?> -</div> -<div class="doc"> -<?= $docHtml ?> -</div> -<?php endif ?> From 282af5a79467ea4cc49b9c6da0424877ccc0b304 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:03:02 +0200 Subject: [PATCH 23/96] 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 @@ +<div class="controls"> + <h1><?= $docName ?> documentation</h1> +</div> +<div class="content" data-base-target="_next"> +<?= $this->partial( + 'layout/menu.phtml', + 'default', + array( + 'items' => $docToc->getChildren(), + 'sub' => false, + 'url' => '' + ) +) ?> +</div> From dad7dc9e6ca0c9d2028a8b9d799a8ff8cf5fa0e4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:06:48 +0200 Subject: [PATCH 24/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:07:49 +0200 Subject: [PATCH 25/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:09:01 +0200 Subject: [PATCH 26/96] 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 <eric.lippmann@netways.de> Date: Tue, 27 May 2014 15:09:43 +0200 Subject: [PATCH 27/96] 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 13:28:06 +0200 Subject: [PATCH 28/96] 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 @@ -<div class="controls"> -<h1>Module documentations</h1> -</div> -<div class="content" data-base-target="_next"> -<?= $this->partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $toc->getChildren(), - 'sub' => false, - 'url' => '' - ) -) ?> -</div> From c12c4a9e4ca64543d981b3aa1486c12fd3ffedcf Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Wed, 28 May 2014 13:29:02 +0200 Subject: [PATCH 29/96] 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 13:33:07 +0200 Subject: [PATCH 30/96] 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 @@ +<?php + +use \Zend_Controller_Router_Route; +use Icinga\Application\Icinga; + +if (Icinga::app()->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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 13:34:39 +0200 Subject: [PATCH 31/96] 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:13:42 +0200 Subject: [PATCH 32/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Data\Tree; + +interface NodeInterface +{ + /** + * Append a child to the node + * + * @param mixed $value + * + * @return self + */ + public function appendChild($value); + + /** + * Get the node's value + * + * @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 9edaaa82e893b9cacf4db412b4e28d9a90f008f2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:14:33 +0200 Subject: [PATCH 33/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Data\Tree; + +//use RecursiveIterator; +// +//class TreeIterator implements RecursiveIterator +//{ +// protected $position = 0; +// +// protected $nodes; +// +// public function __construct(NodeInterface $node) +// { +// $this->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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:15:08 +0200 Subject: [PATCH 34/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc; + +use IteratorAggregate; +use Icinga\Data\Tree\NodeInterface; +use Icinga\Data\Tree\TreeIterator; + +class DocToc implements NodeInterface, IteratorAggregate +{ + protected $children = array(); + + protected $value; + + public function __construct($value = null) + { + $this->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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:15:43 +0200 Subject: [PATCH 35/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Module\Doc; + +use RecursiveIteratorIterator; + +class DocTocHtmlRenderer extends RecursiveIteratorIterator +{ + protected $html = array(); + + public function __construct(DocToc $toc) + { + parent::__construct($toc, RecursiveIteratorIterator::SELF_FIRST); + } + + public function beginIteration() + { + $this->html[] = '<ul>'; + } + + public function endIteration() + { + $this->html[] = '</ul>'; + } + + public function beginChildren() + { + $this->html[] = '<ul>'; + } + + public function endChildren() + { + $this->html[] = '</ul>'; + } + + 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:16:37 +0200 Subject: [PATCH 36/96] 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 @@ -<div class="controls"> - <h1><?= $docName ?> documentation</h1> -</div> -<div class="content" data-base-target="_next"> -<?= $this->partial( - 'layout/menu.phtml', - 'default', - array( - 'items' => $docToc->getChildren(), - 'sub' => false, - 'url' => '' - ) -) ?> -</div> 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:18:07 +0200 Subject: [PATCH 37/96] 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 = '<a name="' . $id . '"></a>' . 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:18:57 +0200 Subject: [PATCH 38/96] 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 @@ +<div class="controls"> + <h1><?= $docName ?> documentation</h1> +</div> +<div class="content" data-base-target="_next"> + <?php + $urlHelper = $this->getHelper('Url'); + $view = $this; + ?> + <?= $tocRenderer->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('<li><a href="%s">%s</a></li>', $url->getAbsoluteUrl(), $section->title); + }); ?> +</div> From dc1bada56f98abe33def0bb002c10b8bb04e7ce0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:19:24 +0200 Subject: [PATCH 39/96] 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 @@ +<div class="controls"> + <h1><?= $docName ?> documentation</h1> +</div> +<div class="content" data-base-target="_next"> + <?php + $urlHelper = $this->getHelper('Url'); + $view = $this; + ?> + <?= $tocRenderer->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('<li><a href="%s">%s</a></li>', $url->getAbsoluteUrl(), $section->title); + }); ?> +</div> From 49e927c4bf3d72e8575889ada522d57cf929600a Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:19:48 +0200 Subject: [PATCH 40/96] 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 <eric.lippmann@netways.de> Date: Wed, 28 May 2014 17:24:28 +0200 Subject: [PATCH 41/96] 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 @@ <h1>Available documentations</h1> <ul> - <li><a href="<?= $this->href('doc/icingaweb'); ?>">Icinga Web 2</a></li> + <li><a href="<?= $this->href('doc/icingaweb/toc'); ?>">Icinga Web 2</a></li> <li><a href="<?= $this->href('doc/module/'); ?>">Module documentations</a></li> </ul> 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 @@ <h1>Module documentations</h1> <ul> <?php foreach ($enabledModules as $module): ?> - <li><a href="<?= $this->href('doc/module/' . $module); ?>"><?= $module ?></a></li> + <li> + <a href="<?= $this->getHelper('Url')->url(array('moduleName' => $module), 'doc/module/toc', false, false); ?>"> + <?= $module ?> + </a> + </li> <?php endforeach ?> </ul> From 370bfca6e6e1f0897aa74b3b755711bb3a9ec501 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 3 Jun 2014 14:53:28 +0200 Subject: [PATCH 42/96] 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 <eric.lippmann@netways.de> Date: Tue, 3 Jun 2014 14:56:44 +0200 Subject: [PATCH 43/96] 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 <eric.lippmann@netways.de> Date: Tue, 3 Jun 2014 15:23:59 +0200 Subject: [PATCH 44/96] 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:55:58 +0200 Subject: [PATCH 45/96] 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:57:18 +0200 Subject: [PATCH 46/96] 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 @@ -<?php -// {{{ICINGA_LICENSE_HEADER}}} -// {{{ICINGA_LICENSE_HEADER}}} - -namespace Icinga\Data\Tree; - -//use RecursiveIterator; -// -//class TreeIterator implements RecursiveIterator -//{ -// protected $position = 0; -// -// protected $nodes; -// -// public function __construct(NodeInterface $node) -// { -// $this->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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:58:14 +0200 Subject: [PATCH 47/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Data\Tree; + +use RecursiveIteratorIterator; +use RuntimeException; +use SplDoublyLinkedList; + +class Node extends SplDoublyLinkedList implements NodeInterface +{ + protected $value; + + public function __construct($value = null) + { + $this->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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:58:40 +0200 Subject: [PATCH 48/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Data\Tree; + +use Exception; +use RecursiveIteratorIterator; +use RuntimeException; + +/** + * A not yet customizable node renderer + */ +class NodeRenderer extends RecursiveIteratorIterator +{ + protected $content = array(); + + public function __construct(NodeInterface $node) + { + parent::__construct($node, RecursiveIteratorIterator::SELF_FIRST); + } + + public function beginIteration() + { + $this->content[] = '<ul>'; + } + + public function endIteration() + { + $this->content[] = '</ul>'; + } + + public function beginChildren() + { + $this->content[] = '<ul>'; + } + + public function endChildren() + { + $this->content[] = '</ul>'; + } + + 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:59:11 +0200 Subject: [PATCH 49/96] 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 @@ -<?php -// {{{ICINGA_LICENSE_HEADER}}} -// {{{ICINGA_LICENSE_HEADER}}} - -namespace Icinga\Module\Doc; - -use IteratorAggregate; -use Icinga\Data\Tree\NodeInterface; -use Icinga\Data\Tree\TreeIterator; - -class DocToc implements NodeInterface, IteratorAggregate -{ - protected $children = array(); - - protected $value; - - public function __construct($value = null) - { - $this->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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 13:59:59 +0200 Subject: [PATCH 50/96] 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 @@ -<?php -// {{{ICINGA_LICENSE_HEADER}}} -// {{{ICINGA_LICENSE_HEADER}} - -namespace Icinga\Module\Doc; - -use RecursiveIteratorIterator; - -class DocTocHtmlRenderer extends RecursiveIteratorIterator -{ - protected $html = array(); - - public function __construct(DocToc $toc) - { - parent::__construct($toc, RecursiveIteratorIterator::SELF_FIRST); - } - - public function beginIteration() - { - $this->html[] = '<ul>'; - } - - public function endIteration() - { - $this->html[] = '</ul>'; - } - - public function beginChildren() - { - $this->html[] = '<ul>'; - } - - public function endChildren() - { - $this->html[] = '</ul>'; - } - - 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 14:09:12 +0200 Subject: [PATCH 51/96] 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 14:10:13 +0200 Subject: [PATCH 52/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +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 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 @@ <?php // {{{ICINGA_LICENSE_HEADER}}} -// {{{ICINGA_LICENSE_HEADER}} +// {{{ICINGA_LICENSE_HEADER}}} 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\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 = '<a name="' . $id . '"></a>' . 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( '#<pre><code class="language-php">(.*?)\</code></pre>#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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 14:10:35 +0200 Subject: [PATCH 53/96] 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 @@ +<?php +$urlHelper = $this->getHelper('Url'); +$view = $this; +?> +<?= preg_replace_callback( + '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/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( + '<a %s%shref="%s"', + strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', + $section->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 @@ <div class="controls"> - <h1><?= $docName ?> documentation</h1> + <h1><?= $docName ?> documentation</h1> </div> <div class="content" data-base-target="_next"> - <?php + <?php $urlHelper = $this->getHelper('Url'); $view = $this; - ?> - <?= $tocRenderer->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('<li><a href="%s">%s</a></li>', $url->getAbsoluteUrl(), $section->title); - }); ?> + ?> + <?= $tocRenderer->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( + '<li><a %shref="%s">%s</a></li>', + $section->nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $section->title + ); + }); ?> </div> 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 @@ <h1>Available documentations</h1> <ul> - <li><a href="<?= $this->href('doc/icingaweb/toc'); ?>">Icinga Web 2</a></li> - <li><a href="<?= $this->href('doc/module/'); ?>">Module documentations</a></li> + <li><a href="<?= $this->href('doc/icingaweb/toc'); ?>">Icinga Web 2</a></li> + <li><a href="<?= $this->href('doc/module/'); ?>">Module documentations</a></li> </ul> 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 @@ +<?php +$urlHelper = $this->getHelper('Url'); +$view = $this; +?> +<?= preg_replace_callback( + '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/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( + '<a %s%shref="%s"', + strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', + $section->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 @@ <h1>Module documentations</h1> <ul> -<?php foreach ($enabledModules as $module): ?> - <li> - <a href="<?= $this->getHelper('Url')->url(array('moduleName' => $module), 'doc/module/toc', false, false); ?>"> - <?= $module ?> - </a> - </li> -<?php endforeach ?> + <?php foreach ($enabledModules as $module): ?> + <li> + <a href="<?= $this->getHelper('Url')->url(array('moduleName' => $module), 'doc/module/toc', false, false); ?>"> + <?= $module ?> + </a> + </li> + <?php endforeach ?> </ul> 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 @@ <div class="controls"> - <h1><?= $docName ?> documentation</h1> + <h1><?= $docName ?> documentation</h1> </div> <div class="content" data-base-target="_next"> - <?php + <?php $urlHelper = $this->getHelper('Url'); $view = $this; - ?> - <?= $tocRenderer->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('<li><a href="%s">%s</a></li>', $url->getAbsoluteUrl(), $section->title); - }); ?> + ?> + <?= $tocRenderer->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( + '<li><a %shref="%s">%s</a></li>', + $section->nofollow ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $section->title + ); + }); ?> </div> 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 @@ -<?= $chapterHtml ?> 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 <eric.lippmann@netways.de> Date: Fri, 6 Jun 2014 14:45:13 +0200 Subject: [PATCH 54/96] 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 <eric.lippmann@netways.de> Date: Fri, 13 Jun 2014 17:22:43 +0200 Subject: [PATCH 55/96] 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 <eric.lippmann@netways.de> Date: Fri, 13 Jun 2014 17:23:20 +0200 Subject: [PATCH 56/96] 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( '#<pre><code class="language-php">(.*?)\</code></pre>#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('#(?:<(?P<tag>a|span) id="(?P<id>.+)"></(?P=tag)>)#u', $header, $match) + if ($header[0] === '<' + && preg_match('#(?:<(?P<tag>a|span) id="(?P<id>.+)"></(?P=tag)>)#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 <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 11:43:25 +0200 Subject: [PATCH 57/96] 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 @@ <h1>Module documentations</h1> <ul> - <?php foreach ($enabledModules as $module): ?> + <?php foreach ($modules as $module): ?> <li> <a href="<?= $this->getHelper('Url')->url(array('moduleName' => $module), 'doc/module/toc', false, false); ?>"> <?= $module ?> From 93cc24a93a87d5a2548486021e816884f1db6ff7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:18:22 +0200 Subject: [PATCH 58/96] 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 <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:21:16 +0200 Subject: [PATCH 59/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc\Exception; + +/** + * Exception thrown if a chapter was not found + */ +class ChapterNotFoundException extends DocException +{ +} From a8e6dda78349257a303a4429bbba97523c969a3b Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:21:35 +0200 Subject: [PATCH 60/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc\Exception; + +/** + * Exception thrown if a documentation directory is empty + */ +class DocEmptyException extends DocException +{ +} From c48f7f9fba6a07f869009d03b5d6f33f2ceab0fb Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:22:32 +0200 Subject: [PATCH 61/96] 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 <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:24:40 +0200 Subject: [PATCH 62/96] 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( + '#<pre><code class="language-php">(.*?)\</code></pre>#s', + array($this, 'highlight'), + Parsedown::instance()->text(implode('', $chapter)) + ); } } - return preg_replace_callback( - '#<pre><code class="language-php">(.*?)\</code></pre>#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 <eric.lippmann@netways.de> Date: Mon, 30 Jun 2014 15:48:43 +0200 Subject: [PATCH 63/96] 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 9af89e6ccfc32b54d643dc308f0c3a9e32eb1129 Mon Sep 17 00:00:00 2001 From: Alexander Klimov <Alexander.Klimov@netways.de> Date: Wed, 23 Jul 2014 11:28:45 +0200 Subject: [PATCH 64/96] 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; ?> +<div class="chapter"> <?= preg_replace_callback( '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/im', function($match) use ($toc, $urlHelper, $view) { @@ -36,3 +37,4 @@ $view = $this; }, $chapterHtml ); ?> +</div> 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; ?> +<div class="chapter"> <?= preg_replace_callback( '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/im', function($match) use ($toc, $moduleName, $urlHelper, $view) { @@ -36,3 +37,4 @@ $view = $this; }, $chapterHtml ); ?> +</div> 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 <http://www.w3.org/TR/CSS21/sample.html> (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 507ac1c4d7ae1b04eeed1e806f95224ba2600dad Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:52:25 +0200 Subject: [PATCH 65/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:53:41 +0200 Subject: [PATCH 66/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:55:54 +0200 Subject: [PATCH 67/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:57:46 +0200 Subject: [PATCH 68/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:58:46 +0200 Subject: [PATCH 69/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Module\Doc; + +use RecursiveFilterIterator; + +/** + * Recursive iterator over non-empty files + */ +class NonEmptyFileIterator extends RecursiveFilterIterator +{ + /** + * Accept non-empty files + * + * @return bool Whether the current element of the iterator is acceptable + * through this filter + */ + public function accept() + { + $current = $this->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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 18:59:42 +0200 Subject: [PATCH 70/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:00:50 +0200 Subject: [PATCH 71/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc; + +use Icinga\Data\Identifiable; + +/** + * A section of a documentation + */ +class Section implements Identifiable +{ + /** + * The ID of the section + * + * @var string + */ + protected $id; + + /** + * The title of the section + * + * @var string + */ + protected $title; + + /** + * The header level + * + * @var int + */ + protected $level; + + /** + * Whether to instruct search engines to not index the link to the section + * + * @var bool + */ + protected $nofollow; + + /** + * The title of the chapter the section is part of + * + * @var string + */ + protected $chapterTitle; + + /** + * The content of the section + * + * @var array + */ + protected $content = array(); + + /** + * Create a new section + * + * @param string $id The ID of the section + * @param string $title The title of the section + * @param int $level The header level + * @param bool $nofollow Whether to instruct search engines to not index the link to the section + * @param string $chapterTitle The title of the chapter the section is part of + */ + public function __construct($id, $title, $level, $nofollow, $chapterTitle) + { + $this->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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:05:37 +0200 Subject: [PATCH 72/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Data; + +/** + * Interface for objects that are identifiable by an ID of any type + */ +interface Identifiable +{ + /** + * Get the ID associated with this Identifiable object + * + * @return mixed + */ + public function getId(); +} From 51bc0274f3e47b6541ae9491373b6ce6cb0e8330 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:06:12 +0200 Subject: [PATCH 73/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc; + +use LogicException; +use Icinga\Data\Identifiable; +use Icinga\Data\Tree\Node; + +/** + * Documentation tree + */ +class DocTree extends Node +{ + /** + * All nodes of the tree + * + * @var array + */ + protected $nodes = array(); + + /** + * Append a root node to the tree + * + * @param Identifiable $root + */ + public function addRoot(Identifiable $root) + { + $rootId = $root->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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:07:13 +0200 Subject: [PATCH 74/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:09:04 +0200 Subject: [PATCH 75/96] 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 = '<a name="' . $id . '"></a>' . PHP_EOL . $line; - } - $chapter[] = $line; - } - if ($currentChapterName === $chapterName) { - return preg_replace_callback( - '#<pre><code class="language-php">(.*?)\</code></pre>#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('#(?:<(?P<tag>a|span) id="(?P<id>.+)"></(?P=tag)>)#u', $header, $match) + && preg_match('#(?:<(?P<tag>a|span) (?:id|name)="(?P<id>.+)"></(?P=tag)>)\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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:09:55 +0200 Subject: [PATCH 76/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Doc; + +use RecursiveIteratorIterator; +use Zend_View_Helper_Url; +use Icinga\Web\View; + +/** + * Base class for toc and section renderer + */ +abstract class Renderer extends RecursiveIteratorIterator +{ + /** + * Encode an anchor identifier + * + * @param string $anchor + * + * @return string + */ + public static function encodeAnchor($anchor) + { + return rawurlencode($anchor); + } + + /** + * Decode an anchor identifier + * + * @param string $anchor + * + * @return string + */ + public static function decodeAnchor($anchor) + { + return rawurldecode($anchor); + } + + /** + * Encode a URL parameter + * + * @param string $param + * + * @return string + */ + public static function encodeUrlParam($param) + { + return str_replace(array('%2F','%5C'), array('%252F','%255C'), rawurlencode($param)); + } + + /** + * Decode a URL parameter + * + * @param string $param + * + * @return string + */ + public static function decodeUrlParam($param) + { + return str_replace(array('%2F', '%5C'), array('/', '\\'), $param); + } + + /** + * Render to HTML + * + * Meant to be overwritten by concrete classes. + * + * @param View $view + * @param Zend_View_Helper_Url $zendUrlHelper + * + * @return string + */ + abstract public function render(View $view, Zend_View_Helper_Url $zendUrlHelper); +} From 8a229e15abceff7cb9974b9cfadef2738830543c Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:10:40 +0200 Subject: [PATCH 77/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Module\Doc; + +use RecursiveIteratorIterator; +use Zend_View_Helper_Url; +use Icinga\Web\View; + +/** + * toc renderer + */ +class TocRenderer extends Renderer +{ + /** + * The URL to replace links with + * + * @var string + */ + protected $url; + + /** + * Additional URL parameters + * + * @var array + */ + protected $urlParams; + + /** + * Content + * + * @var array + */ + protected $content = array(); + + /** + * Create a new toc renderer + * + * @param DocTree $docTree The documentation tree + * @param string $url The URL to replace links with + * @param array $urlParams Additional URL parameters + */ + public function __construct(DocTree $docTree, $url, array $urlParams) + { + parent::__construct($docTree, RecursiveIteratorIterator::SELF_FIRST); + $this->url = $url; + $this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams); + } + + public function beginIteration() + { + $this->content[] = '<nav><ul>'; + } + + public function endIteration() + { + $this->content[] = '</ul></nav>'; + } + + public function beginChildren() + { + $this->content[] = '<ul>'; + } + + public function endChildren() + { + $this->content[] = '</ul></li>'; + } + + /** + * 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( + '<li><a %shref="%s">%s</a>', + $section->isNofollow() ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $view->escape($section->getTitle()) + ); + if (! $this->getInnerIterator()->current()->hasChildren()) { + $this->content[] = '</li>'; + } + } + return implode("\n", $this->content); + } +} From 79f6130e3c510c96e827b5b3b51d3fc44c2e5e3b Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:11:15 +0200 Subject: [PATCH 78/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Module\Doc; + +require_once 'IcingaVendor/Parsedown/Parsedown.php'; + +use Icinga\Module\Doc\Exception\ChapterNotFoundException; +use RecursiveIteratorIterator; +use Parsedown; +use Zend_View_Helper_Url; +use Icinga\Web\View; + +/** + * preg_replace_callback helper to replace links + */ +class Callback +{ + protected $docTree; + + protected $view; + + protected $zendUrlHelper; + + protected $url; + + protected $urlParams; + + public function __construct( + DocTree $docTree, + View $view, + Zend_View_Helper_Url $zendUrlHelper, + $url, + array $urlParams) + { + $this->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( + '<a %s%shref="%s"', + strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', + $section->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 '<pre>' . highlight_string(htmlspecialchars_decode($match[1]), true) . '</pre>'; + } + + /** + * 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( + '<a name="%1$s"></a> <h%2$d>%3$s</h%2$d>', + Renderer::encodeAnchor($section->getId()), + $section->getLevel(), + $view->escape($section->getTitle()) + ); + $html = preg_replace_callback( + '#<pre><code class="language-php">(.*?)</code></pre>#s', + array($this, 'highlightPhp'), + $this->parsedown->text(implode('', $section->getContent())) + ); + $content[] = preg_replace_callback( + '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/', + array($callback, 'render'), + $html + ); + } + return implode("\n", $content); + } +} From 88312e6284b56941daf19f4cc64f43fbbe9dd134 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:11:59 +0200 Subject: [PATCH 79/96] 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 @@ +<?php +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}} + +namespace Icinga\Module\Doc; + +use Countable; +use RecursiveFilterIterator; +use Icinga\Data\Tree\NodeInterface; + +/** + * Recursive iterator over sections that are part of a particular chapter + */ +class SectionFilterIterator extends RecursiveFilterIterator implements Countable +{ + /** + * The chapter title to filter for + * + * @var string + */ + protected $chapterTitle; + + /** + * Create a new SectionFilterIterator + * + * @param NodeInterface $node Node + * @param string $chapterTitle The chapter title to filter for + */ + public function __construct(NodeInterface $node, $chapterTitle) + { + parent::__construct($node); + $this->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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:12:35 +0200 Subject: [PATCH 80/96] 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 @@ -<?php -// {{{ICINGA_LICENSE_HEADER}}} -// {{{ICINGA_LICENSE_HEADER}} - -namespace Icinga\Data\Tree; - -use Exception; -use RecursiveIteratorIterator; -use RuntimeException; - -/** - * A not yet customizable node renderer - */ -class NodeRenderer extends RecursiveIteratorIterator -{ - protected $content = array(); - - public function __construct(NodeInterface $node) - { - parent::__construct($node, RecursiveIteratorIterator::SELF_FIRST); - } - - public function beginIteration() - { - $this->content[] = '<ul>'; - } - - public function endIteration() - { - $this->content[] = '</ul>'; - } - - public function beginChildren() - { - $this->content[] = '<ul>'; - } - - public function endChildren() - { - $this->content[] = '</ul>'; - } - - 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:14:50 +0200 Subject: [PATCH 81/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:15:44 +0200 Subject: [PATCH 82/96] 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:17:03 +0200 Subject: [PATCH 83/96] 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 @@ +<div class="chapter"> + <?= $sectionRenderer->render($this, $this->getHelper('Url')); ?> +</div> 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 @@ -<?php -$urlHelper = $this->getHelper('Url'); -$view = $this; -?> -<div class="chapter"> -<?= preg_replace_callback( - '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/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( - '<a %s%shref="%s"', - strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', - $section->nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl() - ); - } - return $match[0]; - }, - $chapterHtml -); ?> -</div> 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 @@ -<div class="controls"> - <h1><?= $docName ?> documentation</h1> -</div> -<div class="content" data-base-target="_next"> - <?php - $urlHelper = $this->getHelper('Url'); - $view = $this; - ?> - <?= $tocRenderer->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( - '<li><a %shref="%s">%s</a></li>', - $section->nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl(), - $section->title - ); - }); ?> -</div> 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 @@ -<h1>Available documentations</h1> +<div class="controls"></div> +<h1><?= $this->translate('Available documentations'); ?></h1> <ul> <li><a href="<?= $this->href('doc/icingaweb/toc'); ?>">Icinga Web 2</a></li> - <li><a href="<?= $this->href('doc/module/'); ?>">Module documentations</a></li> + <li><a href="<?= $this->href('doc/module/'); ?>"><?= $this->translate('Module documentations'); ?></a></li> </ul> 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 @@ -<?php -$urlHelper = $this->getHelper('Url'); -$view = $this; -?> -<div class="chapter"> -<?= preg_replace_callback( - '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/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( - '<a %s%shref="%s"', - strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', - $section->nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl() - ); - } - return $match[0]; - }, - $chapterHtml -); ?> -</div> 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 @@ -<h1>Module documentations</h1> +<h1><?= $this->translate('Module documentations'); ?></h1> <ul> <?php foreach ($modules as $module): ?> <li> 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 @@ -<div class="controls"> - <h1><?= $docName ?> documentation</h1> -</div> -<div class="content" data-base-target="_next"> - <?php - $urlHelper = $this->getHelper('Url'); - $view = $this; - ?> - <?= $tocRenderer->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( - '<li><a %shref="%s">%s</a></li>', - $section->nofollow ? 'rel="nofollow" ' : '', - $url->getAbsoluteUrl(), - $section->title - ); - }); ?> -</div> 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 @@ +<h1><?= $docName ?> <?= $this->translate('Documentation'); ?></h1> +<div class="toc"> + <?= $tocRenderer->render($this, $this->getHelper('Url')); ?> +</div> +<div class="chapter"> + <?= $sectionRenderer->render($this, $this->getHelper('Url')); ?> +</div> 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 @@ +<div class="controls"> + <h1><?= $docName ?> <?= $this->translate('Documentation'); ?></h1> +</div> +<div class="content toc" data-base-target="_next"> + <?= $tocRenderer->render($this, $this->getHelper('Url')); ?> +</div> 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 <eric.lippmann@netways.de> Date: Mon, 28 Jul 2014 19:26:39 +0200 Subject: [PATCH 84/96] 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 55d3818ebb19379c586d5045a2a0ec39e95f50e9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 29 Jul 2014 11:10:06 +0200 Subject: [PATCH 85/96] 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 '<pre>' . highlight_string(htmlspecialchars_decode($match[1]), true) . '</pre>'; } + /** + * 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( + '/<img[^>]+>/', + array($this, 'replaceImg'), + $html + ); $content[] = preg_replace_callback( '/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/', array($callback, 'render'), From 38a6df91b9e0f2fd3b5210b70b16a67b04856b66 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 29 Jul 2014 11:10:49 +0200 Subject: [PATCH 86/96] 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 <eric.lippmann@netways.de> Date: Tue, 29 Jul 2014 11:12:06 +0200 Subject: [PATCH 87/96] 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 f6b9b13654f7f6e8d43ccd3c8b4b76d57b809ce2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 29 Jul 2014 11:19:40 +0200 Subject: [PATCH 88/96] 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 <eric.lippmann@netways.de> Date: Tue, 29 Jul 2014 11:45:16 +0200 Subject: [PATCH 89/96] 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 289aed84205924a57753a4fa527f1742f9e82b40 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 09:45:53 +0200 Subject: [PATCH 90/96] doc: Use `chapterId' instead of `chapterTitle' in URLs Manually given chapter IDs are meant to not change while a chapter's title could change. refs #4820 --- .../controllers/IcingawebController.php | 10 +++++----- .../application/controllers/ModuleController.php | 10 +++++----- modules/doc/library/Doc/DocController.php | 6 +++--- modules/doc/library/Doc/DocParser.php | 8 ++++---- modules/doc/library/Doc/Section.php | 16 ++++++++-------- .../doc/library/Doc/SectionFilterIterator.php | 14 +++++++------- modules/doc/library/Doc/SectionRenderer.php | 12 ++++++------ modules/doc/library/Doc/TocRenderer.php | 2 +- modules/doc/run.php | 4 ++-- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php index 55f60d36e..117d6eb0e 100644 --- a/modules/doc/application/controllers/IcingawebController.php +++ b/modules/doc/application/controllers/IcingawebController.php @@ -19,18 +19,18 @@ class Doc_IcingawebController extends DocController /** * View a chapter of Icinga Web 2's documentation * - * @throws Zend_Controller_Action_Exception + * @throws Zend_Controller_Action_Exception If the required parameter 'chapterId' is missing */ public function chapterAction() { - $chapterTitle = $this->getParam('chapterTitle'); - if ($chapterTitle === null) { + $chapterId = $this->getParam('chapterId'); + if ($chapterId === null) { throw new Zend_Controller_Action_Exception( - $this->translate('Missing parameter \'chapterTitle\''), + $this->translate('Missing parameter \'chapterId\''), 404 ); } - $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterTitle, 'doc/icingaweb/chapter'); + $this->renderChapter(Icinga::app()->getApplicationDir('/../doc'), $chapterId, 'doc/icingaweb/chapter'); } /** diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index 3a8bfc6af..7429dbdf2 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 'chapterTitle' is missing or if an error in + * @throws Zend_Controller_Action_Exception If the required parameter 'chapterId' 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('chapterTitle'); - if ($chapterTitle === null) { + $chapterId = $this->getParam('chapterId'); + if ($chapterId === null) { throw new Zend_Controller_Action_Exception( - $this->translate('Missing parameter \'chapterTitle\''), + $this->translate('Missing parameter \'chapterId\''), 404 ); } @@ -101,7 +101,7 @@ class Doc_ModuleController extends DocController try { $this->renderChapter( $moduleManager->getModuleDir($moduleName, '/doc'), - $chapterTitle, + $chapterId, 'doc/module/chapter', array('moduleName' => $moduleName) ); diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php index de2a7bac9..16824ce0c 100644 --- a/modules/doc/library/Doc/DocController.php +++ b/modules/doc/library/Doc/DocController.php @@ -12,16 +12,16 @@ class DocController extends ModuleActionController * Render a chapter * * @param string $path Path to the documentation - * @param string $chapterTitle Title of the chapter + * @param string $chapterId ID of the chapter * @param string $url * @param array $urlParams */ - protected function renderChapter($path, $chapterTitle, $url, array $urlParams = array()) + protected function renderChapter($path, $chapterId, $url, array $urlParams = array()) { $parser = new DocParser($path); $this->view->sectionRenderer = new SectionRenderer( $parser->getDocTree(), - SectionRenderer::decodeUrlParam($chapterTitle), + SectionRenderer::decodeUrlParam($chapterId), $url, $urlParams ); diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index d543f9f91..559a9a7d3 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -148,12 +148,12 @@ class DocParser $nofollow = false; } if ($stack->isEmpty()) { - $chapterTitle = $title; - $section = new Section($id, $title, $level, $nofollow, $chapterTitle); + $chapterId = $id; + $section = new Section($id, $title, $level, $nofollow, $chapterId); $tree->addRoot($section); } else { - $chapterTitle = $stack->bottom()->getTitle(); - $section = new Section($id, $title, $level, $nofollow, $chapterTitle); + $chapterId = $stack->bottom()->getId(); + $section = new Section($id, $title, $level, $nofollow, $chapterId); $tree->addChild($section, $stack->top()); } $stack->push($section); diff --git a/modules/doc/library/Doc/Section.php b/modules/doc/library/Doc/Section.php index 57419dc0a..ac458b6fe 100644 --- a/modules/doc/library/Doc/Section.php +++ b/modules/doc/library/Doc/Section.php @@ -40,11 +40,11 @@ class Section implements Identifiable protected $nofollow; /** - * The title of the chapter the section is part of + * The ID of the chapter the section is part of * * @var string */ - protected $chapterTitle; + protected $chapterId; /** * The content of the section @@ -60,15 +60,15 @@ class Section implements Identifiable * @param string $title The title of the section * @param int $level The header level * @param bool $nofollow Whether to instruct search engines to not index the link to the section - * @param string $chapterTitle The title of the chapter the section is part of + * @param string $chapterId The ID of the chapter the section is part of */ - public function __construct($id, $title, $level, $nofollow, $chapterTitle) + public function __construct($id, $title, $level, $nofollow, $chapterId) { $this->id = $id; $this->title = $title; $this->level = $level; $this->nofollow = $nofollow; - $this->chapterTitle= $chapterTitle; + $this->chapterId= $chapterId; } /** @@ -112,13 +112,13 @@ class Section implements Identifiable } /** - * The title of the chapter the section is part of + * The ID of the chapter the section is part of * * @return string */ - public function getChapterTitle() + public function getChapterId() { - return $this->chapterTitle; + return $this->chapterId; } /** diff --git a/modules/doc/library/Doc/SectionFilterIterator.php b/modules/doc/library/Doc/SectionFilterIterator.php index 9b5d2c013..e20d80359 100644 --- a/modules/doc/library/Doc/SectionFilterIterator.php +++ b/modules/doc/library/Doc/SectionFilterIterator.php @@ -14,22 +14,22 @@ use Icinga\Data\Tree\NodeInterface; class SectionFilterIterator extends RecursiveFilterIterator implements Countable { /** - * The chapter title to filter for + * The chapter ID to filter for * * @var string */ - protected $chapterTitle; + protected $chapterId; /** * Create a new SectionFilterIterator * * @param NodeInterface $node Node - * @param string $chapterTitle The chapter title to filter for + * @param string $chapterId The chapter ID to filter for */ - public function __construct(NodeInterface $node, $chapterTitle) + public function __construct(NodeInterface $node, $chapterId) { parent::__construct($node); - $this->chapterTitle = $chapterTitle; + $this->chapterId = $chapterId; } /** @@ -42,7 +42,7 @@ class SectionFilterIterator extends RecursiveFilterIterator implements Countable { $section = $this->getInnerIterator()->current()->getValue(); /* @var $section \Icinga\Module\Doc\Section */ - if ($section->getChapterTitle() === $this->chapterTitle) { + if ($section->getChapterId() === $this->chapterId) { return true; } return false; @@ -54,7 +54,7 @@ class SectionFilterIterator extends RecursiveFilterIterator implements Countable */ public function getChildren() { - return new static($this->getInnerIterator()->getChildren(), $this->chapterTitle); + return new static($this->getInnerIterator()->getChildren(), $this->chapterId); } /** diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index 31143e3c8..1b7c68f1b 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( - 'chapterTitle' => SectionRenderer::encodeUrlParam($section->getChapterTitle()) + 'chapterId' => SectionRenderer::encodeUrlParam($section->getChapterId()) ) ), $this->url, @@ -119,19 +119,19 @@ class SectionRenderer extends Renderer * 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|null $chapterId If not null, the chapter ID 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) + public function __construct(DocTree $docTree, $chapterId, $url, array $urlParams) { - if ($chapterTitle !== null) { - $filter = new SectionFilterIterator($docTree, $chapterTitle); + if ($chapterId !== null) { + $filter = new SectionFilterIterator($docTree, $chapterId); if ($filter->count() === 0) { throw new ChapterNotFoundException( - mt('doc', 'Chapter') . ' \'' . $chapterTitle . '\' ' . mt('doc', 'not found') + mt('doc', 'Chapter') . ' \'' . $chapterId . '\' ' . mt('doc', 'not found') ); } parent::__construct( diff --git a/modules/doc/library/Doc/TocRenderer.php b/modules/doc/library/Doc/TocRenderer.php index f018c556d..4b9d96a05 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( - 'chapterTitle' => $this->encodeUrlParam($section->getChapterTitle()) + 'chapterId' => $this->encodeUrlParam($section->getChapterId()) ) ), $this->url, diff --git a/modules/doc/run.php b/modules/doc/run.php index 959b56095..7392e4c22 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/:chapterTitle', + 'doc/module/:moduleName/chapter/:chapterId', array( 'controller' => 'module', 'action' => 'chapter', @@ -17,7 +17,7 @@ $docModuleChapter = new Zend_Controller_Router_Route( ); $docIcingaWebChapter = new Zend_Controller_Router_Route( - 'doc/icingaweb/chapter/:chapterTitle', + 'doc/icingaweb/chapter/:chapterId', array( 'controller' => 'icingaweb', 'action' => 'chapter', From cb17590b7174f902562521f87aabc13eb1cfe511 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 09:57:22 +0200 Subject: [PATCH 91/96] doc: rename `nofollow' to `noFollow' refs #4820 --- modules/doc/library/Doc/DocParser.php | 8 ++++---- modules/doc/library/Doc/Section.php | 12 ++++++------ modules/doc/library/Doc/SectionRenderer.php | 2 +- modules/doc/library/Doc/TocRenderer.php | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 559a9a7d3..8fa34ba77 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -143,17 +143,17 @@ class DocParser } $path[] = $title; $id = implode('-', $path); - $nofollow = true; + $noFollow = true; } else { - $nofollow = false; + $noFollow = false; } if ($stack->isEmpty()) { $chapterId = $id; - $section = new Section($id, $title, $level, $nofollow, $chapterId); + $section = new Section($id, $title, $level, $noFollow, $chapterId); $tree->addRoot($section); } else { $chapterId = $stack->bottom()->getId(); - $section = new Section($id, $title, $level, $nofollow, $chapterId); + $section = new Section($id, $title, $level, $noFollow, $chapterId); $tree->addChild($section, $stack->top()); } $stack->push($section); diff --git a/modules/doc/library/Doc/Section.php b/modules/doc/library/Doc/Section.php index ac458b6fe..5cf3d61e2 100644 --- a/modules/doc/library/Doc/Section.php +++ b/modules/doc/library/Doc/Section.php @@ -37,7 +37,7 @@ class Section implements Identifiable * * @var bool */ - protected $nofollow; + protected $noFollow; /** * The ID of the chapter the section is part of @@ -59,15 +59,15 @@ class Section implements Identifiable * @param string $id The ID of the section * @param string $title The title of the section * @param int $level The header level - * @param bool $nofollow Whether to instruct search engines to not index the link to the section + * @param bool $noFollow Whether to instruct search engines to not index the link to the section * @param string $chapterId The ID of the chapter the section is part of */ - public function __construct($id, $title, $level, $nofollow, $chapterId) + public function __construct($id, $title, $level, $noFollow, $chapterId) { $this->id = $id; $this->title = $title; $this->level = $level; - $this->nofollow = $nofollow; + $this->noFollow = $noFollow; $this->chapterId= $chapterId; } @@ -106,9 +106,9 @@ class Section implements Identifiable * * @return bool */ - public function isNofollow() + public function isNoFollow() { - return $this->nofollow; + return $this->noFollow; } /** diff --git a/modules/doc/library/Doc/SectionRenderer.php b/modules/doc/library/Doc/SectionRenderer.php index 1b7c68f1b..bc7bfb742 100644 --- a/modules/doc/library/Doc/SectionRenderer.php +++ b/modules/doc/library/Doc/SectionRenderer.php @@ -69,7 +69,7 @@ class Callback return sprintf( '<a %s%shref="%s"', strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '', - $section->isNofollow() ? 'rel="nofollow" ' : '', + $section->isNoFollow() ? 'rel="nofollow" ' : '', $url->getAbsoluteUrl() ); } diff --git a/modules/doc/library/Doc/TocRenderer.php b/modules/doc/library/Doc/TocRenderer.php index 4b9d96a05..b4ad4228c 100644 --- a/modules/doc/library/Doc/TocRenderer.php +++ b/modules/doc/library/Doc/TocRenderer.php @@ -96,7 +96,7 @@ class TocRenderer extends Renderer $url->setAnchor($this->encodeAnchor($section->getId())); $this->content[] = sprintf( '<li><a %shref="%s">%s</a>', - $section->isNofollow() ? 'rel="nofollow" ' : '', + $section->isNoFollow() ? 'rel="nofollow" ' : '', $url->getAbsoluteUrl(), $view->escape($section->getTitle()) ); From 5c52e447f5d01df93c8e101848193384a9373e2f Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 10:26:38 +0200 Subject: [PATCH 92/96] 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 @@ <div class="controls"> - <h1><?= $docName ?> <?= $this->translate('Documentation'); ?></h1> + <h1><?= $this->translate(sprintf('%s Documentation', ucfirst($docName))); ?></h1> </div> <div class="content toc" data-base-target="_next"> <?= $tocRenderer->render($this, $this->getHelper('Url')); ?> From 127e4f444f7536f1bba601c46e2a097bfa03f42c Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 11:30:56 +0200 Subject: [PATCH 93/96] 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 @@ <div class="controls"> <h1><?= $this->translate(sprintf('%s Documentation', ucfirst($docName))); ?></h1> </div> -<div class="content toc" data-base-target="_next"> +<div class="content toc"> <?= $tocRenderer->render($this, $this->getHelper('Url')); ?> </div> 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( - '<a name="%1$s"></a> <h%2$d>%3$s</h%2$d>', + '<a name="%1$s"></a><h%2$d>%3$s</h%2$d>', 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[] = '<ul class="navigation">'; + $this->docTree->prev(); + $prev = $this->docTree->current(); + if ($prev !== null) { + $prev = $prev->getValue(); + $path = $zendUrlHelper->url( + array_merge( + $this->urlParams, + array( + 'chapterId' => $this->encodeUrlParam($prev->getChapterId()) + ) + ), + $this->url, + false, + false + ); + $url = $view->url($path); + $url->setAnchor($this->encodeAnchor($prev->getId())); + $content[] = sprintf( + '<li><a %shref="%s">%s</a></li>', + $prev->isNoFollow() ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $view->escape($prev->getTitle()) + ); + $this->docTree->next(); + $this->docTree->next(); + } else { + $this->docTree->rewind(); + $this->docTree->next(); + } + $url = $view->url($this->tocUrl); + $content[] = sprintf( + '<li><a href="%s">%s</a></li>', + $url->getAbsoluteUrl(), + mt('doc', 'Index') + ); + $next = $this->docTree->current(); + if ($next !== null) { + $next = $next->getValue(); + $path = $zendUrlHelper->url( + array_merge( + $this->urlParams, + array( + 'chapterId' => $this->encodeUrlParam($next->getChapterId()) + ) + ), + $this->url, + false, + false + ); + $url = $view->url($path); + $url->setAnchor($this->encodeAnchor($next->getId())); + $content[] = sprintf( + '<li><a %shref="%s">%s</a></li>', + $next->isNoFollow() ? 'rel="nofollow" ' : '', + $url->getAbsoluteUrl(), + $view->escape($next->getTitle()) + ); + } + $content[] = '</ul>'; + 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 d35e0816c96619f6292bce4e8dd8d36220d970d8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 13:20:46 +0200 Subject: [PATCH 94/96] 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 <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 13:38:18 +0200 Subject: [PATCH 95/96] 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 @@ <div class="controls"> - <h1><?= $this->translate(sprintf('%s Documentation', ucfirst($docName))); ?></h1> + <h1><?= $title ?></h1> </div> <div class="content toc"> <?= $tocRenderer->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[] = '<ul class="navigation">'; + $navigation = array('<ul class="navigation">'); $this->docTree->prev(); $prev = $this->docTree->current(); if ($prev !== null) { @@ -240,8 +240,8 @@ class SectionRenderer extends Renderer ); $url = $view->url($path); $url->setAnchor($this->encodeAnchor($prev->getId())); - $content[] = sprintf( - '<li><a %shref="%s">%s</a></li>', + $navigation[] = sprintf( + '<li class="prev"><a %shref="%s">%s</a></li>', $prev->isNoFollow() ? 'rel="nofollow" ' : '', $url->getAbsoluteUrl(), $view->escape($prev->getTitle()) @@ -253,7 +253,7 @@ class SectionRenderer extends Renderer $this->docTree->next(); } $url = $view->url($this->tocUrl); - $content[] = sprintf( + $navigation[] = sprintf( '<li><a href="%s">%s</a></li>', $url->getAbsoluteUrl(), mt('doc', 'Index') @@ -274,14 +274,15 @@ class SectionRenderer extends Renderer ); $url = $view->url($path); $url->setAnchor($this->encodeAnchor($next->getId())); - $content[] = sprintf( - '<li><a %shref="%s">%s</a></li>', + $navigation[] = sprintf( + '<li class="next"><a %shref="%s">%s</a></li>', $next->isNoFollow() ? 'rel="nofollow" ' : '', $url->getAbsoluteUrl(), $view->escape($next->getTitle()) ); } - $content[] = '</ul>'; + $navigation[] = '</ul>'; + $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 <eric.lippmann@netways.de> Date: Tue, 19 Aug 2014 13:38:34 +0200 Subject: [PATCH 96/96] 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