diff --git a/classes/Pref_Filters.php b/classes/Pref_Filters.php index 1ca542285..a7528865a 100644 --- a/classes/Pref_Filters.php +++ b/classes/Pref_Filters.php @@ -176,11 +176,43 @@ class Pref_Filters extends Handler_Protected { }, $entry, $excerpt_length); + $matches = []; + + $content_preview = $entry["content_preview"]; + $content_title = $entry["title"]; + + // is it even possible to have multiple matched rules here? + foreach ($matched_rules as $rule) { + $can_highlight_content = false; + $can_highlight_title = false; + + $matches[] = $rule['regexp_matches'][0]; + + switch ($rule['type']) { + case "both": + $can_highlight_title = true; + $can_highlight_content = true; + break; + case "title": + $can_highlight_title = true; + break; + case "content": + $can_highlight_content = true; + break; + } + + if ($can_highlight_content) + $content_preview = Sanitizer::highlight_words_str($content_preview, $matches); + + if ($can_highlight_title) + $content_title = Sanitizer::highlight_words_str($content_title, $matches); + } + $rv['items'][] = [ - 'title' => $entry['title'], + 'title' => $content_title, 'feed_title' => $entry['feed_title'], 'date' => mb_substr($entry['date_entered'], 0, 16), - 'content_preview' => $entry['content_preview'], + 'content_preview' => $content_preview, 'matched_rules' => $matched_rules, ]; } diff --git a/classes/Sanitizer.php b/classes/Sanitizer.php index 94d6fe621..2ae07d8d3 100644 --- a/classes/Sanitizer.php +++ b/classes/Sanitizer.php @@ -59,6 +59,65 @@ class Sanitizer { return parse_url(Config::get_self_url(), PHP_URL_SCHEME) == 'https'; } + /** @param array $words */ + public static function highlight_words_str(string $str, array $words) : string { + $doc = new DOMDocument(); + + if ($doc->loadHTML('' . $str . '')) { + $xpath = new DOMXPath($doc); + + if (self::highlight_words($doc, $xpath, $words)) { + $res = $doc->saveHTML(); + + /* strip everything outside of ... */ + $res_frag = array(); + + if (preg_match('/(.*)<\/body>/is', $res, $res_frag)) { + return $res_frag[1]; + } else { + return $res; + } + } + } + + return $str; + } + + /** @param array $words */ + public static function highlight_words(DOMDocument &$doc, DOMXPath $xpath, array $words) : bool { + $rv = false; + + foreach ($words as $word) { + + // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph + $elements = $xpath->query("//*/text()"); + + foreach ($elements as $child) { + + $fragment = $doc->createDocumentFragment(); + $text = $child->textContent; + + while (($pos = mb_stripos($text, $word)) !== false) { + $fragment->appendChild(new DOMText(mb_substr($text, 0, (int)$pos))); + $word = mb_substr($text, (int)$pos, mb_strlen($word)); + $highlight = $doc->createElement('span'); + $highlight->appendChild(new DOMText($word)); + $highlight->setAttribute('class', 'highlight'); + $fragment->appendChild($highlight); + $text = mb_substr($text, $pos + mb_strlen($word)); + } + + if (!empty($text)) $fragment->appendChild(new DOMText($text)); + + $child->parentNode->replaceChild($fragment, $child); + + $rv = true; + } + } + + return $rv; + } + /** * @param array|null $highlight_words Words to highlight in the HTML output. * @@ -197,34 +256,8 @@ class Sanitizer { $div->appendChild($entry); } - if (is_array($highlight_words)) { - foreach ($highlight_words as $word) { - - // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph - - $elements = $xpath->query("//*/text()"); - - foreach ($elements as $child) { - - $fragment = $doc->createDocumentFragment(); - $text = $child->textContent; - - while (($pos = mb_stripos($text, $word)) !== false) { - $fragment->appendChild(new DOMText(mb_substr($text, 0, (int)$pos))); - $word = mb_substr($text, (int)$pos, mb_strlen($word)); - $highlight = $doc->createElement('span'); - $highlight->appendChild(new DOMText($word)); - $highlight->setAttribute('class', 'highlight'); - $fragment->appendChild($highlight); - $text = mb_substr($text, $pos + mb_strlen($word)); - } - - if (!empty($text)) $fragment->appendChild(new DOMText($text)); - - $child->parentNode->replaceChild($fragment, $child); - } - } - } + if (is_array($highlight_words)) + self::highlight_words($doc, $xpath, $highlight_words); $res = $doc->saveHTML(); diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 83bc4cae9..f1f0e9e0c 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -20,7 +20,7 @@ const Filters = { PARAM_ACTIONS: [4, 6, 7, 9, 10], filter_info: {}, formatMatchedRules: function(rules) { - return rules.map((r) => r.regexp_matches[0] + ' - ' + r.reg_exp).join('\n'); + return rules.map((r) => r.regexp_matches[0] + ' - ' + r.reg_exp + ' (' + r.type + ')').join('\n'); }, test: function() { const test_dialog = new fox.SingleUseDialog({ diff --git a/phpstan.neon b/phpstan.neon index 74daee0ce..5bd18c58d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -32,6 +32,7 @@ parameters: - plugins.local/**/tests/* - plugins.local/*/vendor/intervention/* - plugins.local/*/vendor/psr/log/* + - plugins.local/af_readability/* - plugins.local/cache_s3/vendor/* - plugins/**/test/* - plugins/**/Test/*