diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index 883539b957c..e7aab48acb9 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -231,6 +231,9 @@ if ($hassiteconfig) { array('0' => new lang_string('none'), '1' => new lang_string('allfiles'), '2' => new lang_string('htmlfilesonly'))); $items[] = new admin_setting_configcheckbox('filtermatchoneperpage', new lang_string('filtermatchoneperpage', 'admin'), new lang_string('configfiltermatchoneperpage', 'admin'), 0); $items[] = new admin_setting_configcheckbox('filtermatchonepertext', new lang_string('filtermatchonepertext', 'admin'), new lang_string('configfiltermatchonepertext', 'admin'), 0); + $items[] = new admin_setting_configcheckbox('filternavigationwithsystemcontext', + new lang_string('filternavigationwithsystemcontext', 'admin'), + new lang_string('configfilternavigationwithsystemcontext', 'admin'), 0); foreach ($items as $item) { $item->set_updatedcallback('reset_text_filters_cache'); $temp->add($item); diff --git a/lang/en/admin.php b/lang/en/admin.php index 30bcdd3f69f..0e6039a5533 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -250,6 +250,7 @@ $string['configextramemorylimit'] = 'Some scripts like search, backup/restore or $string['configfilterall'] = 'Filter all strings, including headings, titles, navigation bar and so on. This is mostly useful when using the multilang filter, otherwise it will just create extra load on your site for little gain.'; $string['configfiltermatchoneperpage'] = 'Automatic linking filters will only generate a single link for the first matching text instance found on the complete page. All others are ignored.'; $string['configfiltermatchonepertext'] = 'Automatic linking filters will only generate a single link for the first matching text instance found in each item of text on the page. All others are ignored. This setting has no effect if \'Filter match once per page\' is enabled.'; +$string['configfilternavigationwithsystemcontext'] = 'Normal use of the filtering is tied to the context in which it is used (e.g. course context), but for the site navigation, explicitly making everything filter with site context can yield performance improvements when using "content and headings" filtering.'; $string['configfilteruploadedfiles'] = 'Process all uploaded HTML and text files with the filters before displaying them, only uploaded HTML files or none at all.'; $string['configforcelogin'] = 'Normally, the front page and the course listings (but not courses) can be read by people without logging in to the site. If you want to force people to log in before they do ANYTHING on the site, then you should enable this setting.'; $string['configforceloginforprofiles'] = 'This setting forces people to log in as a real (non-guest) account before viewing any user\'s profile. If you disabled this setting, you may find that some users post advertising (spam) or other inappropriate content in their profiles, which is then visible to the whole world.'; @@ -617,6 +618,7 @@ $string['filestoredinhelp'] = 'Where the file will be stored'; $string['filterall'] = 'Filter all strings'; $string['filtermatchoneperpage'] = 'Filter match once per page'; $string['filtermatchonepertext'] = 'Filter match once per text'; +$string['filternavigationwithsystemcontext'] = 'Filter navigation with system context'; $string['filters'] = 'Filters'; $string['filtersettings'] = 'Manage filters'; $string['filtersettingsgeneral'] = 'General filter settings'; diff --git a/lib/accesslib.php b/lib/accesslib.php index ba41ef59618..aa1ee39b458 100644 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -6185,6 +6185,21 @@ class context_helper extends context { return $classname::get_level_name(); } + /** + * Gets the current context to be used for navigation tree filtering. + * + * @param context|null $context The current context to be checked against. + * @return context|null the context that navigation tree filtering should use. + */ + public static function get_navigation_filter_context(?context $context): ?context { + global $CFG; + if (!empty($CFG->filternavigationwithsystemcontext)) { + return context_system::instance(); + } else { + return $context; + } + } + /** * not used */ diff --git a/lib/navigationlib.php b/lib/navigationlib.php index d64ccbd0bf9..a0d47f101eb 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -662,10 +662,13 @@ class navigation_node implements renderable { * @return string */ public function get_content($shorttext=false) { + $navcontext = \context_helper::get_navigation_filter_context(null); + $options = !empty($navcontext) ? ['context' => $navcontext] : null; + if ($shorttext && $this->shorttext!==null) { - return format_string($this->shorttext); + return format_string($this->shorttext, null, $options); } else { - return format_string($this->text); + return format_string($this->text, null, $options); } } @@ -1962,13 +1965,14 @@ class global_navigation extends navigation_node { * @return void. */ protected function add_category(stdClass $category, navigation_node $parent, $nodetype = self::TYPE_CATEGORY) { + global $CFG; if (array_key_exists($category->id, $this->addedcategories)) { return; } $canview = core_course_category::can_view_category($category); $url = $canview ? new moodle_url('/course/index.php', array('categoryid' => $category->id)) : null; - $context = context_coursecat::instance($category->id); - $categoryname = $canview ? format_string($category->name, true, array('context' => $context)) : + $context = \context_helper::get_navigation_filter_context(context_coursecat::instance($category->id)); + $categoryname = $canview ? format_string($category->name, true, ['context' => $context]) : get_string('categoryhidden'); $categorynode = $parent->add($categoryname, $url, $nodetype, $categoryname, $category->id); if (!$canview) { @@ -2179,7 +2183,8 @@ class global_navigation extends navigation_node { } // Prepare the default name and url for the node - $activityname = format_string($activity->name, true, array('context' => context_module::instance($activity->id))); + $displaycontext = \context_helper::get_navigation_filter_context(context_module::instance($activity->id)); + $activityname = format_string($activity->name, true, ['context' => $displaycontext]); $action = new moodle_url($activity->url); // Check if the onclick property is set (puke!) @@ -2631,8 +2636,9 @@ class global_navigation extends navigation_node { } $issite = ($course->id == $SITE->id); - $shortname = format_string($course->shortname, true, array('context' => $coursecontext)); - $fullname = format_string($course->fullname, true, array('context' => $coursecontext)); + $displaycontext = \context_helper::get_navigation_filter_context($coursecontext); + $shortname = format_string($course->shortname, true, ['context' => $displaycontext]); + $fullname = format_string($course->fullname, true, ['context' => $displaycontext]); // This is the name that will be shown for the course. $coursename = empty($CFG->navshowfullcoursenames) ? $shortname : $fullname; @@ -2687,7 +2693,7 @@ class global_navigation extends navigation_node { $coursenode->showinflatnavigation = $coursetype == self::COURSE_MY; $coursenode->hidden = (!$course->visible); - $coursenode->title(format_string($course->fullname, true, array('context' => $coursecontext, 'escape' => false))); + $coursenode->title(format_string($course->fullname, true, ['context' => $displaycontext, 'escape' => false])); if ($canexpandcourse) { // This course can be expanded by the user, make it a branch to make the system aware that its expandable by ajax. $coursenode->nodetype = self::NODETYPE_BRANCH; @@ -3659,8 +3665,10 @@ class navbar extends navigation_node { if (!core_course_category::can_view_category($category)) { continue; } - $url = new moodle_url('/course/index.php', array('categoryid' => $category->id)); - $name = format_string($category->name, true, array('context' => $context)); + + $displaycontext = \context_helper::get_navigation_filter_context($context); + $url = new moodle_url('/course/index.php', ['categoryid' => $category->id]); + $name = format_string($category->name, true, ['context' => $displaycontext]); $categorynode = breadcrumb_navigation_node::create($name, $url, self::TYPE_CATEGORY, null, $category->id); if (!$category->visible) { $categorynode->hidden = true; @@ -4011,10 +4019,11 @@ class flat_navigation extends navigation_node_collection { $url = new moodle_url('/course/view.php', array('id' => $course->id)); $coursecontext = context_course::instance($course->id, MUST_EXIST); + $displaycontext = \context_helper::get_navigation_filter_context($coursecontext); // This is the name that will be shown for the course. $coursename = empty($CFG->navshowfullcoursenames) ? - format_string($course->shortname, true, array('context' => $coursecontext)) : - format_string($course->fullname, true, array('context' => $coursecontext)); + format_string($course->shortname, true, ['context' => $displaycontext]) : + format_string($course->fullname, true, ['context' => $displaycontext]); $flat = new flat_navigation_node(navigation_node::create($coursename, $url), 0); $flat->set_collectionlabel($coursename); diff --git a/lib/tests/accesslib_test.php b/lib/tests/accesslib_test.php index 116db957fc4..ceb2a700c81 100644 --- a/lib/tests/accesslib_test.php +++ b/lib/tests/accesslib_test.php @@ -4595,6 +4595,29 @@ class core_accesslib_testcase extends advanced_testcase { $this->expectException(\required_capability_exception::class); require_all_capabilities($sca, $coursecontext); } + + /** + * Test get_navigation_filter_context. + * + * @covers ::get_navigation_filter_context + */ + public function test_get_navigation_filter_context() { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + set_config('filternavigationwithsystemcontext', 0); + // First test passed values are returned if disabled. + $this->assertNull(context_helper::get_navigation_filter_context(null)); + $coursecontext = context_course::instance($course->id); + $filtercontext = context_helper::get_navigation_filter_context($coursecontext); + $this->assertEquals($coursecontext->id, $filtercontext->id); + + // Now test that any input returns system context if enabled. + set_config('filternavigationwithsystemcontext', 1); + $filtercontext = context_helper::get_navigation_filter_context(null); + $this->assertInstanceOf('\context_system', $filtercontext); + $filtercontext = context_helper::get_navigation_filter_context($coursecontext); + $this->assertInstanceOf('\context_system', $filtercontext); + } } /**