diff --git a/admin/tool/httpsreplace/classes/form.php b/admin/tool/httpsreplace/classes/form.php new file mode 100644 index 00000000000..cd834736404 --- /dev/null +++ b/admin/tool/httpsreplace/classes/form.php @@ -0,0 +1,52 @@ +. + +/** + * Site wide http -> https search-replace form. + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_httpsreplace; + +defined('MOODLE_INTERNAL') || die(); + +require_once("$CFG->libdir/formslib.php"); + +/** + * Site wide http -> https search-replace form. + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class form extends \moodleform { + + /** + * Define the form. + */ + public function definition() { + $mform = $this->_form; + + $mform->addElement('header', 'confirmhdr', get_string('confirm')); + $mform->setExpanded('confirmhdr', true); + $mform->addElement('checkbox', 'sure', get_string('disclaimer', 'tool_httpsreplace')); + $mform->addRule('sure', get_string('required'), 'required', null, 'client'); + $mform->disable_form_change_checker(); + + $this->add_action_buttons(false, get_string('doit', 'tool_httpsreplace')); + } +} diff --git a/admin/tool/httpsreplace/classes/url_finder.php b/admin/tool/httpsreplace/classes/url_finder.php new file mode 100644 index 00000000000..af9fc9bc01d --- /dev/null +++ b/admin/tool/httpsreplace/classes/url_finder.php @@ -0,0 +1,261 @@ +. + +/** + * url_finder class definition. + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_httpsreplace; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Examines DB for non-https src or data links + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class url_finder { + + /** + * Returns a hash of what hosts are referred to over http and would need to be changed. + * + * @param progress_bar $progress Progress bar keeping track of this process. + * @return array Hash of domains with number of references as the value. + */ + public function http_link_stats($progress = null) { + return $this->process(false, $progress); + } + + /** + * Changes all resources referred to over http to https. + * + * @param progress_bar $progress Progress bar keeping track of this process. + * @return bool True upon success + */ + public function upgrade_http_links($progress = null) { + return $this->process(true, $progress); + } + + /** + * Replace http domains with https equivalent, with two types of exceptions + * for less straightforward swaps. + * + * @param string $table + * @param string $column + * @param string $domain + * @param string $search search string that has prefix, protocol, domain name and one extra character, + * example1: src="http://host.com/ + * example2: DATA="HTTP://MYDOMAIN.EDU" + * example3: src="HTTP://hello.world? + * @return void + */ + protected function domain_swap($table, $column, $domain, $search) { + global $DB; + + $renames = json_decode(get_config('tool_httpsreplace', 'renames'), true); + + if (isset($renames[$domain])) { + $replace = preg_replace('|http://'.preg_quote($domain).'|i', 'https://' . $renames[$domain], $search); + } else { + $replace = preg_replace('|http://|i', 'https://', $search); + } + $DB->set_debug(true); + $DB->replace_all_text($table, $column, $search, $replace); + $DB->set_debug(false); + } + + /** + * Returns SQL to be used to match embedded http links in the given column + * + * @param string $columnname name of the column (ready to be used in the SQL query) + * @return array + */ + protected function get_select_search_in_column($columnname) { + global $DB; + + if ($DB->sql_regex_supported()) { + // Database supports regex, use it for better match. + $select = $columnname . ' ' . $DB->sql_regex() . ' ?'; + $params = ["(src|data)\ *=\ *[\\\"\']http://"]; + } else { + // Databases without regex support should use case-insensitive LIKE. + // This will have false positive matches and more results than we need, we'll have to filter them in php. + $select = $DB->sql_like($columnname, '?', false); + $params = ['%=%http://%']; + } + + return [$select, $params]; + } + + /** + * Originally forked from core function db_search(). + * @param bool $replacing Whether or not to replace the found urls. + * @param progress_bar $progress Progress bar keeping track of this process. + * @return bool|array If $replacing, return true on success. If not, return hash of http urls to number of times used. + */ + protected function process($replacing = false, $progress = null) { + global $DB, $CFG; + + require_once($CFG->libdir.'/filelib.php'); + + // TODO: block_instances have HTML content as base64, need to decode then + // search, currently just skipped. See MDL-60024. + $skiptables = array( + 'block_instances', + 'config', + 'config_log', + 'config_plugins', + 'events_queue', + 'files', + 'filter_config', + 'grade_grades_history', + 'grade_items_history', + 'log', + 'logstore_standard_log', + 'repository_instance_config', + 'sessions', + 'upgrade_log', + 'grade_categories_history', + '', + ); + + // Turn off time limits. + \core_php_time_limit::raise(); + if (!$tables = $DB->get_tables() ) { // No tables yet at all. + return false; + } + + $urls = array(); + + $numberoftables = count($tables); + $tablenumber = 0; + foreach ($tables as $table) { + if ($progress) { + $progress->update($tablenumber, $numberoftables, get_string('searching', 'tool_httpsreplace', $table)); + $tablenumber++; + } + if (in_array($table, $skiptables)) { + continue; + } + if ($columns = $DB->get_columns($table)) { + foreach ($columns as $column) { + + // Only convert columns that are either text or long varchar. + if ($column->meta_type == 'X' || ($column->meta_type == 'C' && $column->max_length > 255)) { + $columnname = $column->name; + $columnnamequoted = $DB->get_manager()->generator->getEncQuoted($columnname); + list($select, $params) = $this->get_select_search_in_column($columnnamequoted); + $rs = $DB->get_recordset_select($table, $select, $params, '', $columnnamequoted); + + $found = array(); + foreach ($rs as $record) { + // Regex to match src=http://etc. and data=http://etc.urls. + // Standard warning on expecting regex to perfectly parse HTML + // read http://stackoverflow.com/a/1732454 for more info. + $regex = '#((src|data)\ *=\ *[\'\"])(http://)([^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))[\'\"]#i'; + preg_match_all($regex, $record->$columnname, $match); + foreach ($match[0] as $i => $fullmatch) { + if (strpos($fullmatch, $CFG->wwwroot) !== false) { + continue; + } + $prefix = $match[1][$i]; + $protocol = $match[3][$i]; + $url = $protocol . $match[4][$i]; + $host = \core_text::strtolower(parse_url($url, PHP_URL_HOST)); + if (empty($host)) { + continue; + } + if ($replacing) { + // For replace string use: prefix, protocol, host and one extra character. + $found[$prefix . substr($url, 0, strlen($host) + 8)] = $host; + } else { + $entry["table"] = $table; + $entry["columnname"] = $columnname; + $entry["url"] = $url; + $entry["host"] = $host; + $entry["raw"] = $record->$columnname; + $entry["ssl"] = ''; + $urls[] = $entry; + } + } + } + $rs->close(); + + if ($replacing) { + foreach ($found as $search => $domain) { + $this->domain_swap($table, $column, $domain, $search); + } + } + } + } + } + } + + if ($replacing) { + rebuild_course_cache(0, true); + purge_all_caches(); + return true; + } + + $domains = array_map(function ($i) { + return $i['host']; + }, $urls); + + $uniquedomains = array_unique($domains); + + $sslfailures = array(); + + foreach ($uniquedomains as $domain) { + if (!$this->check_domain_availability("https://$domain/")) { + $sslfailures[] = $domain; + } + } + + $results = array(); + foreach ($urls as $url) { + $host = $url['host']; + foreach ($sslfailures as $badhost) { + if ($host == $badhost) { + if (!isset($results[$host])) { + $results[$host] = 1; + } else { + $results[$host]++; + } + } + } + } + return $results; + } + + /** + * Check if url is available (GET request returns 200) + * + * @param string $url + * @return bool + */ + protected function check_domain_availability($url) { + $curl = new \curl(); + $curl->head($url); + $info = $curl->get_info(); + return !empty($info['http_code']) && $info['http_code'] == 200; + } +} diff --git a/admin/tool/httpsreplace/cli/url_replace.php b/admin/tool/httpsreplace/cli/url_replace.php new file mode 100644 index 00000000000..7945a772af0 --- /dev/null +++ b/admin/tool/httpsreplace/cli/url_replace.php @@ -0,0 +1,93 @@ +. + +/** + * url_replace cli script. Examines DB for non-https src or data links, and lists broken ones or replaces all links. + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('CLI_SCRIPT', true); +require(__DIR__ . '/../../../../config.php'); +require_once($CFG->libdir.'/clilib.php'); + +list($options, $unrecognized) = cli_get_params( + array( + 'help' => false, + 'list' => false, + 'replace' => false, + 'confirm' => false, + ), + array( + 'h' => 'help', + 'l' => 'list', + 'r' => 'replace', + ) +); +if ($unrecognized) { + $unrecognized = implode("\n ", $unrecognized); + cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2); +} +if ($options['help'] || (!$options['list'] && !$options['replace'])) { + $help = "Examines DB for non-https src or data links, and lists broken links or replaces all links. +Options: +-h, --help Print out this help +-l, --list List of http (not https) urls on a site in the DB that would become broken. +-r, --replace List of http (not https) urls on a site in the DB that would become broken. +--confirm Replaces http urls with https across a site's content. +Example: +\$ sudo -u www-data /usr/bin/php admin/tool/httpsreplace/cli/url_replace.php --list \n"; + echo $help; + exit(0); +} + +if (!$DB->replace_all_text_supported()) { + echo $OUTPUT->notification(get_string('notimplemented', 'tool_httpsreplace')); + exit(1); +} + +if (!is_https()) { + echo $OUTPUT->notification(get_string('httpwarning', 'tool_httpsreplace'), 'warning'); + echo "\n"; +} + +if ($options['replace']) { + + if ($options['confirm']) { + + $urlfinder = new \tool_httpsreplace\url_finder(); + $urlfinder->upgrade_http_links(); + } else { + echo "Once this is tool run, changes made can't be reverted. \n" . + "A complete backup should be made before running this script. \n\n" . + "There is a low risk that the wrong content will be replaced, introducing problems. \n" . + "If you are sure you want to continue, add --confirm\n\n"; + } + +} else { + + $urlfinder = new \tool_httpsreplace\url_finder(); + $results = $urlfinder->http_link_stats(); + asort($urlfinder); + $fp = fopen('php://stdout', 'w'); + fputcsv($fp, ['clientsite', 'httpdomain', 'urlcount']); + foreach ($results as $domain => $count) { + fputcsv($fp, [$SITE->shortname, $domain, $count]); + } + fclose($fp); +} diff --git a/admin/tool/httpsreplace/index.php b/admin/tool/httpsreplace/index.php new file mode 100644 index 00000000000..a3cec32cda5 --- /dev/null +++ b/admin/tool/httpsreplace/index.php @@ -0,0 +1,59 @@ +. + +/** + * Search and replace http -> https throughout all texts in the whole database + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); + +admin_externalpage_setup('toolhttpsreplace'); + +$context = context_system::instance(); + +require_login(); +require_capability('moodle/site:config', $context); + +$PAGE->set_context($context); +$PAGE->set_url(new moodle_url('/admin/tool/httpsreplace/index.php')); +$PAGE->set_title(get_string('pageheader', 'tool_httpsreplace')); +$PAGE->set_pagelayout('admin'); + +echo $OUTPUT->header(); + +echo $OUTPUT->heading(get_string('pageheader', 'tool_httpsreplace')); + +if (!$DB->replace_all_text_supported()) { + echo $OUTPUT->notification(get_string('notimplemented', 'tool_httpsreplace')); + echo $OUTPUT->footer(); + die; +} + +if (!is_https()) { + echo $OUTPUT->notification(get_string('httpwarning', 'tool_httpsreplace'), 'warning'); +} + +echo '

'.get_string('domainexplain', 'tool_httpsreplace').'

'; +echo '

'.page_doc_link(get_string('doclink', 'tool_httpsreplace')).'

'; + +echo $OUTPUT->continue_button(new moodle_url('/admin/tool/httpsreplace/tool.php')); + +echo $OUTPUT->footer(); diff --git a/admin/tool/httpsreplace/lang/en/tool_httpsreplace.php b/admin/tool/httpsreplace/lang/en/tool_httpsreplace.php new file mode 100644 index 00000000000..956687450ab --- /dev/null +++ b/admin/tool/httpsreplace/lang/en/tool_httpsreplace.php @@ -0,0 +1,41 @@ +. + +/** + * Strings for component 'tool_httpsreplace' + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['complete'] = 'Completed.'; +$string['count'] = 'Number of embeded content items'; +$string['disclaimer'] = 'I understand the risks of this operation'; +$string['doclink'] = 'Read more documentation on the wiki'; +$string['doit'] = 'Perform replacement'; +$string['domain'] = 'Problematic domain'; +$string['domainexplain'] = 'When an instance is moved from HTTP to HTTPS, all embeded HTTP content will stop working. This tool allows you to automatically convert the HTTP content to HTTPS. Below you can run a report of content that may not work once you run this script. You may want to check each one has HTTPS available or find alternative resources.'; +$string['domainexplainhelp'] = 'These domains are found in your content, but do not appear to support HTTPS content. After switching to HTTPS, the content included from these sites will no longer display within Moodle for users with secure modern browsers. It is possible that these sites are temporarily or permanently unavailable and will not work with either security setting. Proceed only after reviewing these results and determining if this externally hosted content is non-essential. Note: This content would no longer work upon switching to HTTPS anyway.'; +$string['httpwarning'] = 'This instance is still running on HTTP. You can still run this tool and external content will be changed to HTTPS, but internal content will remain on HTTP. You will need to run this script again after switching to HTTPS to convert internal content.'; +$string['notimplemented'] = 'Sorry, this feature is not implemented in your database driver.'; +$string['oktoprocede'] = 'The scan finds no issues with your content. You can proceed to upgrade any HTTP content to use HTTPS.'; +$string['pageheader'] = 'Upgrade externally hosted content urls to HTTPS'; +$string['pluginname'] = 'HTTPS conversion tool'; +$string['replacing'] = 'Replacing HTTP content with HTTPS...'; +$string['searching'] = 'Searching {$a}'; +$string['takeabackupwarning'] = 'Once this is tool run, changes made can\'t be reverted. A complete backup should be made before running this script. There is a low risk that the wrong content will be replaced, introducing problems.'; +$string['toolintro'] = 'If you are planning on converting your site to HTTPS, you can use the HTTPS conversion tool to convert your embeded content to HTTPS.'; diff --git a/admin/tool/httpsreplace/settings.php b/admin/tool/httpsreplace/settings.php new file mode 100644 index 00000000000..bf5c259580c --- /dev/null +++ b/admin/tool/httpsreplace/settings.php @@ -0,0 +1,41 @@ +. + +/** + * Link to http -> https replace script. + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +if ($hassiteconfig) { + + $pluginname = get_string('pluginname', 'tool_httpsreplace'); + $url = $CFG->wwwroot.'/'.$CFG->admin.'/tool/httpsreplace/index.php'; + $ADMIN->add('security', new admin_externalpage('toolhttpsreplace', $pluginname, $url, 'moodle/site:config', true)); + + $httpsreplaceurl = $CFG->wwwroot.'/'.$CFG->admin.'/tool/httpsreplace/index.php'; + $ADMIN->locate('httpsecurity')->add( + new admin_setting_heading( + 'tool_httpsreplaceheader', + new lang_string('pluginname', 'tool_httpsreplace'), + new lang_string('toolintro', 'tool_httpsreplace', $httpsreplaceurl) + ) + ); +} diff --git a/admin/tool/httpsreplace/tests/behat/httpsreplace.feature b/admin/tool/httpsreplace/tests/behat/httpsreplace.feature new file mode 100644 index 00000000000..82bbf590151 --- /dev/null +++ b/admin/tool/httpsreplace/tests/behat/httpsreplace.feature @@ -0,0 +1,29 @@ +@tool @tool_httpsreplace +Feature: View the httpsreplace report + In order to switch to https + As an admin + I need to be able to automatically replace http links + + Background: Create some http links + Given I am on site homepage + And the following "courses" exist: + | fullname | shortname | category | summary | + | Course 1 | C1 | 0 | | + And I log in as "admin" + + @javascript + Scenario: Go to the HTTPS replace report screen. Make sure broken domains are reported. + When I navigate to "HTTP security" node in "Site administration > Security" + And I follow "HTTPS conversion tool" + And I press "Continue" + Then I should see "intentionally.unavailable" + + @javascript + Scenario: Use the find and replace tool. + When I navigate to "HTTP security" node in "Site administration > Security" + And I follow "HTTPS conversion tool" + And I press "Continue" + And I set the field "I understand the risks of this operation" to "1" + And I press "Perform replacement" + Then I should see "intentionally.unavailable" + And I should see "download.moodle.org" diff --git a/admin/tool/httpsreplace/tests/httpsreplace_test.php b/admin/tool/httpsreplace/tests/httpsreplace_test.php new file mode 100644 index 00000000000..c10ea2f658d --- /dev/null +++ b/admin/tool/httpsreplace/tests/httpsreplace_test.php @@ -0,0 +1,413 @@ +. + +/** + * HTTPS find and replace Tests + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_httpsreplace\tests; + + +defined('MOODLE_INTERNAL') || die(); + +/** + * Tests the httpsreplace tool. + * + * @package tool_httpsreplace + * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class httpsreplace_test extends \advanced_testcase { + + /** + * Data provider for test_upgrade_http_links + */ + public function upgrade_http_links_provider() { + global $CFG; + // Get the http url, since the default test wwwroot is https. + $wwwroothttp = preg_replace('/^https:/', 'http:', $CFG->wwwroot); + return [ + "Test image from another site should be replaced" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "Test object from another site should be replaced" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "Test image from a site with international name should be replaced" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "Link that is from this site should be replaced" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "Link that is from this site, https new so doesn't need replacing" => [ + "content" => '', + "outputregex" => '/^$/', + "expectedcontent" => '', + ], + "Unavailable image should be replaced" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "Https content that has an http url as a param should not be replaced" => [ + "content" => '', + "outputregex" => '/^$/', + "expectedcontent" => '', + ], + "Search for params should be case insensitive" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "URL should be case insensitive" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + "More params should not interfere" => [ + "content" => 'A picture

', + "outputregex" => '/UPDATE/', + "expectedcontent" => 'A picture

', + ], + "Broken URL should not be changed" => [ + "content" => '', + "outputregex" => '/^$/', + "expectedcontent" => '', + ], + "Link URL should not be changed" => [ + "content" => '' . + $this->getExternalTestFileUrl('/test.png', false) . '', + "outputregex" => '/^$/', + "expectedcontent" => '' . + $this->getExternalTestFileUrl('/test.png', false) . '', + ], + "Test image from another site should be replaced but link should not" => [ + "content" => '', + "outputregex" => '/UPDATE/', + "expectedcontent" => '', + ], + ]; + } + + /** + * Test upgrade_http_links + * @param string $content Example content that we'll attempt to replace. + * @param string $ouputregex Regex for what output we expect. + * @param string $expectedcontent What content we are expecting afterwards. + * @dataProvider upgrade_http_links_provider + */ + public function test_upgrade_http_links($content, $ouputregex, $expectedcontent) { + global $DB; + + $this->resetAfterTest(); + $this->expectOutputRegex($ouputregex); + + $finder = new tool_httpreplace_url_finder_test(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course((object) [ + 'summary' => $content, + ]); + + $finder->upgrade_http_links(); + + $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); + $this->assertContains($expectedcontent, $summary); + } + + /** + * Data provider for test_http_link_stats + */ + public function http_link_stats_provider() { + global $CFG; + // Get the http url, since the default test wwwroot is https. + $wwwrootdomain = 'www.example.com'; + $wwwroothttp = preg_replace('/^https:/', 'http:', $CFG->wwwroot); + $testdomain = 'download.moodle.org'; + return [ + "Test image from an available site so shouldn't be reported" => [ + "content" => '', + "domain" => $testdomain, + "expectedcount" => 0, + ], + "Link that is from this site shouldn't be reported" => [ + "content" => '', + "domain" => $wwwrootdomain, + "expectedcount" => 0, + ], + "Unavailable, but https shouldn't be reported" => [ + "content" => '', + "domain" => 'intentionally.unavailable', + "expectedcount" => 0, + ], + "Unavailable image should be reported" => [ + "content" => '', + "domain" => 'intentionally.unavailable', + "expectedcount" => 1, + ], + "Unavailable object should be reported" => [ + "content" => '', + "domain" => 'intentionally.unavailable', + "expectedcount" => 1, + ], + "Link should not be reported" => [ + "content" => 'Link', + "domain" => 'intentionally.unavailable', + "expectedcount" => 0, + ], + "Text should not be reported" => [ + "content" => 'http://intentionally.unavailable/page.php', + "domain" => 'intentionally.unavailable', + "expectedcount" => 0, + ], + ]; + } + + /** + * Test http_link_stats + * @param string $content Example content that we'll attempt to replace. + * @param string $domain The domain we will check was replaced. + * @param string $expectedcount Number of urls from that domain that we expect to be replaced. + * @dataProvider http_link_stats_provider + */ + public function test_http_link_stats($content, $domain, $expectedcount) { + $this->resetAfterTest(); + + $finder = new tool_httpreplace_url_finder_test(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course((object) [ + 'summary' => $content, + ]); + + $results = $finder->http_link_stats(); + + $this->assertEquals($expectedcount, $results[$domain] ?? 0); + } + + /** + * Test links and text are not changed + */ + public function test_links_and_text() { + global $DB; + + $this->resetAfterTest(); + $this->expectOutputRegex('/^$/'); + + $finder = new tool_httpreplace_url_finder_test(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course((object) [ + 'summary' => 'Link http://other.unavailable/page.php', + ]); + + $results = $finder->http_link_stats(); + $this->assertCount(0, $results); + + $finder->upgrade_http_links(); + + $results = $finder->http_link_stats(); + $this->assertCount(0, $results); + + $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); + $this->assertContains('http://intentionally.unavailable/page.php', $summary); + $this->assertContains('http://other.unavailable/page.php', $summary); + $this->assertNotContains('https://intentionally.unavailable', $summary); + $this->assertNotContains('https://other.unavailable', $summary); + } + + /** + * If we have an http wwwroot then we shouldn't report it. + */ + public function test_httpwwwroot() { + global $DB, $CFG; + + $this->resetAfterTest(); + $CFG->wwwroot = preg_replace('/^https:/', 'http:', $CFG->wwwroot); + $this->expectOutputRegex('/^$/'); + + $finder = new tool_httpreplace_url_finder_test(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course((object) [ + 'summary' => '', + ]); + + $results = $finder->http_link_stats(); + $this->assertCount(0, $results); + + $finder->upgrade_http_links(); + $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); + $this->assertContains($CFG->wwwroot, $summary); + } + + /** + * Test that links in excluded tables are not replaced + */ + public function test_upgrade_http_links_excluded_tables() { + $this->resetAfterTest(); + + set_config('test_upgrade_http_links', ''); + + $finder = new tool_httpreplace_url_finder_test(); + ob_start(); + $results = $finder->upgrade_http_links(); + $output = ob_get_contents(); + ob_end_clean(); + $this->assertTrue($results); + $this->assertNotContains('https://somesite', $output); + $testconf = get_config('core', 'test_upgrade_http_links'); + $this->assertContains('http://somesite', $testconf); + $this->assertNotContains('https://somesite', $testconf); + } + + /** + * Test renamed domains + */ + public function test_renames() { + global $DB, $CFG; + $this->resetAfterTest(); + $this->expectOutputRegex('/UPDATE/'); + + $renames = [ + 'example.com' => 'secure.example.com', + ]; + + set_config('renames', json_encode($renames), 'tool_httpsreplace'); + + $finder = new tool_httpreplace_url_finder_test(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course((object) [ + 'summary' => '