Skip to content

Commit

Permalink
wip: test: can preg_replace sufficient
Browse files Browse the repository at this point in the history
  • Loading branch information
sukhwinder33445 committed Jun 24, 2024
1 parent b99e4a7 commit 790e7a9
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 109 deletions.
116 changes: 9 additions & 107 deletions library/Icingadb/Util/PluginOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@

namespace Icinga\Module\Icingadb\Util;

use DOMDocument;
use DOMNode;
use DOMText;
use Icinga\Module\Icingadb\Hook\PluginOutputHook;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Web\Dom\DomNodeIterator;
use Icinga\Web\Helper\HtmlPurifier;
use InvalidArgumentException;
use ipl\Html\HtmlString;
use ipl\Orm\Model;
use LogicException;
use RecursiveIteratorIterator;

class PluginOutput extends HtmlString
{
Expand Down Expand Up @@ -46,18 +41,6 @@ class PluginOutput extends HtmlString
'@@@@@@'
];

/** @var string[] Patterns to be replaced in html plugin output */
const HTML_PATTERNS = [
'~\\\t~',
'~\\\n~'
];

/** @var string[] Replacements for {@see PluginOutput::HTML_PATTERNS} */
const HTML_REPLACEMENTS = [
"\t",
"\n"
];

/** @var string Already rendered output */
protected $renderedOutput;

Expand Down Expand Up @@ -178,104 +161,23 @@ public function render()
$output = substr($output, 0, $this->characterLimit);
}

if (preg_match('~<\w+(?>\s\w+=[^>]*)?>~', $output)) {
// HTML
$output = HtmlPurifier::process(preg_replace(
self::HTML_PATTERNS,
self::HTML_REPLACEMENTS,
$output
));
$this->isHtml = true;
} else {
// Plaintext
$output = preg_replace(
self::TEXT_PATTERNS,
self::TEXT_REPLACEMENTS,
htmlspecialchars($output, ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5, null, false)
);
$this->isHtml = false;
}
$output = preg_replace(
self::TEXT_PATTERNS,
self::TEXT_REPLACEMENTS,
$output
);

$output = trim($output);
$this->isHtml = (bool) preg_match('~<\w+(?>\s\w+=[^>]*)?>~', $output);
if ($this->isHtml) {
$output = HtmlPurifier::process($output);
}

// Add zero-width space after commas which are not followed by a whitespace character
// in oder to help browsers to break words in plugin output
$output = preg_replace('/,(?=[^\s])/', ',&#8203;', $output);

if ($this->enrichOutput && $this->isHtml) {
$output = $this->processHtml($output);
}

$this->renderedOutput = $output;

return $output;
}

/**
* Replace color state information, if any
*
* @param string $html
*
* @todo Do we really need to create a DOM here? Or is a preg_replace like we do it for text also feasible?
* @return string
*/
protected function processHtml(string $html): string
{
$pattern = '/[([](OK|WARNING|CRITICAL|UNKNOWN|UP|DOWN)[)\]]/';
$doc = new DOMDocument();
$doc->loadXML('<div>' . $html . '</div>', LIBXML_NOERROR | LIBXML_NOWARNING);
$dom = new RecursiveIteratorIterator(new DomNodeIterator($doc), RecursiveIteratorIterator::SELF_FIRST);

$nodesToRemove = [];
foreach ($dom as $node) {
/** @var DOMNode $node */
if ($node->nodeType !== XML_TEXT_NODE) {
continue;
}

$start = 0;
while (preg_match($pattern, $node->nodeValue, $match, PREG_OFFSET_CAPTURE, $start)) {
$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 state ball for the match
$span = $doc->createElement('span');
$span->setAttribute(
'class',
'state-ball ball-size-m 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;
}
}

foreach ($nodesToRemove as $node) {
/** @var DOMNode $node */
$node->parentNode->removeChild($node);
}

return substr($doc->saveHTML(), 5, -7);
}
}
5 changes: 3 additions & 2 deletions test/php/library/Icingadb/Util/PluginOutputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Tests\Icinga\Module\Icingadb\Util;

use Icinga\Module\Icingadb\Util\PluginOutput;
use Icinga\Web\Helper\HtmlPurifier;
use PHPUnit\Framework\TestCase;

class PluginOutputTest extends TestCase
Expand All @@ -31,7 +32,7 @@ public function testRenderTextWithStates()

$expectedOutput = <<<'EXPECTED_OUTPUT'
<span class="state-ball ball-size-m state-ok"></span> Dummy state
\_ <span class="state-ball ball-size-m state-ok"></span> Fake &quot;state&quot;
\_ <span class="state-ball ball-size-m state-ok"></span> Fake "state"
\_ <span class="state-ball ball-size-m state-warning"></span> Fake state again
EXPECTED_OUTPUT;

Expand Down Expand Up @@ -115,7 +116,7 @@ public function testRenderTextWithHtmlIncludingStatesAndSpecialChars()
$expectedOutput = <<<'EXPECTED_OUTPUT'
Hello <h3>World</h3>, this "is" a <strong>test</strong>.
<span class="state-ball ball-size-m state-ok"></span> Dummy state
special chars: !@#$%^&amp;*()_+{}|:"&lt;&gt;?`-=[]\;',&#x200B;./
special chars: !@#$%^&amp;*()_+{}|:"&lt;&gt;?`-=[]\;',&#8203;./
text <span> ends </span> here
EXPECTED_OUTPUT;

Expand Down

0 comments on commit 790e7a9

Please sign in to comment.