From 9a1a2f1e2937b4558475515301791a3b27ffe568 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Fri, 19 Aug 2016 19:02:13 +0200 Subject: [PATCH] Added missing search keyword highlighting --- .../bbcode/KeywordHighlighter.class.php | 9 ++ .../html/output/HtmlOutputProcessor.class.php | 10 +- .../node/HtmlOutputNodeProcessor.class.php | 98 +++++++++++++++++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php index d4df438321..086ccede2e 100644 --- a/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php @@ -135,4 +135,13 @@ class KeywordHighlighter extends SingletonFactory { $keywordPattern = str_replace('\*', '\w*', $keywordPattern); return preg_replace('+(?)+i', '\\1', $text); } + + /** + * Returns search keywords + * + * @return string[] + */ + public function getKeywords() { + return $this->keywords; + } } diff --git a/wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php b/wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php index 6e28572b9f..fdc0865a51 100644 --- a/wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php +++ b/wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php @@ -29,14 +29,16 @@ class HtmlOutputProcessor extends AbstractHtmlProcessor { /** * Processes the input html string. * - * @param string $html html string - * @param string $objectType object type identifier - * @param integer $objectID object id + * @param string $html html string + * @param string $objectType object type identifier + * @param integer $objectID object id + * @param boolean $doKeywordHighlighting */ - public function process($html, $objectType, $objectID) { + public function process($html, $objectType, $objectID, $doKeywordHighlighting = true) { $this->setContext($objectType, $objectID); $this->getHtmlOutputNodeProcessor()->setOutputType($this->outputType); + $this->getHtmlOutputNodeProcessor()->enableKeywordHighlighting($doKeywordHighlighting); $this->getHtmlOutputNodeProcessor()->load($this, $html); $this->getHtmlOutputNodeProcessor()->process(); } diff --git a/wcfsetup/install/files/lib/system/html/output/node/HtmlOutputNodeProcessor.class.php b/wcfsetup/install/files/lib/system/html/output/node/HtmlOutputNodeProcessor.class.php index c3402703c2..4379be102d 100644 --- a/wcfsetup/install/files/lib/system/html/output/node/HtmlOutputNodeProcessor.class.php +++ b/wcfsetup/install/files/lib/system/html/output/node/HtmlOutputNodeProcessor.class.php @@ -1,5 +1,7 @@ sourceBBCodes = HtmlBBCodeParser::getInstance()->getSourceBBCodes(); + } + /** * Sets the desired output type. * @@ -39,6 +59,9 @@ class HtmlOutputNodeProcessor extends AbstractHtmlNodeProcessor { * @inheritDoc */ public function process() { + // highlight keywords + $this->highlightKeywords(); + $this->invokeHtmlNode(new HtmlOutputNodeWoltlabMetacode()); // dynamic node handlers @@ -125,6 +148,81 @@ class HtmlOutputNodeProcessor extends AbstractHtmlNodeProcessor { return $html; } + /** + * Enables the keyword highlighting. + * + * @param boolean $enable + */ + public function enableKeywordHighlighting($enable = true) { + $this->keywordHighlighting = $enable; + } + + /** + * Executes the keyword highlighting. + */ + protected function highlightKeywords() { + if (!$this->keywordHighlighting) return; + if (!count(KeywordHighlighter::getInstance()->getKeywords())) return; + $keywordPattern = '('.implode('|', KeywordHighlighter::getInstance()->getKeywords()).')'; + + $nodes = []; + foreach ($this->getXPath()->query('//text()') as $node) { + $value = StringUtil::trim($node->textContent); + if (empty($value)) { + // skip empty nodes + continue; + } + + // check if node is within a code element or link + if ($this->hasCodeParent($node)) { + continue; + } + + $nodes[] = $node; + } + foreach ($nodes as $node) { + $split = preg_split('+'.$keywordPattern.'+', $node->textContent, -1, PREG_SPLIT_DELIM_CAPTURE); + if (count($split) == 1) return; + + for ($i = 0; $i < count($split); $i++) { + if ($i % 2 == 0) { // text + $node->parentNode->insertBefore($node->ownerDocument->createTextNode($split[$i]), $node); + } + else { // match + $element = $node->ownerDocument->createElement('span'); + $element->setAttribute('class', 'highlight'); + $element->appendChild($node->ownerDocument->createTextNode($split[$i])); + $node->parentNode->insertBefore($element, $node); + } + } + + DOMUtil::removeNode($node); + } + } + + /** + * Returns true if text node is inside a code element, suppresing any + * auto-detection of content. + * + * @param \DOMText $text text node + * @return boolean true if text node is inside a code element + */ + protected function hasCodeParent(\DOMText $text) { + $parent = $text; + /** @var \DOMElement $parent */ + while ($parent = $parent->parentNode) { + $nodeName = $parent->nodeName; + if ($nodeName === 'code' || $nodeName === 'kbd' || $nodeName === 'pre') { + return true; + } + else if ($nodeName === 'woltlab-metacode' && in_array($parent->getAttribute('data-name'), $this->sourceBBCodes)) { + return true; + } + } + + return false; + } + /** * @inheritDoc */ -- 2.20.1