From a0d501463af4aed72cbb1bb0aea05aa963d1703b Mon Sep 17 00:00:00 2001 From: Stefan Hanauska Date: Wed, 6 Mar 2024 07:36:48 +0100 Subject: [PATCH] MDL-69656 backup: Replace urlencoded pluginfile urls --- backup/moodle2/backup_course_task.class.php | 16 +++++++++-- backup/moodle2/restore_course_task.class.php | 1 + .../util/helper/restore_decode_rule.class.php | 17 ++++++++++- .../tests/backup_encode_content_test.php | 28 ++++++++++++------- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/backup/moodle2/backup_course_task.class.php b/backup/moodle2/backup_course_task.class.php index 00184a79777..e252b3f21ce 100644 --- a/backup/moodle2/backup_course_task.class.php +++ b/backup/moodle2/backup_course_task.class.php @@ -168,6 +168,7 @@ class backup_course_task extends backup_task { $content = self::encode_links_helper($content, 'BADGESVIEWBYID', '/badges/view.php?type=2&id='); $content = self::encode_links_helper($content, 'USERINDEXVIEWBYID', '/user/index.php?id='); $content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXT', '/pluginfile.php/'); + $content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/', true); return $content; } @@ -178,17 +179,26 @@ class backup_course_task extends backup_task { * @param string $name the name of this type of encoded link. * @param string $path the path that identifies this type of link, up * to the ?paramname= bit. + * @param bool $urlencoded whether to use urlencode() before replacing the path. * @return string content with one type of link encoded. */ - static private function encode_links_helper($content, $name, $path) { + private static function encode_links_helper(string $content, string $name, string $path, bool $urlencoded = false) { global $CFG; // We want to convert both http and https links. $root = $CFG->wwwroot; $httpsroot = str_replace('http://', 'https://', $root); $httproot = str_replace('https://', 'http://', $root); - $httpsbase = preg_quote($httpsroot . $path, '/'); - $httpbase = preg_quote($httproot . $path, '/'); + $httpsbase = $httpsroot . $path; + $httpbase = $httproot . $path; + + if ($urlencoded) { + $httpsbase = urlencode($httpsbase); + $httpbase = urlencode($httpbase); + } + + $httpsbase = preg_quote($httpsbase, '/'); + $httpbase = preg_quote($httpbase, '/'); $return = preg_replace('/(' . $httpsbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $content); $return = preg_replace('/(' . $httpbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $return); diff --git a/backup/moodle2/restore_course_task.class.php b/backup/moodle2/restore_course_task.class.php index ab4d04c186d..ac1db1d19e4 100644 --- a/backup/moodle2/restore_course_task.class.php +++ b/backup/moodle2/restore_course_task.class.php @@ -167,6 +167,7 @@ class restore_course_task extends restore_task { $rules[] = new restore_decode_rule('BADGESVIEWBYID', '/badges/view.php?type=2&id=$1', 'course'); $rules[] = new restore_decode_rule('USERINDEXVIEWBYID', '/user/index.php?id=$1', 'course'); $rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXT', '/pluginfile.php/$1', 'context'); + $rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/$1', 'context', true); return $rules; } diff --git a/backup/util/helper/restore_decode_rule.class.php b/backup/util/helper/restore_decode_rule.class.php index ded4126b3df..a9a286cedae 100644 --- a/backup/util/helper/restore_decode_rule.class.php +++ b/backup/util/helper/restore_decode_rule.class.php @@ -44,7 +44,18 @@ class restore_decode_rule { protected $cregexp; // Calculated regular expresion we'll be looking for matches - public function __construct($linkname, $urltemplate, $mappings) { + /** @var bool $urlencoded Whether to use urlencode() on the final URL. */ + protected bool $urlencoded; + + /** + * Constructor + * + * @param string $linkname How the link has been encoded in backup (CHOICEVIEWBYID, COURSEVIEWBYID...) + * @param string $urltemplate How the original URL looks like, with dollar placeholders + * @param array|string $mappings Which backup_ids mappings do we need to apply for replacing the placeholders + * @param bool $urlencoded Whether to use urlencode() on the final URL (defaults to false) + */ + public function __construct(string $linkname, string $urltemplate, $mappings, bool $urlencoded = false) { // Validate all the params are ok $this->mappings = $this->validate_params($linkname, $urltemplate, $mappings); $this->linkname = $linkname; @@ -52,6 +63,7 @@ class restore_decode_rule { $this->restoreid = 0; $this->sourcewwwroot = ''; $this->targetwwwroot = ''; // yes, uses to be $CFG->wwwroot, and? ;-) + $this->urlencoded = $urlencoded; $this->cregexp = $this->get_calculated_regexp(); } @@ -96,6 +108,9 @@ class restore_decode_rule { } else { // All mappings found, apply target values to the template $toreplace = str_replace($placeholdersarr, $mappingstargetarr, $toreplace); } + if ($this->urlencoded) { + $toreplace = urlencode($toreplace); + } // Finally, perform the replacement in original content $content = str_replace($tosearch, $toreplace, $content); } diff --git a/backup/util/helper/tests/backup_encode_content_test.php b/backup/util/helper/tests/backup_encode_content_test.php index c93df258173..ca214722c7d 100644 --- a/backup/util/helper/tests/backup_encode_content_test.php +++ b/backup/util/helper/tests/backup_encode_content_test.php @@ -50,14 +50,18 @@ class backup_encode_content_test extends \basic_testcase { // HTTPS root and links of both types in content. $CFG->wwwroot = $httpsroot; $encoded = backup_course_task::encode_content_links( - $httproot . '/course/view.php?id=123, ' . - $httpsroot . '/course/view.php?id=123, ' . - $httpsroot . '/grade/index.php?id=123, ' . - $httpsroot . '/grade/report/index.php?id=123, ' . - $httpsroot . '/badges/view.php?type=2&id=123 and ' . - $httpsroot . '/user/index.php?id=123.'); + $httproot . '/course/view.php?id=123, ' . + $httpsroot . '/course/view.php?id=123, ' . + $httpsroot . '/grade/index.php?id=123, ' . + $httpsroot . '/grade/report/index.php?id=123, ' . + $httpsroot . '/badges/view.php?type=2&id=123, ' . + $httpsroot . '/user/index.php?id=123, ' . + $httpsroot . '/pluginfile.php/123 and ' . + urlencode($httpsroot . '/pluginfile.php/123') . '.' + ); $this->assertEquals('$@COURSEVIEWBYID*123@$, $@COURSEVIEWBYID*123@$, $@GRADEINDEXBYID*123@$, ' . - '$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$ and $@USERINDEXVIEWBYID*123@$.', $encoded); + '$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$, $@USERINDEXVIEWBYID*123@$, ' . + '$@PLUGINFILEBYCONTEXT*123@$ and $@PLUGINFILEBYCONTEXTURLENCODED*123@$.', $encoded); // HTTP root and links of both types in content. $CFG->wwwroot = $httproot; @@ -66,10 +70,14 @@ class backup_encode_content_test extends \basic_testcase { $httpsroot . '/course/view.php?id=123, ' . $httproot . '/grade/index.php?id=123, ' . $httproot . '/grade/report/index.php?id=123, ' . - $httproot . '/badges/view.php?type=2&id=123 and ' . - $httproot . '/user/index.php?id=123.'); + $httproot . '/badges/view.php?type=2&id=123, ' . + $httproot . '/user/index.php?id=123, ' . + $httproot . '/pluginfile.php/123 and ' . + urlencode($httproot . '/pluginfile.php/123') . '.' + ); $this->assertEquals('$@COURSEVIEWBYID*123@$, $@COURSEVIEWBYID*123@$, $@GRADEINDEXBYID*123@$, ' . - '$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$ and $@USERINDEXVIEWBYID*123@$.', $encoded); + '$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$, $@USERINDEXVIEWBYID*123@$, ' . + '$@PLUGINFILEBYCONTEXT*123@$ and $@PLUGINFILEBYCONTEXTURLENCODED*123@$.', $encoded); $CFG->wwwroot = $oldroot; } }