diff --git a/mod/wiki/parser/markups/html.php b/mod/wiki/parser/markups/html.php index fc1545d931e..ad12c34b454 100644 --- a/mod/wiki/parser/markups/html.php +++ b/mod/wiki/parser/markups/html.php @@ -46,6 +46,15 @@ class html_parser extends nwiki_parser { parent::before_parsing(); $this->minheaderlevel = $this->find_min_header_level($this->string); + + // Protect all explicit links from further wiki parsing. The link text may contain another URL which would get + // converted into another link via {@see nwiki_parser::$tagrules} 'url' element. + if (preg_match_all('/]+?>(.*?)<\/a>/is', $this->string, $matches)) { + foreach (array_unique($matches[0]) as $match) { + $this->string = str_replace($match, $this->protect($match), $this->string); + } + } + $this->rules($this->string); } diff --git a/mod/wiki/tests/wikiparser_test.php b/mod/wiki/tests/wikiparser_test.php index 909c2c17e39..003eff0f52f 100644 --- a/mod/wiki/tests/wikiparser_test.php +++ b/mod/wiki/tests/wikiparser_test.php @@ -39,6 +39,92 @@ require_once($CFG->dirroot . '/mod/wiki/parser/parser.php'); class mod_wiki_wikiparser_test extends basic_testcase { + /** + * URL inside the clickable text of some link should not be turned into a new link via the url_tag_rule. + * + * @dataProvider urls_inside_link_text_provider + * @param string $markup Markup of the Wiki page the text is part of. + * @param string $input The input text. + * @param string $output The expected output HTML as a result of the parsed input text. + */ + public function test_urls_inside_link_text(string $markup, string $input, string $output) { + + $parsingresult = wiki_parser_proxy::parse($input, $markup, [ + 'link_callback' => '/mod/wiki/locallib.php:wiki_parser_link', + 'link_callback_args' => ['swid' => 1], + ]); + + $this->assertContains($output, $parsingresult['parsed_text']); + } + + /** + * Provides data sets for {@see self::test_urls_inside_link_text()}. + * + * @return array + */ + public function urls_inside_link_text_provider() { + return [ + 'creole implicit link' => [ + 'markup' => 'creole', + 'input' => 'Visit https://site.url for more information.', + 'output' => 'Visit https://site.url for more information.', + ], + 'creole explicit link' => [ + 'markup' => 'creole', + 'input' => 'Visit [[https://site.url]] for more information.', + 'output' => 'Visit https://site.url for more information.', + ], + 'creole explicit link with text' => [ + 'markup' => 'creole', + 'input' => 'Visit [[https://site.url|http://www.site.url]] for more information.', + 'output' => 'Visit http://www.site.url for more information.', + ], + 'nwiki implicit link' => [ + 'markup' => 'nwiki', + 'input' => 'Visit https://site.url for more information.', + 'output' => 'Visit https://site.url for more information.', + ], + 'nwiki explicit link' => [ + 'markup' => 'nwiki', + 'input' => 'Visit [https://site.url] for more information.', + 'output' => 'Visit https://site.url for more information.', + ], + 'nwiki explicit link with space separated text' => [ + 'markup' => 'nwiki', + 'input' => 'Visit [https://site.url http://www.site.url] for more information.', + 'output' => 'Visit http://www.site.url for more information.', + ], + 'nwiki explicit link with pipe separated text' => [ + 'markup' => 'nwiki', + 'input' => 'Visit [https://site.url|http://www.site.url] for more information.', + 'output' => 'Visit http://www.site.url for more information.', + ], + 'html implicit link' => [ + 'markup' => 'html', + 'input' => 'Visit https://site.url for more information.', + 'output' => 'Visit https://site.url for more information.', + ], + 'html explicit link with text' => [ + 'markup' => 'html', + 'input' => 'Visit http://www.site.url for more information.', + 'output' => 'Visit http://www.site.url for more information.', + ], + 'html wiki link to non-existing page' => [ + 'markup' => 'html', + 'input' => 'Visit [[Another page]] for more information.', + 'output' => 'Visit ' . + 'Another page for more information.', + ], + 'html wiki link inside an explicit link' => [ + // The explicit href URL takes precedence here, the [[...]] is not turned into a wiki link. + 'markup' => 'html', + 'input' => 'Visit [[Another page]] for more information.', + 'output' => 'Visit [[Another page]] for more information.', + ], + ]; + } + function testCreoleMarkup() { $this->assertTestFiles('creole'); }