From b902913220d05e1d4a277706de3e86ef563b747c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 13:18:27 +0200 Subject: [PATCH 1/6] doc: Support setext-style only headers Setext-style used to work before too but only if an atx-style header comes first. --- modules/doc/library/Doc/DocParser.php | 46 ++++++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index a35ebc05b..038ae3e02 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Doc; +use CachingIterator; use LogicException; use SplStack; use Icinga\Data\Tree\SimpleTree; @@ -15,6 +16,20 @@ use Icinga\Module\Doc\Exception\DocException; */ class DocParser { + /** + * Internal identifier for Atx-style headers + * + * @var int + */ + const HEADER_ATX = 1; + + /** + * Internal identifier for Setext-style headers + * + * @var int + */ + const HEADER_SETEXT = 2; + /** * Path to the documentation * @@ -70,11 +85,11 @@ class DocParser * Extract atx- or setext-style headers from the given lines * * @param string $line - * @param string $lastLine + * @param string $nextLine * * @return array|null An array containing the header and the header level or null if there's nothing to extract */ - protected function extractHeader($line, $lastLine) + protected function extractHeader($line, $nextLine) { if (! $line) { return null; @@ -90,13 +105,14 @@ class DocParser if (! $header) { return null; } + $headerStyle = static::HEADER_ATX; } elseif ( - $line - && ($line[0] === '=' || $line[0] === '-') - && preg_match('/^[=-]+\s*$/', $line, $match) === 1 + $nextLine + && ($nextLine[0] === '=' || $nextLine[0] === '-') + && preg_match('/^[=-]+\s*$/', $nextLine, $match) === 1 ) { // Setext - $header = trim($lastLine); + $header = trim($line); if (! $header) { return null; } @@ -105,6 +121,7 @@ class DocParser } else { $level = 2; } + $headerStyle = static::HEADER_SETEXT; } if ($header === null) { return null; @@ -117,7 +134,7 @@ class DocParser } else { $id = null; } - return array($header, $id, $level); + return array($header, $id, $level, $headerStyle); } /** @@ -133,10 +150,13 @@ class DocParser /** @var $fileInfo \SplFileInfo */ $file = $fileInfo->openFile(); $lastLine = null; - foreach ($file as $line) { - $header = $this->extractHeader($line, $lastLine); + $cachingIterator = new CachingIterator($file, CachingIterator::TOSTRING_USE_CURRENT); + for ($cachingIterator->rewind(); $line = $cachingIterator->valid(); $cachingIterator->next()) { + $fileIterator = $cachingIterator->getInnerIterator(); + $line = $cachingIterator->current(); + $header = $this->extractHeader($line, $fileIterator->valid() ? $fileIterator->current() : null); if ($header !== null) { - list($title, $id, $level) = $header; + list($title, $id, $level, $headerStyle) = $header; while (! $stack->isEmpty() && $stack->top()->getLevel() >= $level) { $stack->pop(); } @@ -169,14 +189,16 @@ class DocParser $tree->addChild($section, $stack->top()); } $stack->push($section); + if ($headerStyle === static::HEADER_SETEXT) { + $cachingIterator->next(); + continue; + } } else { if ($stack->isEmpty()) { throw new LogicException('Heading required'); } $stack->top()->appendContent($line); } - // Save last line for setext-style headers - $lastLine = $line; } } return $tree; From 5ef8f95e5b2ecd21f9ea739fdff149929e22ea7c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 13:22:16 +0200 Subject: [PATCH 2/6] doc: Replace whitespaces with - for section IDs Fixes jQuery selector issues when jumping to an anchor. --- modules/doc/library/Doc/DocSection.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/doc/library/Doc/DocSection.php b/modules/doc/library/Doc/DocSection.php index 4121fd69e..a8efc6fc3 100644 --- a/modules/doc/library/Doc/DocSection.php +++ b/modules/doc/library/Doc/DocSection.php @@ -88,6 +88,14 @@ class DocSection extends TreeNode return $this->content; } + /** + * {@inheritdoc} + */ + public function setId($id) + { + return parent::setId(str_replace(' ', '-', (string) $id)); + } + /** * Set the header level * From d90790e86d0a113be0bc5b48ab5691b0059ae824 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 13:23:14 +0200 Subject: [PATCH 3/6] doc: Support header-less sections --- modules/doc/library/Doc/DocParser.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index 038ae3e02..bffe86db0 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -145,11 +145,11 @@ class DocParser public function getDocTree() { $tree = new SimpleTree(); - $stack = new SplStack(); foreach ($this->docIterator as $fileInfo) { /** @var $fileInfo \SplFileInfo */ $file = $fileInfo->openFile(); $lastLine = null; + $stack = new SplStack(); $cachingIterator = new CachingIterator($file, CachingIterator::TOSTRING_USE_CURRENT); for ($cachingIterator->rewind(); $line = $cachingIterator->valid(); $cachingIterator->next()) { $fileIterator = $cachingIterator->getInnerIterator(); @@ -195,7 +195,20 @@ class DocParser } } else { if ($stack->isEmpty()) { - throw new LogicException('Heading required'); + $title = ucfirst($file->getBasename('.' . pathinfo($file->getFilename(), PATHINFO_EXTENSION))); + $id = $title; + if ($tree->getNode($id) !== null) { + $id = uniqid($id); + } + $section = new DocSection(); + $section + ->setId($id) + ->setTitle($title) + ->setLevel(1) + ->setNoFollow(true); + $section->setChapter($section); + $tree->addChild($section); + $stack->push($section); } $stack->top()->appendContent($line); } From b0a75dd89bb6c7f494d8aee2d126271326198ff0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 13:25:08 +0200 Subject: [PATCH 4/6] lib: Fix PHPDoc in ConfigObject --- library/Icinga/Data/ConfigObject.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Data/ConfigObject.php b/library/Icinga/Data/ConfigObject.php index de00e5b5e..230d7e979 100644 --- a/library/Icinga/Data/ConfigObject.php +++ b/library/Icinga/Data/ConfigObject.php @@ -173,8 +173,10 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess /** * Add a new property or section * - * @param string $key The name of the new property or section - * @param mixed $value The value to set for the new property or section + * @param string $key The name of the new property or section + * @param mixed $value The value to set for the new property or section + * + * @throws ProgrammingError If the key is null */ public function offsetSet($key, $value) { @@ -254,7 +256,7 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess /** * Merge the given data with this config * - * @param array|Config $data An array or a config + * @param array|ConfigObject $data An array or a config * * @return $this */ From 1363ea4370b8f92fc3b6e79abb4cafd71949dc0f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 14:02:19 +0200 Subject: [PATCH 5/6] Menu: Defer construction of the Url object to Menu::getUrl() Before, the Url object was constructed in Menu::setUrl() which lead to an exception when parsing a module's configuration.php from our CLI. refs #9375 --- library/Icinga/Web/Menu.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/Icinga/Web/Menu.php b/library/Icinga/Web/Menu.php index 3ad8d347f..7992364b9 100644 --- a/library/Icinga/Web/Menu.php +++ b/library/Icinga/Web/Menu.php @@ -43,7 +43,7 @@ class Menu implements RecursiveIterator /** * The url of this menu * - * @var string + * @var string|null */ protected $url; @@ -404,21 +404,20 @@ class Menu implements RecursiveIterator */ public function setUrl($url) { - if ($url instanceof Url) { - $this->url = $url; - } else { - $this->url = Url::fromPath($url); - } + $this->url = $url; return $this; } /** * Return the url of this menu * - * @return Url + * @return Url|null */ public function getUrl() { + if ($this->url !== null && ! $this->url instanceof Url) { + $this->url = Url::fromPath($this->url); + } return $this->url; } From aa4e3c5a22100ed76e0bb537a1c49730110a76be Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 10 Aug 2015 14:05:08 +0200 Subject: [PATCH 6/6] Dashboard: Defer construction of the Url object to Dashlet::getUrl() Before, the Url object was constructed in Dashlet::setUrl() and Dashlet::__construct8) which lead to an exception when parsing a module's configuration.php from our CLI. refs #9375 --- .../Icinga/Web/Widget/Dashboard/Dashlet.php | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/library/Icinga/Web/Widget/Dashboard/Dashlet.php b/library/Icinga/Web/Widget/Dashboard/Dashlet.php index d972cc5d6..06d6851e2 100644 --- a/library/Icinga/Web/Widget/Dashboard/Dashlet.php +++ b/library/Icinga/Web/Widget/Dashboard/Dashlet.php @@ -20,7 +20,7 @@ class Dashlet extends UserWidget /** * The url of this Dashlet * - * @var \Icinga\Web\Url + * @var Url|null */ private $url; @@ -74,16 +74,13 @@ EOD; { $this->title = $title; $this->pane = $pane; - if ($url instanceof Url) { - $this->url = $url; - } elseif ($url) { - $this->url = Url::fromPath($url); - } else { + if (! $url) { throw new IcingaException( 'Cannot create dashboard dashlet "%s" without valid URL', $title ); } + $this->url = $url; } /** @@ -107,10 +104,13 @@ EOD; /** * Retrieve the dashlets url * - * @return Url + * @return Url|null */ public function getUrl() { + if ($this->url !== null && ! $this->url instanceof Url) { + $this->url = Url::fromPath($this->url); + } return $this->url; } @@ -123,11 +123,7 @@ EOD; */ public function setUrl($url) { - if ($url instanceof Url) { - $this->url = $url; - } else { - $this->url = Url::fromPath($url); - } + $this->url = $url; return $this; } @@ -159,7 +155,7 @@ EOD; public function toArray() { $array = array( - 'url' => $this->url->getRelativeUrl(), + 'url' => $this->getUrl()->getRelativeUrl(), 'title' => $this->getTitle() ); if ($this->getDisabled() === true) { @@ -178,9 +174,9 @@ EOD; } $view = $this->view(); - $url = clone($this->url); + $url = $this->getUrl(); $url->setParam('view', 'compact'); - $iframeUrl = clone($url); + $iframeUrl = clone $url; $iframeUrl->setParam('isIframe'); $searchTokens = array(