From b0ecbe079f7fa89971639adab804b3cfc5470cb5 Mon Sep 17 00:00:00 2001 From: Markus Frosch <markus.frosch@icinga.com> Date: Wed, 7 Mar 2018 15:55:15 +0100 Subject: [PATCH] PluginOutput: Fix text splicing for status tags in HTML output Trailing text was lost in processing. Also add tests to check this behavior plus some basics. fixes #3382 --- .../views/helpers/PluginOutput.php | 12 ++ .../views/helpers/PluginOutputTest.php | 141 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 modules/monitoring/test/php/application/views/helpers/PluginOutputTest.php diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index cecf0262b..1c77b578d 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -143,16 +143,28 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $offsetLeft = $match[0][1]; $matchLength = strlen($match[0][0]); $leftLength = $offsetLeft - $start; + // if there is text before the match if ($leftLength) { + // create node for leading text $text = new DOMText(substr($node->nodeValue, $start, $leftLength)); $node->parentNode->insertBefore($text, $node); } + // create the new element for the match $span = $doc->createElement('span', $match[0][0]); $span->setAttribute('class', 'state-' . strtolower($match[1][0])); $node->parentNode->insertBefore($span, $node); + + // start for next match $start = $offsetLeft + $matchLength; } if ($start) { + // is there text left? + if (strlen($node->nodeValue) > $start) { + // create node for trailing text + $text = new DOMText(substr($node->nodeValue, $start)); + $node->parentNode->insertBefore($text, $node); + } + // delete the old node later $nodesToRemove[] = $node; } } elseif ($node->nodeType === XML_ELEMENT_NODE) { diff --git a/modules/monitoring/test/php/application/views/helpers/PluginOutputTest.php b/modules/monitoring/test/php/application/views/helpers/PluginOutputTest.php new file mode 100644 index 000000000..9a3e4a0fa --- /dev/null +++ b/modules/monitoring/test/php/application/views/helpers/PluginOutputTest.php @@ -0,0 +1,141 @@ +<?php +/* Icinga Web 2 | (c) 2018 Icinga Development Team | GPLv2+ */ + +namespace Tests\Icinga\Module\Monitoring\Application\Views\Helpers; + +use Icinga\Web\View; +use Zend_View_Helper_PluginOutput; +use Icinga\Test\BaseTestCase; + +require_once realpath(BaseTestCase::$moduleDir . '/monitoring/application/views/helpers/PluginOutput.php'); + +class PluginOutputTest extends BaseTestCase +{ + /** @var Zend_View_Helper_PluginOutput */ + protected $helper; + + const PREFIX_PRE = '<div class="plugin-output preformatted">'; + const PREFIX = '<div class="plugin-output">'; + const SUFFIX = '</div>'; + + protected static $statusTags = array('OK', 'WARNING', 'CRITICAL', 'UNKNOWN', 'UP', 'DOWN'); + + public function setUp() + { + parent::setUp(); + + $this->helper = $h = new Zend_View_Helper_PluginOutput; + $h->setView(new View()); + } + + protected function checkOutput($output, $html, $regexp = false, $isHtml = false) + { + $actual = $this->helper->pluginOutput($output); + + if ($isHtml) { + $prefix = self::PREFIX; + } else { + $prefix = self::PREFIX_PRE; + } + + if ($regexp) { + $expect = sprintf( + '~%s%s%s~', + preg_quote($prefix, '~'), + $html, + preg_quote(self::SUFFIX, '~') + ); + $this->assertRegExp($expect, $actual, 'Output must match example regexp'); + } else { + $expect = $prefix . $html . self::SUFFIX; + $this->assertEquals($expect, $actual, 'Output must match example'); + } + } + + protected function checkHtmlOutput($outputHtml, $html, $regexp = false) + { + return $this->checkOutput($outputHtml, $html, $regexp, true); + } + + public function testSimpleOutput() + { + $this->checkOutput( + 'Foobar', + 'Foobar' + ); + } + + public function testSimpleHtmlOutput() + { + /** @noinspection HtmlUnknownAttribute */ + $this->checkHtmlOutput( + 'OK - Teststatus <a href="http://localhost/test.php" target="_blank">Info</a>', + 'OK - Teststatus <a href="http://localhost/test.php" target="_blank"[^>]*>Info</a>', + true + ); + } + + public function testMultilineHtmlOutput() + { + $input = array( + 'Teststatus', + '<a href="http://localhost/test.php" target="_blank">Info</a><br/><br/>', + '<a href="http://localhost/test2.php" target="_blank">Info2</a>' + ); + /** @noinspection HtmlUnknownAttribute */ + $output = array( + 'Teststatus', + '<a href="http://localhost/test.php" target="_blank"[^>]*>Info</a>', + '', + '', + '<a href="http://localhost/test2.php" target="_blank"[^>]*>Info2</a>' + ); + $this->checkHtmlOutput( + join("\n", $input), + join("\n", $output), + true + ); + } + + public function testHtmlTable() + { + $this->markTestIncomplete(); + } + + public function testAllowedHtmlTags() + { + $this->markTestIncomplete(); + } + + public function testTextStatusTags() + { + foreach (self::$statusTags as $s) { + $l = strtolower($s); + $this->checkOutput( + sprintf('[%s] Test', $s), + sprintf('<span class="state-%s">[%s]</span> Test', $l, $s) + ); + $this->checkOutput( + sprintf('(%s) Test', $s), + sprintf('<span class="state-%s">(%s)</span> Test', $l, $s) + ); + } + } + + public function testHtmlStatusTags() + { + $dummyHtml = '<a href="#"></a>'; + + foreach (self::$statusTags as $s) { + $l = strtolower($s); + $this->checkHtmlOutput( + sprintf('%s [%s] Test', $dummyHtml, $s), + sprintf('%s <span class="state-%s">[%s]</span> Test', $dummyHtml, $l, $s) + ); + $this->checkHtmlOutput( + sprintf('%s (%s) Test', $dummyHtml, $s), + sprintf('%s <span class="state-%s">(%s)</span> Test', $dummyHtml, $l, $s) + ); + } + } +}