diff --git a/availability/classes/info.php b/availability/classes/info.php index 05d82fa7a5d..25fb076a2a2 100644 --- a/availability/classes/info.php +++ b/availability/classes/info.php @@ -290,9 +290,9 @@ abstract class info { */ protected function warn_about_invalid_availability(\coding_exception $e) { $name = $this->get_thing_name(); - // If it occurs while building modinfo based on somebody calling $cm->name, - // we can't get $cm->name, and this line will cause a warning. - $htmlname = @$this->format_info($name, $this->course); + $htmlname = $this->format_info($name, $this->course); + // Because we call format_info here, likely in the middle of building dynamic data for the + // activity, there could be a chance that the name might not be available. if ($htmlname === '') { // So instead use the numbers (cmid) from the tag. $htmlname = preg_replace('~[^0-9]~', '', $name); @@ -739,11 +739,11 @@ abstract class info { $info = preg_replace_callback('~~', function($matches) use($modinfo, $context) { $cm = $modinfo->get_cm($matches[1]); - if ($cm->has_view() and $cm->uservisible) { + if ($cm->has_view() and $cm->get_user_visible()) { // Help student by providing a link to the module which is preventing availability. - return \html_writer::link($cm->url, format_string($cm->name, true, array('context' => $context))); + return \html_writer::link($cm->get_url(), format_string($cm->get_name(), true, ['context' => $context])); } else { - return format_string($cm->name, true, array('context' => $context)); + return format_string($cm->get_name(), true, ['context' => $context]); } }, $info); diff --git a/availability/tests/info_test.php b/availability/tests/info_test.php index e8044df1380..7bf202dc0b1 100644 --- a/availability/tests/info_test.php +++ b/availability/tests/info_test.php @@ -511,4 +511,33 @@ class info_testcase extends advanced_testcase { sort($result); $this->assertEquals($expected, $result); } + + /** + * Tests the info_module class when involved in a recursive call to $cm->name. + */ + public function test_info_recursive_name_call() { + global $DB; + + $this->resetAfterTest(); + + // Create a course and page. + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $page1 = $generator->create_module('page', ['course' => $course->id, 'name' => 'Page1']); + + // Set invalid availability. + $DB->set_field('course_modules', 'availability', 'not valid', ['id' => $page1->cmid]); + + // Get the cm_info object. + $this->setAdminUser(); + $modinfo = get_fast_modinfo($course); + $cm1 = $modinfo->get_cm($page1->cmid); + + // At this point we will generate dynamic data for $cm1, which will cause the debugging + // call below. + $this->assertEquals('Page1', $cm1->name); + + $this->assertDebuggingCalled('Error processing availability data for ' . + '‘Page1’: Invalid availability text'); + } } diff --git a/lib/modinfolib.php b/lib/modinfolib.php index 69bfbcb1009..9ada74eb416 100644 --- a/lib/modinfolib.php +++ b/lib/modinfolib.php @@ -1312,9 +1312,15 @@ class cm_info implements IteratorAggregate { } /** + * Gets the URL to link to for this module. + * + * This method is normally called by the property ->url, but can be called directly if + * there is a case when it might be called recursively (you can't call property values + * recursively). + * * @return moodle_url URL to link to for this module, or null if it doesn't have a view page */ - private function get_url() { + public function get_url() { $this->obtain_dynamic_data(); return $this->url; } @@ -1360,9 +1366,13 @@ class cm_info implements IteratorAggregate { /** * Getter method for property $name, ensures that dynamic data is obtained. + * + * This method is normally called by the property ->name, but can be called directly if there + * is a case when it might be called recursively (you can't call property values recursively). + * * @return string */ - private function get_name() { + public function get_name() { $this->obtain_dynamic_data(); return $this->name; } @@ -1915,9 +1925,14 @@ class cm_info implements IteratorAggregate { /** * Getter method for property $uservisible, ensures that dynamic data is retrieved. + * + * This method is normally called by the property ->uservisible, but can be called directly if + * there is a case when it might be called recursively (you can't call property values + * recursively). + * * @return bool */ - private function get_user_visible() { + public function get_user_visible() { $this->obtain_dynamic_data(); return $this->uservisible; }