diff options
Diffstat (limited to 'modules/doc/application')
13 files changed, 623 insertions, 0 deletions
diff --git a/modules/doc/application/controllers/IcingawebController.php b/modules/doc/application/controllers/IcingawebController.php new file mode 100644 index 0000000..e841c41 --- /dev/null +++ b/modules/doc/application/controllers/IcingawebController.php @@ -0,0 +1,62 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Doc\Controllers; + +use Icinga\Application\Icinga; +use Icinga\Module\Doc\DocController; + +class IcingawebController extends DocController +{ + /** + * Get the path to Icinga Web 2's documentation + * + * @return string + * + * @throws \Icinga\Exception\Http\HttpNotFoundException If Icinga Web 2's documentation is not available + */ + protected function getPath() + { + $path = Icinga::app()->getBaseDir('doc'); + if (is_dir($path)) { + return $path; + } + if (($path = $this->Config()->get('documentation', 'icingaweb2')) !== null) { + if (is_dir($path)) { + return $path; + } + } + $this->httpNotFound($this->translate('Documentation for Icinga Web 2 is not available')); + } + + /** + * View the toc of Icinga Web 2's documentation + */ + public function tocAction() + { + $this->renderToc($this->getPath(), 'Icinga Web 2', 'doc/icingaweb/chapter'); + } + + /** + * View a chapter of Icinga Web 2's documentation + * + * @throws \Icinga\Exception\MissingParameterException If the required parameter 'chapter' is missing + */ + public function chapterAction() + { + $chapter = $this->params->getRequired('chapter'); + $this->renderChapter( + $this->getPath(), + $chapter, + 'doc/icingaweb/chapter' + ); + } + + /** + * View Icinga Web 2's documentation as PDF + */ + public function pdfAction() + { + $this->renderPdf($this->getPath(), 'Icinga Web 2', 'doc/icingaweb/chapter'); + } +} diff --git a/modules/doc/application/controllers/IndexController.php b/modules/doc/application/controllers/IndexController.php new file mode 100644 index 0000000..3ff5aa1 --- /dev/null +++ b/modules/doc/application/controllers/IndexController.php @@ -0,0 +1,27 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Doc\Controllers; + +use Icinga\Module\Doc\DocController; +use Icinga\Web\Url; + +/** + * Documentation module index + */ +class IndexController extends DocController +{ + /** + * Documentation module landing page + * + * Lists documentation links + */ + public function indexAction() + { + $this->getTabs()->add('documentation', array( + 'active' => true, + 'title' => $this->translate('Documentation', 'Tab title'), + 'url' => Url::fromRequest() + )); + } +} diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php new file mode 100644 index 0000000..47dfb1c --- /dev/null +++ b/modules/doc/application/controllers/ModuleController.php @@ -0,0 +1,206 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Doc\Controllers; + +use finfo; +use SplFileInfo; +use Icinga\Application\Icinga; +use Icinga\Module\Doc\DocController; +use Icinga\Module\Doc\Exception\DocException; +use Icinga\Web\Url; + +class ModuleController extends DocController +{ + /** + * Get the path to a module documentation + * + * @param string $module The name of the module + * @param string $default The default path + * @param bool $suppressErrors Whether to not throw an exception if the module documentation is not available + * + * @return string|null Path to the documentation or null if the module documentation is not available + * and errors are suppressed + * + * @throws \Icinga\Exception\Http\HttpNotFoundException If the module documentation is not available and errors + * are not suppressed + */ + protected function getPath($module, $default, $suppressErrors = false) + { + if (is_dir($default)) { + return $default; + } + if (($path = $this->Config()->get('documentation', 'modules')) !== null) { + $path = str_replace('{module}', $module, $path); + if (is_dir($path)) { + return $path; + } + } + if ($suppressErrors) { + return null; + } + $this->httpNotFound($this->translate('Documentation for module \'%s\' is not available'), $module); + } + + /** + * List modules which are enabled and having the 'doc' directory + */ + public function indexAction() + { + $moduleManager = Icinga::app()->getModuleManager(); + $modules = array(); + foreach ($moduleManager->listInstalledModules() as $module) { + $path = $this->getPath($module, $moduleManager->getModuleDir($module, '/doc'), true); + if ($path !== null) { + $modules[] = $moduleManager->getModule($module, false); + } + } + $this->view->modules = $modules; + $this->getTabs()->add('module-documentation', array( + 'active' => true, + 'title' => $this->translate('Module Documentation', 'Tab title'), + 'url' => Url::fromRequest() + )); + } + + /** + * Assert that the given module is installed + * + * @param string $moduleName + * + * @throws \Icinga\Exception\Http\HttpNotFoundException If the given module is not installed + */ + protected function assertModuleInstalled($moduleName) + { + $moduleManager = Icinga::app()->getModuleManager(); + if (! $moduleManager->hasInstalled($moduleName)) { + $this->httpNotFound($this->translate('Module \'%s\' is not installed'), $moduleName); + } + } + + /** + * View the toc of a module's documentation + * + * @throws \Icinga\Exception\MissingParameterException If the required parameter 'moduleName' is empty + * @throws \Icinga\Exception\Http\HttpNotFoundException If the given module is not installed + * @see assertModuleInstalled() + */ + public function tocAction() + { + $module = $this->params->getRequired('moduleName'); + $this->assertModuleInstalled($module); + $moduleManager = Icinga::app()->getModuleManager(); + $name = $moduleManager->getModule($module, false)->getTitle(); + try { + $this->renderToc( + $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')), + $name, + 'doc/module/chapter', + array('moduleName' => $module) + ); + } catch (DocException $e) { + $this->httpNotFound($e->getMessage()); + } + } + + /** + * View a chapter of a module's documentation + * + * @throws \Icinga\Exception\MissingParameterException If one of the required parameters 'moduleName' and + * 'chapter' is empty + * @throws \Icinga\Exception\Http\HttpNotFoundException If the given module is not installed + * @see assertModuleInstalled() + */ + public function chapterAction() + { + $module = $this->params->getRequired('moduleName'); + $this->assertModuleInstalled($module); + $chapter = $this->params->getRequired('chapter'); + $this->view->moduleName = $module; + try { + $this->renderChapter( + $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')), + $chapter, + 'doc/module/chapter', + 'doc/module/img', + array('moduleName' => $module) + ); + } catch (DocException $e) { + $this->httpNotFound($e->getMessage()); + } + } + + /** + * Deliver images + */ + public function imageAction() + { + $module = $this->params->getRequired('moduleName'); + $image = $this->params->getRequired('image'); + $docPath = $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')); + $imagePath = realpath($docPath . '/' . $image); + if ($imagePath === false || substr($imagePath, 0, strlen($docPath)) !== $docPath) { + $this->httpNotFound('%s does not exist', $image); + } + + $this->_helper->viewRenderer->setNoRender(true); + $this->_helper->layout()->disableLayout(); + + $imageInfo = new SplFileInfo($imagePath); + + $etag = md5($imageInfo->getMTime() . $imagePath); + $lastModified = gmdate('D, d M Y H:i:s T', $imageInfo->getMTime()); + $match = false; + + if (isset($_SERER['HTTP_IF_NONE_MATCH'])) { + $ifNoneMatch = explode(', ', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])); + foreach ($ifNoneMatch as $tag) { + if ($tag === $etag) { + $match = true; + break; + } + } + } elseif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + $lastModifiedSince = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']); + if ($lastModifiedSince === $lastModified) { + $match = true; + } + } + + $this->getResponse() + ->setHeader('ETag', $etag) + ->setHeader('Cache-Control', 'no-transform,public,max-age=3600,must-revalidate', true); + + if ($match) { + $this->getResponse()->setHttpResponseCode(304); + } else { + $finfo = new finfo(); + $this->getResponse() + ->setHeader('Content-Type', $finfo->file($imagePath, FILEINFO_MIME_TYPE)) + ->setHeader('Content-Length', $imageInfo->getSize()) + ->sendHeaders(); + + ob_end_clean(); + readfile($imagePath); + } + } + + /** + * View a module's documentation as PDF + * + * @throws \Icinga\Exception\MissingParameterException If the required parameter 'moduleName' is empty + * @throws \Icinga\Exception\Http\HttpNotFoundException If the given module is not installed + * @see assertModuleInstalled() + */ + public function pdfAction() + { + $module = $this->params->getRequired('moduleName'); + $this->assertModuleInstalled($module); + $this->renderPdf( + $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')), + $module, + 'doc/module/chapter', + array('moduleName' => $module) + ); + } +} diff --git a/modules/doc/application/controllers/SearchController.php b/modules/doc/application/controllers/SearchController.php new file mode 100644 index 0000000..6ae2b14 --- /dev/null +++ b/modules/doc/application/controllers/SearchController.php @@ -0,0 +1,97 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Doc\Controllers; + +use Icinga\Application\Icinga; +use Icinga\Module\Doc\DocController; +use Icinga\Module\Doc\DocParser; +use Icinga\Module\Doc\Exception\DocException; +use Icinga\Module\Doc\Renderer\DocSearchRenderer; +use Icinga\Module\Doc\Search\DocSearch; +use Icinga\Module\Doc\Search\DocSearchIterator; + +class SearchController extends DocController +{ + /** + * Render search + */ + public function indexAction() + { + $parser = new DocParser($this->getWebPath()); + $search = new DocSearchRenderer( + new DocSearchIterator( + $parser->getDocTree()->getIterator(), + new DocSearch($this->params->get('q')) + ) + ); + $search->setUrl('doc/icingaweb/chapter'); + if (strlen($this->params->get('q')) < 3) { + $this->view->searches = array(); + return; + } + $searches = array( + 'Icinga Web 2' => $search + ); + foreach (Icinga::app()->getModuleManager()->listEnabledModules() as $module) { + if (($path = $this->getModulePath($module)) !== null) { + try { + $parser = new DocParser($path); + $search = new DocSearchRenderer( + new DocSearchIterator( + $parser->getDocTree()->getIterator(), + new DocSearch($this->params->get('q')) + ) + ); + } catch (DocException $e) { + continue; + } + $search + ->setUrl('doc/module/chapter') + ->setUrlParams(array('moduleName' => $module)); + $searches[$module] = $search; + } + } + $this->view->searches = $searches; + } + + /** + * Get the path to a module's documentation + * + * @param string $module + * + * @return string|null + */ + protected function getModulePath($module) + { + if (is_dir(($path = Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')))) { + return $path; + } + if (($path = $this->Config()->get('documentation', 'modules')) !== null) { + $path = str_replace('{module}', $module, $path); + if (is_dir($path)) { + return $path; + } + } + return null; + } + + /** + * Get the path to Icinga Web 2's documentation + * + * @return string + */ + protected function getWebPath() + { + $path = Icinga::app()->getBaseDir('doc'); + if (is_dir($path)) { + return $path; + } + if (($path = $this->Config()->get('documentation', 'icingaweb2')) !== null) { + if (is_dir($path)) { + return $path; + } + } + $this->httpNotFound($this->translate('Documentation for Icinga Web 2 is not available')); + } +} diff --git a/modules/doc/application/controllers/StyleController.php b/modules/doc/application/controllers/StyleController.php new file mode 100644 index 0000000..5890367 --- /dev/null +++ b/modules/doc/application/controllers/StyleController.php @@ -0,0 +1,41 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Doc\Controllers; + +use Icinga\Application\Icinga; +use Icinga\Web\Controller; +use Icinga\Web\Widget; + +class StyleController extends Controller +{ + public function guideAction() + { + $this->view->tabs = $this->tabs()->activate('guide'); + } + + public function fontAction() + { + $this->view->tabs = $this->tabs()->activate('font'); + $confFile = Icinga::app()->getApplicationDir('fonts/fontello-ifont/config.json'); + $this->view->font = json_decode(file_get_contents($confFile)); + } + + protected function tabs() + { + return Widget::create('tabs')->add( + 'guide', + array( + 'label' => $this->translate('Style Guide'), + 'url' => 'doc/style/guide' + ) + )->add( + 'font', + array( + 'label' => $this->translate('Icons'), + 'title' => $this->translate('List all available icons'), + 'url' => 'doc/style/font' + ) + ); + } +} diff --git a/modules/doc/application/views/scripts/chapter.phtml b/modules/doc/application/views/scripts/chapter.phtml new file mode 100644 index 0000000..8cd4f6e --- /dev/null +++ b/modules/doc/application/views/scripts/chapter.phtml @@ -0,0 +1,6 @@ +<div class="controls"> + <?= /** @var \Icinga\Web\Widget\Tabs $tabs */ $this->tabs ?> +</div> +<div class="content chapter"> + <?= /** @var \Icinga\Module\Doc\Renderer\DocSectionRenderer $section */ $section ?> +</div> diff --git a/modules/doc/application/views/scripts/index/index.phtml b/modules/doc/application/views/scripts/index/index.phtml new file mode 100644 index 0000000..9bf745a --- /dev/null +++ b/modules/doc/application/views/scripts/index/index.phtml @@ -0,0 +1,19 @@ +<div class="controls"> + <?= /** @var \Icinga\Web\Widget\Tabs $tabs */ $tabs ?> +</div> +<div class="content"> + <ul> + <li><?= $this->qlink( + 'Icinga Web 2', + 'doc/icingaweb/toc', + null, + array('title' => $this->translate('Show the documentation\'s table of contents for Icinga Web 2')) + ) ?></li> + <li><?= $this->qlink( + $this->translate('Module documentations'), + 'doc/module/', + null, + array('title' => $this->translate('List all modules for which documentation is available')) + ) ?></li> + </ul> +</div> diff --git a/modules/doc/application/views/scripts/module/index.phtml b/modules/doc/application/views/scripts/module/index.phtml new file mode 100644 index 0000000..f70d69a --- /dev/null +++ b/modules/doc/application/views/scripts/module/index.phtml @@ -0,0 +1,19 @@ +<div class="controls"> + <?= $this->tabs ?> +</div> + +<div class="content"> + <ul> + <?php foreach ($modules as $module): /** @var \Icinga\Application\Modules\Module $module */ ?> + <li><?= $this->qlink( + $module->getTitle(), + 'doc/module/toc', + array('moduleName' => $module->getName()), + array('title' => sprintf( + $this->translate('Show the documentation\'s table of contents for the %s'), + $module->getTitle() + )) + ) ?></li> + <?php endforeach ?> + </ul> +</div> diff --git a/modules/doc/application/views/scripts/pdf.phtml b/modules/doc/application/views/scripts/pdf.phtml new file mode 100644 index 0000000..2666efb --- /dev/null +++ b/modules/doc/application/views/scripts/pdf.phtml @@ -0,0 +1,5 @@ +<div class="content"> + <h1><?= /** @var string $title */ $title ?></h1> + <?= /** @var \Icinga\Module\Doc\Renderer\DocTocRenderer $toc */ $toc ?> + <?= /** @var \Icinga\Module\Doc\Renderer\DocSectionRenderer $section */ $section ?> +</div> diff --git a/modules/doc/application/views/scripts/search/index.phtml b/modules/doc/application/views/scripts/search/index.phtml new file mode 100644 index 0000000..c613f04 --- /dev/null +++ b/modules/doc/application/views/scripts/search/index.phtml @@ -0,0 +1,8 @@ +<div class="content"> + <?php foreach (/** @var \Icinga\Module\Doc\Renderer\DocSearchRenderer[] $searches */ $searches as $title => $search): ?> + <h2><?= $this->escape($title) ?></h2> + <?= $search->isEmpty() + ? $this->translate('No documentation found matching the filter') + : $search ?> + <?php endforeach ?> +</div> diff --git a/modules/doc/application/views/scripts/style/font.phtml b/modules/doc/application/views/scripts/style/font.phtml new file mode 100644 index 0000000..c84a983 --- /dev/null +++ b/modules/doc/application/views/scripts/style/font.phtml @@ -0,0 +1,15 @@ +<div class="controls"> +<?= $this->tabs ?> +<h1>Icinga Web 2 Icons</h1> +</div> + +<div class="content"> +<?php foreach ($this->font->glyphs as $icon): ?> +<!-- TODO: move CSS away //--> +<div style="width: 33%; font-size: 1.5em; height: 2em; float: left;" class="<?= + $this->font->css_prefix_text . $icon->css +?>"> +<?= $this->escape($icon->css) ?> <span style="font-size: 0.6em">(0x<?= dechex($icon->code) ?>)</span> +</div> +<?php endforeach ?> +</div> diff --git a/modules/doc/application/views/scripts/style/guide.phtml b/modules/doc/application/views/scripts/style/guide.phtml new file mode 100644 index 0000000..f2f57d2 --- /dev/null +++ b/modules/doc/application/views/scripts/style/guide.phtml @@ -0,0 +1,112 @@ +<div class="controls"> + <?= $this->tabs ?> +</div> + +<div class="content styleguide"> + <div class="section"> + <h1>Icinga Web 2 Design Guidelines</h1> + + <ul class="toc"> + <li><a href="#headings">Headings</a></li> + <li><a href="#block-content">Block Content</a></li> + <li><a href="#tables">Tables</a></li> + <li><a href="#comment-list">Comment List</a></li> + <li><a href="#blockquote">Blockquote</a></li> + </ul> + </div> + + <div class="section"> + <h2 id="headings">Headings</h2> + <h1>Header h1</h1> + <h2>Header h2</h2> + <h3>Header h3</h3> + <h4>Header h4</h4> + <h5>Header h5</h5> + <h6>Header h6</h6> + </div> + + <div class="section"> + <h2 id="block-content">Block Content</h2> + <h3>Paragraph</h3> + <p> + This is a paragraph. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor + invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo + dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + A <a href="#">link inside a paragraph</a>. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et + dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + </p> + </div> + + <div class="section"> + <h2 id="tables">Tables</h2> + <table class="common-table"> + <thead> + <tr> + <th>Table Head - th in thead</th> + <td>td in thead<td> + </tr> + </thead> + <tbody> + <tr> + <th>Tbody - th</th> + <td>Tbody - td</td> + </tr> + <tr> + <th>Tbody - th</th> + <td>Tbody - td</td> + </tr> + <tr> + <th>Tbody - th</th> + <td>Tbody - td</td> + </tr> + </tbody> + </table> + </div> + + <div class="section"> + <h2 id="comment-list"><?= $this->translate('Comment List') ?></h2> + <dl class="comment-list"> + <dt> + John Doe + <span class="comment-time"> + <?= $this->translate('commented') ?> + <span class="relative-time"><?= $this->translate('some time ago') ?></span> + </span> + <i class="remove-action icon-cancel"></i> + </dt> + <dd> + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore + <br> + et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + </dd> + <dt> + Richard Roe + <span class="comment-time"> + <?= $this->translate('commented') ?> + <span class="relative-time"><?= $this->translate('some time ago') ?></span> + </span> + <i class="remove-action icon-cancel"></i> + </dt> + <dd> + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore + <br> + et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + </dd> + </dl> + </div> + + <div class="section"> + <h2 id="blockquote"><?= $this->translate('Blockquote') ?></h2> + <blockquote> + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor + invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. + At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, + consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore + magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + </blockquote> + </div> +</div> diff --git a/modules/doc/application/views/scripts/toc.phtml b/modules/doc/application/views/scripts/toc.phtml new file mode 100644 index 0000000..d08830b --- /dev/null +++ b/modules/doc/application/views/scripts/toc.phtml @@ -0,0 +1,6 @@ +<div class="controls"> + <?= /** @var \Icinga\Web\Widget\Tabs $tabs */ $tabs ?> +</div> +<div class="content"> + <?= /** @var \Icinga\Module\Doc\Renderer\DocTocRenderer $toc */ $toc ?> +</div> |