From e8e497f4d474eba7c73b0fe8e573af94cb47e4e6 Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Wed, 31 Jan 2018 18:57:06 +0800 Subject: [PATCH 1/2] MDL-61268 blocks: Fix corrupt configdata in block instances. Old configuration data in the block instances is base64 encoded with the deprecated object class instead of stdClass. --- blocks/html/backup/moodle1/lib.php | 6 ++++- lib/db/upgrade.php | 9 ++++++++ lib/db/upgradelib.php | 37 ++++++++++++++++++++++++++++++ version.php | 2 +- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/blocks/html/backup/moodle1/lib.php b/blocks/html/backup/moodle1/lib.php index 3830ba99e99..da215c22298 100644 --- a/blocks/html/backup/moodle1/lib.php +++ b/blocks/html/backup/moodle1/lib.php @@ -30,9 +30,13 @@ defined('MOODLE_INTERNAL') || die(); class moodle1_block_html_handler extends moodle1_block_handler { private $fileman = null; protected function convert_configdata(array $olddata) { + global $CFG; + require_once($CFG->libdir . '/db/upgradelib.php'); $instanceid = $olddata['id']; $contextid = $this->converter->get_contextid(CONTEXT_BLOCK, $olddata['id']); - $configdata = unserialize(base64_decode($olddata['configdata'])); + $decodeddata = base64_decode($olddata['configdata']); + list($updated, $configdata) = upgrade_fix_serialized_objects($decodeddata); + $configdata = unserialize($configdata); // get a fresh new file manager for this instance $this->fileman = $this->converter->get_file_manager($contextid, 'block_html'); diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 91eb81fc9c2..e170ca53ab9 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2826,5 +2826,14 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2017111300.02); } + if ($oldversion < 2017111301.08) { + + // Fix old block configurations that use the deprecated (and now removed) object class. + upgrade_fix_block_instance_configuration(); + + // Main savepoint reached. + upgrade_main_savepoint(true, 2017111301.08); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index 134d4c0b3b7..e0913a5ed12 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -502,3 +502,40 @@ function upgrade_block_positions() { WHERE pagetype IN ('my-index', 'user-profile') AND subpage NOT IN (SELECT $id FROM {my_pages})"; $DB->execute($sql, ['']); } + +/** + * Fix configdata in block instances that are using the old object class that has been removed (deprecated). + */ +function upgrade_fix_block_instance_configuration() { + global $DB; + + $sql = "SELECT * + FROM {block_instances} + WHERE configdata <> ''"; + $blockinstances = $DB->get_recordset_sql($sql); + foreach ($blockinstances as $blockinstance) { + $configdata = base64_decode($blockinstance->configdata); + list($updated, $configdata) = upgrade_fix_serialized_objects($configdata); + if ($updated) { + $blockinstance->configdata = base64_encode($configdata); + $DB->update_record('block_instances', $blockinstance); + } + } + $blockinstances->close(); +} + +/** + * Provides a way to check and update a serialized string that uses the deprecated object class. + * + * @param string $serializeddata Serialized string which may contain the now deprecated object. + * @return array Returns an array where the first variable is a bool with a status of whether the initial data was changed + * or not. The second variable is the said data. + */ +function upgrade_fix_serialized_objects($serializeddata) { + $updated = false; + if (strpos($serializeddata, ":6:\"object") !== false) { + $serializeddata = str_replace(":6:\"object", ":8:\"stdClass", $serializeddata); + $updated = true; + } + return [$updated, $serializeddata]; +} diff --git a/version.php b/version.php index 25509d7ccdb..d5362d0915c 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2017111301.07; // 20171113 = branching date YYYYMMDD - do not modify! +$version = 2017111301.08; // 20171113 = branching date YYYYMMDD - do not modify! // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From ec72828d18cfb70151d07ad8e7c61ecf2fc745ed Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Tue, 27 Feb 2018 12:01:13 +0800 Subject: [PATCH 2/2] MDL-61268 blocks: Unit tests for deprecated object update. Unit tests for testing that the upgrade functions to change the now deprecated object to stdClass works. --- lib/tests/upgradelib_test.php | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/lib/tests/upgradelib_test.php b/lib/tests/upgradelib_test.php index 8df2704dfcd..7bbe0aa42dd 100644 --- a/lib/tests/upgradelib_test.php +++ b/lib/tests/upgradelib_test.php @@ -820,4 +820,103 @@ class core_upgradelib_testcase extends advanced_testcase { $this->assertFalse(upgrade_theme_is_from_family('testtheme', 'loop'), 'Infinite loop without testtheme parent is false'); $this->assertTrue(upgrade_theme_is_from_family('testtheme', 'themewithbrokenparent'), 'No error on broken parent'); } + + /** + * Data provider of serialized string. + * + * @return array + */ + public function serialized_strings_dataprovider() { + return [ + 'A configuration that uses the old object' => [ + 'O:6:"object":3:{s:4:"text";s:32:"Nothing that anyone cares about.";s:5:"title";s:16:"Really old block";s:6:"format";s:1:"1";}', + true, + 'O:8:"stdClass":3:{s:4:"text";s:32:"Nothing that anyone cares about.";s:5:"title";s:16:"Really old block";s:6:"format";s:1:"1";}' + ], + 'A configuration that uses stdClass' => [ + 'O:8:"stdClass":5:{s:5:"title";s:4:"Tags";s:12:"numberoftags";s:2:"80";s:12:"showstandard";s:1:"0";s:3:"ctx";s:3:"289";s:3:"rec";s:1:"1";}', + false, + 'O:8:"stdClass":5:{s:5:"title";s:4:"Tags";s:12:"numberoftags";s:2:"80";s:12:"showstandard";s:1:"0";s:3:"ctx";s:3:"289";s:3:"rec";s:1:"1";}' + ], + 'A setting I saw when importing a course with blocks from 1.9' => [ + 'N;', + false, + 'N;' + ], + 'An object in an object' => [ + 'O:6:"object":2:{s:2:"id";i:5;s:5:"other";O:6:"object":1:{s:4:"text";s:13:"something new";}}', + true, + 'O:8:"stdClass":2:{s:2:"id";i:5;s:5:"other";O:8:"stdClass":1:{s:4:"text";s:13:"something new";}}' + ], + 'An array with an object in it' => [ + 'a:3:{s:4:"name";s:4:"Test";s:10:"additional";O:6:"object":2:{s:2:"id";i:5;s:4:"info";s:18:"text in the object";}s:4:"type";i:1;}', + true, + 'a:3:{s:4:"name";s:4:"Test";s:10:"additional";O:8:"stdClass":2:{s:2:"id";i:5;s:4:"info";s:18:"text in the object";}s:4:"type";i:1;}' + ] + ]; + } + + /** + * Test that objects in serialized strings will be changed over to stdClass. + * + * @dataProvider serialized_strings_dataprovider + * @param string $initialstring The initial serialized setting. + * @param bool $expectededited If the string is expected to be edited. + * @param string $expectedresult The expected serialized setting to be returned. + */ + public function test_upgrade_fix_serialized_objects($initialstring, $expectededited, $expectedresult) { + list($edited, $resultstring) = upgrade_fix_serialized_objects($initialstring); + $this->assertEquals($expectededited, $edited); + $this->assertEquals($expectedresult, $resultstring); + } + + /** + * Data provider for base64_encoded block instance config data. + */ + public function encoded_strings_dataprovider() { + return [ + 'Normal data using stdClass' => [ + 'Tzo4OiJzdGRDbGFzcyI6NTp7czo1OiJ0aXRsZSI7czo0OiJUYWdzIjtzOjEyOiJudW1iZXJvZnRhZ3MiO3M6MjoiODAiO3M6MTI6InNob3dzdGFuZGFyZCI7czoxOiIwIjtzOjM6ImN0eCI7czozOiIyODkiO3M6MzoicmVjIjtzOjE6IjEiO30=', + 'Tzo4OiJzdGRDbGFzcyI6NTp7czo1OiJ0aXRsZSI7czo0OiJUYWdzIjtzOjEyOiJudW1iZXJvZnRhZ3MiO3M6MjoiODAiO3M6MTI6InNob3dzdGFuZGFyZCI7czoxOiIwIjtzOjM6ImN0eCI7czozOiIyODkiO3M6MzoicmVjIjtzOjE6IjEiO30=' + ], + 'No data at all' => [ + '', + '' + ], + 'Old data using object' => [ + 'Tzo2OiJvYmplY3QiOjM6e3M6NDoidGV4dCI7czozMjoiTm90aGluZyB0aGF0IGFueW9uZSBjYXJlcyBhYm91dC4iO3M6NToidGl0bGUiO3M6MTY6IlJlYWxseSBvbGQgYmxvY2siO3M6NjoiZm9ybWF0IjtzOjE6IjEiO30=', + 'Tzo4OiJzdGRDbGFzcyI6Mzp7czo0OiJ0ZXh0IjtzOjMyOiJOb3RoaW5nIHRoYXQgYW55b25lIGNhcmVzIGFib3V0LiI7czo1OiJ0aXRsZSI7czoxNjoiUmVhbGx5IG9sZCBibG9jayI7czo2OiJmb3JtYXQiO3M6MToiMSI7fQ==' + ] + ]; + } + + /** + * Check that entries in the block_instances table are coverted over correctly. + * + * @dataProvider encoded_strings_dataprovider + * @param string $original The original base64_encoded block config setting. + * @param string $expected The expected base64_encoded block config setting. + */ + public function test_upgrade_fix_block_instance_configuration($original, $expected) { + global $DB; + + $this->resetAfterTest(); + + $data = new stdClass(); + $data->blockname = 'html'; + $data->parentcontextid = 1; + $data->showinsubcontexts = 0; + $data->requirebytheme = 0; + $data->pagetypepattern = 'admin-setting-frontpagesettings'; + $data->defaultregion = 'side-post'; + $data->defaultweight = 1; + $data->timecreated = time(); + $data->timemodified = time(); + + $data->configdata = $original; + $entryid = $DB->insert_record('block_instances', $data); + upgrade_fix_block_instance_configuration(); + $record = $DB->get_record('block_instances', ['id' => $entryid]); + $this->assertEquals($expected, $record->configdata); + } }