diff --git a/.upgradenotes/MDL-85405-2025061114040750.yml b/.upgradenotes/MDL-85405-2025061114040750.yml new file mode 100644 index 00000000000..f2dafd54ea8 --- /dev/null +++ b/.upgradenotes/MDL-85405-2025061114040750.yml @@ -0,0 +1,9 @@ +issueNumber: MDL-85405 +notes: + core_course: + - message: >- + The external function `core_course_get_course_contents` now includes the + `candisplay` property for each returned module. If this is false, the + module should not be displayed on the course page (for example, for + question banks). + type: improved diff --git a/public/availability/classes/info.php b/public/availability/classes/info.php index 5f3fdff3802..a573374bfea 100644 --- a/public/availability/classes/info.php +++ b/public/availability/classes/info.php @@ -746,7 +746,11 @@ abstract class info { $modulename = format_string($cm->get_name(), true, ['context' => $context]); // We make sure that we add a data attribute to the name so we can change it later if the // original module name changes. - if ($cm->has_view() && $cm->get_user_visible()) { + if ( + \course_modinfo::is_mod_type_visible_on_course($cm->modname) + && $cm->has_view() + && $cm->get_user_visible() + ) { // Help student by providing a link to the module which is preventing availability. return \html_writer::link($cm->get_url(), $modulename, ['data-cm-name-for' => $cm->id]); } else { diff --git a/public/blocks/activity_modules/block_activity_modules.php b/public/blocks/activity_modules/block_activity_modules.php index cbf6c804fc1..2f4eee6a21c 100644 --- a/public/blocks/activity_modules/block_activity_modules.php +++ b/public/blocks/activity_modules/block_activity_modules.php @@ -53,7 +53,11 @@ class block_activity_modules extends block_list { foreach($modinfo->cms as $cm) { // Exclude activities that aren't visible or have no view link (e.g. label). Account for folder being displayed inline. - if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) { + if ( + !\course_modinfo::is_mod_type_visible_on_course($cm->modname) + || !$cm->uservisible + || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0) + ) { continue; } if (array_key_exists($cm->modname, $modfullnames)) { diff --git a/public/blocks/activity_modules/tests/behat/block_activity_modules.feature b/public/blocks/activity_modules/tests/behat/block_activity_modules.feature index d925524cafd..d99a7d0e8e5 100644 --- a/public/blocks/activity_modules/tests/behat/block_activity_modules.feature +++ b/public/blocks/activity_modules/tests/behat/block_activity_modules.feature @@ -26,6 +26,7 @@ Feature: Block activity modules | url | Frontpage url name | Frontpage url description | Acceptance test site | url0 | | wiki | Frontpage wiki name | Frontpage wiki description | Acceptance test site | wiki0 | | workshop | Frontpage workshop name | Frontpage workshop description | Acceptance test site | workshop0 | + | qbank | Frontpage qbank name | Frontpage qbank description | Acceptance test site | qbank0 | When I log in as "admin" And I am on site homepage @@ -74,6 +75,8 @@ Feature: Block activity modules And I should see "Frontpage imscp name" And I should see "Frontpage folder name" And I should see "Frontpage url name" + And I am on site homepage + And "Question banks" "link" should not exist in the "Activities" "block" Scenario: Add activities block in a course Given the following "courses" exist: @@ -100,6 +103,7 @@ Feature: Block activity modules | url | Test url name | Test url description | C1 | url1 | | wiki | Test wiki name | Test wiki description | C1 | wiki1 | | workshop | Test workshop name | Test workshop description | C1 | workshop1 | + | qbank | Test qbank name | Test qbank description | C1 | qbank1 | When I log in as "admin" And I am on "Course 1" course homepage with editing mode on @@ -147,3 +151,5 @@ Feature: Block activity modules And I should see "Test imscp name" And I should see "Test folder name" And I should see "Test url name" + And I am on "Course 1" course homepage + And "Question banks" "link" should not exist in the "Activities" "block" diff --git a/public/course/externallib.php b/public/course/externallib.php index be64fd4fc52..76f11754ec4 100644 --- a/public/course/externallib.php +++ b/public/course/externallib.php @@ -287,6 +287,7 @@ class core_course_external extends external_api { $module['completion'] = $cm->completion; $module['downloadcontent'] = $cm->downloadcontent; $module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false); + $module['candisplay'] = plugin_supports('mod', $cm->modname, FEATURE_CAN_DISPLAY, true); $module['dates'] = $activitydates; $module['groupmode'] = $cm->groupmode; @@ -488,6 +489,10 @@ class core_course_external extends external_api { 'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL), 'noviewlink' => new external_value(PARAM_BOOL, 'Whether the module has no view page', VALUE_OPTIONAL), + 'candisplay' => new external_value( + PARAM_BOOL, + 'Whether the module should be displayed on the course page', + VALUE_OPTIONAL), 'completion' => new external_value(PARAM_INT, 'Type of completion tracking: 0 means none, 1 manual, 2 automatic.', VALUE_OPTIONAL), 'completiondata' => $completiondefinition, diff --git a/public/course/format/classes/output/local/overview/overviewpage.php b/public/course/format/classes/output/local/overview/overviewpage.php index 9a43021c1dc..64c63e520f5 100644 --- a/public/course/format/classes/output/local/overview/overviewpage.php +++ b/public/course/format/classes/output/local/overview/overviewpage.php @@ -92,9 +92,14 @@ class overviewpage implements renderable, named_templatable { $archetypes = []; foreach ($modinfo->cms as $cm) { - // Exclude activities that aren't visible or have no view link (e.g. label). + // Exclude activities that are not displayed on the course page, + // aren't visible or have no view link (e.g. label). // Account for folder being displayed inline. - if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) { + if ( + !\course_modinfo::is_mod_type_visible_on_course($cm->modname) + || !$cm->uservisible + || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0) + ) { continue; } if (array_key_exists($cm->modname, $modfullnames)) { diff --git a/public/course/format/classes/output/local/overview/overviewtable.php b/public/course/format/classes/output/local/overview/overviewtable.php index f9f23100c07..7d99372e289 100644 --- a/public/course/format/classes/output/local/overview/overviewtable.php +++ b/public/course/format/classes/output/local/overview/overviewtable.php @@ -221,7 +221,9 @@ class overviewtable implements externable, named_templatable, renderable { private function is_cm_displayable(cm_info $cm): bool { // Folder is an exception because it has settings to be displayed in the course // page without having a view link. - return $cm->uservisible && ($cm->has_view() || strcmp($cm->modname, 'folder') === 0); + return $cm->uservisible + && ($cm->has_view() || strcmp($cm->modname, 'folder') === 0) + && \course_modinfo::is_mod_type_visible_on_course($cm->modname); } /** diff --git a/public/course/format/singleactivity/lib.php b/public/course/format/singleactivity/lib.php index a142102736b..acc9cd1181d 100644 --- a/public/course/format/singleactivity/lib.php +++ b/public/course/format/singleactivity/lib.php @@ -225,10 +225,11 @@ class format_singleactivity extends core_courseformat\base implements core_cours public static function get_supported_activities() { $availabletypes = get_module_types_names(); foreach ($availabletypes as $module => $name) { - if (plugin_supports('mod', $module, FEATURE_NO_VIEW_LINK, false)) { - unset($availabletypes[$module]); - } - if (sectiondelegate::has_delegate_class('mod_' . $module)) { + if ( + plugin_supports('mod', $module, FEATURE_NO_VIEW_LINK, false) + || sectiondelegate::has_delegate_class('mod_' . $module) + || !course_modinfo::is_mod_type_visible_on_course($module) + ) { unset($availabletypes[$module]); } } diff --git a/public/course/format/singleactivity/tests/behat/create_course.feature b/public/course/format/singleactivity/tests/behat/create_course.feature index f3d7782d432..515b4098749 100644 --- a/public/course/format/singleactivity/tests/behat/create_course.feature +++ b/public/course/format/singleactivity/tests/behat/create_course.feature @@ -34,6 +34,7 @@ Feature: Courses can be created in Single Activity mode # Check that not all the activity types are in the dropdown. And I should not see "Text and media" in the "Type of activity" "field" And I should not see "Subsection" in the "Type of activity" "field" + And I should not see "Question bank" in the "Type of activity" "field" And I set the field "Type of activity" to "Assignment" And I press "Save and display" And I should see "New Assignment" diff --git a/public/course/tests/behat/course_overview.feature b/public/course/tests/behat/course_overview.feature index 043b44a911d..45ebcadf3e7 100644 --- a/public/course/tests/behat/course_overview.feature +++ b/public/course/tests/behat/course_overview.feature @@ -109,6 +109,7 @@ Feature: Users can access the course activities overview page | url | C1 | Activity 18 | | wiki | C1 | Activity 19 | | workshop | C1 | Activity 20 | + | qbank | C1 | Activity 21 | Given I am on the "Course 1" "course > activities" page logged in as "teacher1" And I should see "Assignments" in the "assign_overview_collapsible" "region" And I should see "Choices" in the "choice_overview_collapsible" "region" @@ -124,6 +125,9 @@ Feature: Users can access the course activities overview page And I should see "Workshops" in the "workshop_overview_collapsible" "region" # All resources are grouped. And I should see "Resources" in the "resource_overview_collapsible" "region" + # Qbanks and labels are not shown. + And I should not see "Labels" in the "course-overview-page" "region" + And I should not see "Question banks" in the "course-overview-page" "region" @javascript Scenario: The resources overview is loaded at the moment the section is expanded via Ajax diff --git a/public/filter/activitynames/classes/text_filter.php b/public/filter/activitynames/classes/text_filter.php index ca8e321d25d..5b7d759dee1 100644 --- a/public/filter/activitynames/classes/text_filter.php +++ b/public/filter/activitynames/classes/text_filter.php @@ -20,6 +20,7 @@ use cache; use cache_store; use core\output\html_writer; use core_collator; +use course_modinfo; use filterobject; /** @@ -124,7 +125,12 @@ class text_filter extends \core_filters\text_filter { $sortedactivities = []; foreach ($modinfo->cms as $cm) { // Use normal access control and visibility, but exclude labels and hidden activities. - if ($cm->visible && $cm->has_view() && $cm->uservisible) { + if ( + $cm->visible + && $cm->has_view() + && $cm->uservisible + && course_modinfo::is_mod_type_visible_on_course($cm->modname) + ) { $sortedactivities[] = (object)[ 'name' => $cm->name, 'url' => $cm->url, diff --git a/public/filter/activitynames/tests/text_filter_test.php b/public/filter/activitynames/tests/text_filter_test.php index a86bd2faa87..8d771043d81 100644 --- a/public/filter/activitynames/tests/text_filter_test.php +++ b/public/filter/activitynames/tests/text_filter_test.php @@ -41,9 +41,18 @@ final class text_filter_test extends \advanced_testcase { 'page', ['course' => $course->id, 'name' => 'Test (2)'] ); + // Create a label and question bank that should not be linked to. + $this->getDataGenerator()->create_module( + 'label', + ['course' => $course->id, 'name' => 'Label 1', 'intro' => 'Label 1'] + ); + $this->getDataGenerator()->create_module( + 'qbank', + ['course' => $course->id, 'name' => 'Question bank 1'] + ); // Format text with all three entries in HTML. - $html = '
Please read the two pages Test 1 and Test (2).
'; + $html = 'Please read the two pages Test 1 and Test (2), but not Label 1 or Question bank 1.
'; $filtered = format_text($html, FORMAT_HTML, ['context' => $context]); // Find all the glossary links in the result. diff --git a/public/lang/en/moodle.php b/public/lang/en/moodle.php index af2d0dead08..86d529f794e 100644 --- a/public/lang/en/moodle.php +++ b/public/lang/en/moodle.php @@ -1369,6 +1369,8 @@ $string['missingteacher'] = 'Must choose something'; $string['missingurl'] = 'Missing URL'; $string['missingusername'] = 'Missing username'; $string['moddoesnotsupporttype'] = 'Module {$a->modname} does not support uploads of type {$a->type}'; +$string['modhidden'] = 'Availability'; +$string['modhidden_help'] = '* Hide on course page: Not available to students. This module cannot be shown to students.'; $string['modhide'] = 'Hide'; $string['modshow'] = 'Show'; $string['modvisible'] = 'Availability';