From c7a2e19ee0ba6edbf6c7e6e9843486e09b7531b5 Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Tue, 1 Feb 2022 17:53:14 +0100 Subject: [PATCH 1/3] MDL-73683 core_courseformat: fix state hasavailability The course index lock icon depends on the state hasrestrictions attributes on both section and course module. The lock icon should be consistent with the content availability information and it was not. With this patch the hasrestrictions attribute uses a similar logic than the course content. --- .../format/classes/output/local/state/cm.php | 9 +- .../classes/output/local/state/section.php | 48 ++- .../tests/output/local/state/cm_test.php | 394 ++++++++++++++++++ .../tests/output/local/state/section_test.php | 388 +++++++++++++++++ 4 files changed, 823 insertions(+), 16 deletions(-) create mode 100644 course/format/tests/output/local/state/cm_test.php create mode 100644 course/format/tests/output/local/state/section_test.php diff --git a/course/format/classes/output/local/state/cm.php b/course/format/classes/output/local/state/cm.php index fa6e28d7353..c41e4ba1ab8 100644 --- a/course/format/classes/output/local/state/cm.php +++ b/course/format/classes/output/local/state/cm.php @@ -95,8 +95,13 @@ class cm implements renderable { // Check if restriction access are visible to the user. $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $cm->context); if (!empty($CFG->enableavailability) && $canviewhidden) { - // Some users can see restrictions even if it does not apply to them. - $data->hascmrectrictions = !empty($cm->availableinfo); + if (has_capability('moodle/course:manageactivities', $cm->context)) { + // Course editors can see all restrictions. + $data->hascmrectrictions = !empty($cm->availability); + } else { + // Some users can see restrictions even if it does not apply to them. + $data->hascmrectrictions = !empty($cm->availableinfo); + } } else { $data->hascmrectrictions = !$data->accessvisible || !$cm->uservisible; } diff --git a/course/format/classes/output/local/state/section.php b/course/format/classes/output/local/state/section.php index 232720c2c6c..0b3fba0054e 100644 --- a/course/format/classes/output/local/state/section.php +++ b/course/format/classes/output/local/state/section.php @@ -16,6 +16,7 @@ namespace core_courseformat\output\local\state; +use core_availability\info_section; use core_courseformat\base as course_format; use section_info; use renderable; @@ -55,8 +56,6 @@ class section implements renderable { * @return array data context for a mustache template */ public function export_for_template(\renderer_base $output): stdClass { - global $CFG; - $format = $this->format; $course = $format->get_course(); $section = $this->section; @@ -87,20 +86,10 @@ class section implements renderable { 'sectionurl' => course_get_url($course, $section->section)->out(), 'current' => $format->is_section_current($section), 'indexcollapsed' => $indexcollapsed, - 'contentcollapsed' => $contentcollapsed + 'contentcollapsed' => $contentcollapsed, + 'hasrestrictions' => $this->get_has_restrictions(), ]; - // If the section availability restrictions must be displayed. - $canviewhidden = has_capability( - 'moodle/course:viewhiddenactivities', - context_course::instance($course->id) - ); - if (!empty($CFG->enableavailability) && $canviewhidden) { - $data->hasrestrictions = !empty($section->availableinfo); - } else { - $data->hasrestrictions = false; - } - if (empty($modinfo->sections[$section->section])) { return $data; } @@ -114,4 +103,35 @@ class section implements renderable { return $data; } + + /** + * Return if the section has a restrictions icon displayed or not. + * + * @return bool if the section has visible restrictions for the user. + */ + protected function get_has_restrictions(): bool { + global $CFG; + + $section = $this->section; + $course = $this->format->get_course(); + $context = context_course::instance($course->id); + + // Hidden sections have no restriction indicator displayed. + if (!$section->visible || empty($CFG->enableavailability)) { + return false; + } + if (!has_capability('moodle/course:viewhiddenactivities', $context)) { + // If the section is visible but not user visible means it has some locking rule. + return empty($section->uservisible); + } + + if (has_capability('moodle/course:manageactivities', $context)) { + // Course editors can see all restrictions if the section is visible. + $ci = new info_section($section); + return !empty($ci->get_full_information()); + } else { + // Some users can see restrictions even if it does not apply to them. + return !empty($section->availableinfo); + } + } } diff --git a/course/format/tests/output/local/state/cm_test.php b/course/format/tests/output/local/state/cm_test.php new file mode 100644 index 00000000000..3a33bd05733 --- /dev/null +++ b/course/format/tests/output/local/state/cm_test.php @@ -0,0 +1,394 @@ +. + +namespace core_courseformat\output\local\state; + +use availability_date\condition; +use core_availability\tree; +use context_course; +use stdClass; + +/** + * Tests for cm state class. + * + * @package core_courseformat + * @copyright 2022 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_courseformat\output\local\state\cm + */ +class cm_test extends \advanced_testcase { + + /** + * Setup to ensure that fixtures are loaded. + */ + public static function setupBeforeClass(): void { + global $CFG; + require_once($CFG->dirroot . '/course/lib.php'); + require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php'); + require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_state.php'); + require_once($CFG->libdir . '/externallib.php'); + } + + /** + * Test the behaviour of state\cm hasavailability attribute. + * + * @dataProvider hasrestrictions_state_provider + * @covers ::export_for_template + * + * @param string $format the course format + * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. + * @param string $rolename the user role name (editingteacher or student) + * @param bool $hasavailability if the activity|section has availability + * @param bool $available if the activity availability condition is available or not to the user + * @param bool $expected the expected result + */ + public function test_cm_hasrestrictions_state( + string $format = 'topics', + bool $addcanviewhidden = false, + string $rolename = 'editingteacher', + bool $hasavailability = false, + bool $available = false, + bool $expected = false + ) { + $data = $this->setup_hasrestrictions_scenario($format, $addcanviewhidden, $rolename, $hasavailability, $available); + + // Get the cm state. + $courseformat = $data->courseformat; + $renderer = $data->renderer; + + $cmclass = $courseformat->get_output_classname('state\\cm'); + + $cmstate = new $cmclass( + $courseformat, + $data->section, + $data->cm + ); + $state = $cmstate->export_for_template($renderer); + + $this->assertEquals($expected, $state->hascmrectrictions); + } + + /** + * Setup section or cm has restrictions scenario. + * + * @param string $format the course format + * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. + * @param string $rolename the user role name (editingteacher or student) + * @param bool $hasavailability if the activity|section has availability + * @param bool $available if the activity availability condition is available or not to the user + * @return stdClass the scenario instances. + */ + private function setup_hasrestrictions_scenario( + string $format = 'topics', + bool $addcanviewhidden = false, + string $rolename = 'editingteacher', + bool $hasavailability = false, + bool $available = false + ): stdClass { + global $PAGE, $DB; + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(['numsections' => 1, 'format' => $format]); + + // Create and enrol user. + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user( + $user->id, + $course->id, + $rolename + ); + $this->setUser($user); + + // Add capabilities if necessary. + if ($addcanviewhidden) { + $roleid = $DB->get_field('role', 'id', array('shortname' => $rolename), MUST_EXIST); + $coursecontext = context_course::instance($course->id); + assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleid, $coursecontext->id); + } + + // Create an activity. + $activity = $this->getDataGenerator()->create_module('page', ['course' => $course->id], [ + 'section' => 1, + 'visible' => 1 + ]); + + // Set up the availability settings. + if ($hasavailability) { + $operation = ($available) ? condition::DIRECTION_UNTIL : condition::DIRECTION_FROM; + $availabilityjson = json_encode(tree::get_root_json( + [ + condition::get_json($operation, time() + 3600), + ], + '&', + true + )); + $selector = ['id' => $activity->cmid]; + $DB->set_field('course_modules', 'availability', trim($availabilityjson), $selector); + } + + // Get the cm state. + $courseformat = course_get_format($course->id); + $modinfo = $courseformat->get_modinfo(); + $renderer = $courseformat->get_renderer($PAGE); + + if ($format == 'theunittest') { + // These course format's hasn't the renderer file, so a debugging message will be displayed. + $this->assertDebuggingCalled(); + } + + return (object)[ + 'courseformat' => $courseformat, + 'section' => $modinfo->get_section_info(1), + 'cm' => $modinfo->get_cm($activity->cmid), + 'renderer' => $renderer, + ]; + } + + /** + * Data provider for test_state(). + * + * @return array + */ + public function hasrestrictions_state_provider(): array { + return [ + // Teacher scenarios (topics). + 'Topics, can edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Topics, can edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, can edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Teacher scenarios (weeks). + 'Weeks, can edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Weeks, can edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, can edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Teacher scenarios (mock format). + 'Mock format, can edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Mock format, can edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, can edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (topics). + 'Topics, cannot edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Topics, cannot edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, cannot edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (weeks). + 'Weeks, cannot edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Weeks, cannot edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, cannot edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (mock format). + 'Mock format, cannot edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Mock format, cannot edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, cannot edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (topics). + 'Topics, can view hidden but not edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Topics, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, can view hidden but not edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (weeks). + 'Weeks, can view hidden but not edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Weeks, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, can view hidden but not edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (mock format). + 'Mock format, can view hidden but not edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Mock format, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, can view hidden but not edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + ]; + } +} diff --git a/course/format/tests/output/local/state/section_test.php b/course/format/tests/output/local/state/section_test.php new file mode 100644 index 00000000000..c7a5fcdf0ad --- /dev/null +++ b/course/format/tests/output/local/state/section_test.php @@ -0,0 +1,388 @@ +. + +namespace core_courseformat\output\local\state; + +use availability_date\condition; +use core_availability\tree; +use context_course; +use stdClass; + +/** + * Tests for section state class. + * + * @package core_courseformat + * @copyright 2022 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_courseformat\output\local\state\section + */ +class section_test extends \advanced_testcase { + + /** + * Setup to ensure that fixtures are loaded. + */ + public static function setupBeforeClass(): void { + global $CFG; + require_once($CFG->dirroot . '/course/lib.php'); + require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php'); + require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_state.php'); + require_once($CFG->libdir . '/externallib.php'); + } + + /** + * Test the behaviour of state\section hasavailability attribute. + * + * @dataProvider hasrestrictions_state_provider + * @covers ::export_for_template + * + * @param string $format the course format + * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. + * @param string $rolename the user role name (editingteacher or student) + * @param bool $hasavailability if the activity|section has availability + * @param bool $available if the activity availability condition is available or not to the user + * @param bool $expected the expected result + */ + public function test_section_hasrestrictions_state( + string $format = 'topics', + bool $addcanviewhidden = false, + string $rolename = 'editingteacher', + bool $hasavailability = false, + bool $available = false, + bool $expected = false + ) { + $data = $this->setup_hasrestrictions_scenario($format, $addcanviewhidden, $rolename, $hasavailability, $available); + + // Get the cm state. + $courseformat = $data->courseformat; + $renderer = $data->renderer; + + $sectionclass = $courseformat->get_output_classname('state\\section'); + + $sectionstate = new $sectionclass( + $courseformat, + $data->section + ); + $state = $sectionstate->export_for_template($renderer); + + $this->assertEquals($expected, $state->hasrestrictions); + } + + /** + * Setup section or cm has restrictions scenario. + * + * @param string $format the course format + * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. + * @param string $rolename the user role name (editingteacher or student) + * @param bool $hasavailability if the section has availability + * @param bool $available if the section availability condition is available or not to the user + * @return stdClass the scenario instances. + */ + private function setup_hasrestrictions_scenario( + string $format = 'topics', + bool $addcanviewhidden = false, + string $rolename = 'editingteacher', + bool $hasavailability = false, + bool $available = false + ): stdClass { + global $PAGE, $DB; + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(['numsections' => 1, 'format' => $format]); + + // Create and enrol user. + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user( + $user->id, + $course->id, + $rolename + ); + $this->setUser($user); + + // Add capabilities if necessary. + if ($addcanviewhidden) { + $roleid = $DB->get_field('role', 'id', array('shortname' => $rolename), MUST_EXIST); + $coursecontext = context_course::instance($course->id); + assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleid, $coursecontext->id); + } + + // Set up the availability settings. + if ($hasavailability) { + $operation = ($available) ? condition::DIRECTION_UNTIL : condition::DIRECTION_FROM; + $availabilityjson = json_encode(tree::get_root_json( + [ + condition::get_json($operation, time() + 3600), + ], + '&', + true + )); + $modinfo = get_fast_modinfo($course); + $sectioninfo = $modinfo->get_section_info(1); + $selector = ['id' => $sectioninfo->id]; + $DB->set_field('course_sections', 'availability', trim($availabilityjson), $selector); + } + rebuild_course_cache($course->id, true); + + $courseformat = course_get_format($course->id); + $modinfo = $courseformat->get_modinfo(); + $renderer = $courseformat->get_renderer($PAGE); + + if ($format == 'theunittest') { + // These course format's hasn't the renderer file, so a debugging message will be displayed. + $this->assertDebuggingCalled(); + } + + return (object)[ + 'courseformat' => $courseformat, + 'section' => $modinfo->get_section_info(1), + 'renderer' => $renderer, + ]; + } + + /** + * Data provider for test_state(). + * + * @return array + */ + public function hasrestrictions_state_provider(): array { + return [ + // Teacher scenarios (topics). + 'Topics, can edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Topics, can edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, can edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Teacher scenarios (weeks). + 'Weeks, can edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Weeks, can edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, can edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Teacher scenarios (mock format). + 'Mock format, can edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Mock format, can edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, can edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'editingteacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (topics). + 'Topics, cannot edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Topics, cannot edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, cannot edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (weeks). + 'Weeks, cannot edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Weeks, cannot edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, cannot edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Student scenarios (mock format). + 'Mock format, cannot edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Mock format, cannot edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, cannot edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => false, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (topics). + 'Topics, can view hidden but not edit, has availability and is available' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Topics, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Topics, can view hidden but not edit and has not availability' => [ + 'format' => 'topics', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (weeks). + 'Weeks, can view hidden but not edit, has availability and is available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Weeks, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Weeks, can view hidden but not edit and has not availability' => [ + 'format' => 'weeks', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Students with view hidden activities capabilities (mock format). + 'Mock format, can view hidden but not edit, has availability and is available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Mock format, can view hidden but not edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Mock format, can view hidden but not edit and has not availability' => [ + 'format' => 'theunittest', + 'addcanviewhidden' => true, + 'rolename' => 'student', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + ]; + } +} From d7d154bd8b376590a6ccaa6aefc2fd0ec7fd61ea Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Wed, 2 Feb 2022 16:30:03 +0100 Subject: [PATCH 2/3] MDL-73683 core_courseformat: format section visible logic Move the section visible logic to the format base class. This way format plugins can decide whenever a section is visible or not instead of using format custom settings from core. --- course/externallib.php | 8 +------ course/format/classes/base.php | 22 +++++++++++++++++++ course/format/classes/external/get_state.php | 2 +- .../format/classes/output/local/content.php | 8 +------ .../classes/output/local/state/course.php | 2 +- .../classes/output/local/state/section.php | 2 +- .../classes/output/section_renderer.php | 9 ++------ course/format/classes/stateupdates.php | 8 ++++--- 8 files changed, 34 insertions(+), 27 deletions(-) diff --git a/course/externallib.php b/course/externallib.php index 42a2369e245..2ed81936116 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -388,14 +388,8 @@ class core_course_external extends external_api { // We didn't this before to be able to retrieve stealth activities. foreach ($coursecontents as $sectionnumber => $sectioncontents) { $section = $sections[$sectionnumber]; - // Show the section if the user is permitted to access it OR - // if it's not available but there is some available info text which explains the reason & should display OR - // the course is configured to show hidden sections name. - $showsection = $section->uservisible || - ($section->visible && !$section->available && !empty($section->availableinfo)) || - (!$section->visible && empty($courseformat->get_course()->hiddensections)); - if (!$showsection) { + if (!$courseformat->is_section_visible($section)) { unset($coursecontents[$sectionnumber]); continue; } diff --git a/course/format/classes/base.php b/course/format/classes/base.php index 079cc4acf2c..36452825365 100644 --- a/course/format/classes/base.php +++ b/course/format/classes/base.php @@ -1210,6 +1210,28 @@ abstract class base { return ($sectionnum && ($course = $this->get_course()) && $course->marker == $sectionnum); } + /** + * Returns if an specific section is visible to the current user. + * + * Formats can overrride this method to implement any special section logic. + * + * @param section_info $section the section modinfo + * @return bool; + */ + public function is_section_visible(section_info $section): bool { + // Previous to Moodle 4.0 thas logic was hardcoded. To prevent errors in the contrib plugins + // the default logic is the same required for topics and weeks format and still uses + // a "hiddensections" format setting. + $course = $this->get_course(); + $hidesections = $course->hiddensections ?? true; + // Show the section if the user is permitted to access it, OR if it's not available + // but there is some available info text which explains the reason & should display, + // OR it is hidden but the course has a setting to display hidden sections as unavailable. + return $section->uservisible || + ($section->visible && !$section->available && !empty($section->availableinfo)) || + (!$section->visible && !$hidesections); + } + /** * return true if the course editor must be displayed. * diff --git a/course/format/classes/external/get_state.php b/course/format/classes/external/get_state.php index f718ac0d626..99c354f21bd 100644 --- a/course/format/classes/external/get_state.php +++ b/course/format/classes/external/get_state.php @@ -102,7 +102,7 @@ class get_state extends external_api { // Sections and course modules state. $sections = $modinfo->get_section_info_all(); foreach ($sections as $section) { - if (!empty($section->uservisible)) { + if ($courseformat->is_section_visible($section)) { // Only return this section data if it's visible by current user on the course page. $sectionstate = new $sectionclass($courseformat, $section); $result->section[] = $sectionstate->export_for_template($renderer); diff --git a/course/format/classes/output/local/content.php b/course/format/classes/output/local/content.php index 9c864fb7b4c..434cbdb8f88 100644 --- a/course/format/classes/output/local/content.php +++ b/course/format/classes/output/local/content.php @@ -153,13 +153,7 @@ class content implements renderable, templatable { continue; } - // Show the section if the user is permitted to access it, OR if it's not available - // but there is some available info text which explains the reason & should display, - // OR it is hidden but the course has a setting to display hidden sections as unavilable. - $showsection = $thissection->uservisible || - ($thissection->visible && !$thissection->available && !empty($thissection->availableinfo)) || - (!$thissection->visible && !$course->hiddensections); - if (!$showsection) { + if (!$format->is_section_visible($thissection)) { continue; } diff --git a/course/format/classes/output/local/state/course.php b/course/format/classes/output/local/state/course.php index e6f33e50401..475ff63c9bb 100644 --- a/course/format/classes/output/local/state/course.php +++ b/course/format/classes/output/local/state/course.php @@ -69,7 +69,7 @@ class course implements renderable { $sections = $modinfo->get_section_info_all(); foreach ($sections as $section) { - if (!empty($section->uservisible)) { + if ($format->is_section_visible($section)) { $data->sectionlist[] = $section->id; } } diff --git a/course/format/classes/output/local/state/section.php b/course/format/classes/output/local/state/section.php index 0b3fba0054e..f2294a26637 100644 --- a/course/format/classes/output/local/state/section.php +++ b/course/format/classes/output/local/state/section.php @@ -96,7 +96,7 @@ class section implements renderable { foreach ($modinfo->sections[$section->section] as $modnumber) { $mod = $modinfo->cms[$modnumber]; - if ($mod->is_visible_on_course_page()) { + if ($section->uservisible && $mod->is_visible_on_course_page()) { $data->cmlist[] = $mod->id; } } diff --git a/course/format/classes/output/section_renderer.php b/course/format/classes/output/section_renderer.php index f84ff3cf1d2..b0493ce94d3 100644 --- a/course/format/classes/output/section_renderer.php +++ b/course/format/classes/output/section_renderer.php @@ -1033,13 +1033,8 @@ abstract class section_renderer extends core_course_renderer { // Activities inside this section are 'orphaned', this section will be printed as 'stealth' below. continue; } - // Show the section if the user is permitted to access it, OR if it's not available - // but there is some available info text which explains the reason & should display, - // OR it is hidden but the course has a setting to display hidden sections as unavilable. - $showsection = $thissection->uservisible || - ($thissection->visible && !$thissection->available && !empty($thissection->availableinfo)) || - (!$thissection->visible && !$course->hiddensections); - if (!$showsection) { + + if (!$format->is_section_visible($thissection)) { continue; } diff --git a/course/format/classes/stateupdates.php b/course/format/classes/stateupdates.php index e4922e449f6..a3921da0082 100644 --- a/course/format/classes/stateupdates.php +++ b/course/format/classes/stateupdates.php @@ -111,14 +111,15 @@ class stateupdates implements JsonSerializable { } $course = $this->format->get_course(); $modinfo = course_modinfo::instance($course); + $format = $this->format; $section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST); - if (!$section->uservisible) { + if (!$format->is_section_visible($section)) { return; } - $sectionclass = $this->format->get_output_classname('state\\section'); + $sectionclass = $format->get_output_classname('state\\section'); $currentstate = new $sectionclass($this->format, $section); $this->add_update('section', $action, $currentstate->export_for_template($this->output)); @@ -162,12 +163,13 @@ class stateupdates implements JsonSerializable { $cm = $modinfo->get_cm($cmid); $section = $modinfo->get_section_info_by_id($cm->section); + $format = $this->format; if (!$section->uservisible || !$cm->is_visible_on_course_page()) { return; } - $cmclass = $this->format->get_output_classname('state\\cm'); + $cmclass = $format->get_output_classname('state\\cm'); $currentstate = new $cmclass($this->format, $section, $cm); $this->add_update('cm', $action, $currentstate->export_for_template($this->output)); From d8b8f9bc3bbfff2d9f1ace45132c6c1f60df1858 Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Fri, 18 Feb 2022 12:14:04 +0100 Subject: [PATCH 3/3] MDL-73683 core_courseformat: match hasRestrictions criteria Orignally, the lock icon in the course index and the course content represents different things and it was confusing for users. Now both section and cm state data calculate the hasRestrictions attribute the same way as the course content lock icons. --- course/amd/build/actions.min.js | 2 +- course/amd/build/actions.min.js.map | 2 +- course/amd/src/actions.js | 4 - .../amd/build/local/courseindex/cm.min.js | 2 +- .../amd/build/local/courseindex/cm.min.js.map | 2 +- .../build/local/courseindex/section.min.js | 2 +- .../local/courseindex/section.min.js.map | 2 +- course/format/amd/src/local/courseindex/cm.js | 4 +- .../amd/src/local/courseindex/section.js | 4 +- .../format/classes/output/local/state/cm.php | 44 ++-- .../classes/output/local/state/section.php | 22 +- .../templates/local/courseindex/cm.mustache | 4 +- .../local/courseindex/section.mustache | 4 +- .../tests/output/local/state/cm_test.php | 212 ++++++++---------- .../tests/output/local/state/section_test.php | 190 +++++++--------- theme/boost/scss/moodle/courseindex.scss | 2 +- theme/boost/style/moodle.css | 2 +- theme/classic/style/moodle.css | 2 +- 18 files changed, 221 insertions(+), 285 deletions(-) diff --git a/course/amd/build/actions.min.js b/course/amd/build/actions.min.js index 20369231437..b8206bb69df 100644 --- a/course/amd/build/actions.min.js +++ b/course/amd/build/actions.min.js @@ -1,2 +1,2 @@ -function _defineProperty(a,b,c){if(b in a){Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0})}else{a[b]=c}return a}define ("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core_course/events"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){var o=["moveSection","moveCm","addSection","deleteSection"],p=l.getCurrentCourseEditor(),q,r={EDITINPROGRESS:"editinprogress",SECTIONDRAGGABLE:"sectiondraggable",EDITINGMOVE:"editing_move"},s={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:"[data-region=\"sectionbadges\"]"};g.use("moodle-course-coursebase",function(){var a=M.course.format.get_section_selector();if(a){s.SECTIONLI=a}});var t=function(a,b,c,d){if(!(c instanceof Element)&&c.get!==void 0){c=c.get(0)}return m.dispatchEvent(a,b,c,d)},u=function(a){var b=a.get(0);if(b.dataset.id){return b.dataset.id}var c;g.use("moodle-course-util",function(a){c=a.Moodle.core_course.util.cm.getId(a.Node(b))});return c},v=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getName(c.Node(a.get(0)))});var c=p.state,d=u(a);if(!b&&c&&d){var e;b=null===(e=c.cm.get(d))||void 0===e?void 0:e.name}return b},w=function(a){a.addClass(r.EDITINPROGRESS);var b=a.find(s.ACTIONAREA).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();if(a.data("id")!==void 0){p.dispatch("cmLock",[a.data("id")],!0)}return c}return null},x=function(a){a.addClass(r.EDITINPROGRESS);var b=a.find(s.SECTIONACTIONMENU).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();if(a.data("id")!==void 0){p.dispatch("sectionLock",[a.data("id")],!0)}return c}return null},y=function(a){var b=a.get(0),c=M.util.add_lightbox(g,g.Node(b));if("section"==b.dataset.for&&b.dataset.id){p.dispatch("sectionLock",[b.dataset.id],!0);c.setAttribute("data-state","section");c.setAttribute("data-state-id",b.dataset.id)}c.show();return c},z=function(a,b,c){window.setTimeout(function(){a.removeClass(r.EDITINPROGRESS);if(b){b.hide()}if(a.data("id")!==void 0){var c="section"===a.data("for")?"sectionLock":"cmLock";p.dispatch(c,[a.data("id")],!1)}},c)},A=function(a,b){if(a){window.setTimeout(function(){a.hide();if(a.getAttribute("data-state")){p.dispatch("".concat(a.getAttribute("data-state"),"Lock"),[a.getAttribute("data-state-id")],!1)}},b)}},B=function(a){g.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource","#"+a)});if(M.core.actionmenu&&M.core.actionmenu.newDOMNode){M.core.actionmenu.newDOMNode(g.one("#"+a))}},C=function(b,c){var d=a("#"+b),e="[data-action="+c+"]";if("groupsseparate"===c||"groupsvisible"===c||"groupsnone"===c){e="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"}if(d.find(e).is(":visible")){d.find(e).focus()}else{d.find(s.MENU).find(s.TOGGLE).focus()}},D=function(b){var c=a("a:visible"),d=!1,e=null;c.each(function(){if(a.contains(b[0],this)){d=!0}else if(d){e=this;return!1}return!0});return e},E=function(c,e,f){var g=f.attr("data-action"),h=w(c),i=b.call([{methodname:"core_course_edit_module",args:{id:e,action:g,sectionreturn:f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0}}],!0),j;if("duplicate"===g){j=y(f.closest(s.SECTIONLI))}a.when.apply(a,i).done(function(b){var d=D(c);c.replaceWith(b);var f=[];a("
"+b+"
").find(s.ACTIVITYLI).each(function(b){B(a(this).attr("id"));if(0===b){C(a(this).attr("id"),g);d=null}f.push(u(a(this)))});if(d){d.focus()}z(c,h,400);A(j,400);c.trigger(a.Event("coursemoduleedited",{ajaxreturn:b,action:g}));p.dispatch("legacyActivityAction",g,e,f)}).fail(function(b){z(c,h);A(j);var f=a.Event("coursemoduleeditfailed",{exception:b,action:g});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})},F=function(c,d,e){if(e===void 0){e=p.sectionReturn}var f=a(c),g=w(f),h=b.call([{methodname:"core_course_get_module",args:{id:d,sectionreturn:e}}],!0);return new Promise(function(b,c){a.when.apply(a,h).done(function(a){z(f,g,400);L(a);b(a)}).fail(function(){z(f,g);c()})})},G=function(a,b){var c=a.attr("class").match(/modtype_([^\s]*)/)[1],f=v(a);e.get_string("pluginname",c).done(function(a){e.get_strings([{key:"confirm",component:"core"},{key:null===f?"deletechecktype":"deletechecktypename",param:{type:a,name:f}},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],b)})})},H=function(a,b){e.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done(function(c){d.confirm(c[0],a,c[1],c[2],b)})},I=function(a,b,f,g,h){return e.get_strings([{key:f,component:g}]).then(function(d){a.find("span.menu-action-text").html(d[0]);return c.renderPix(b,"core")}).then(function(b){a.find(".icon").replaceWith(b);a.attr("data-action",h)}).catch(d.exception)},J=function(b,c,d,e,f){var g=c.attr("data-action");if("hide"===g||"show"===g){if("hide"===g){b.addClass("hidden");O(b[0],"hiddenfromstudents",!0);I(c,"i/show","showfromothers","format_"+e,"show")}else{O(b[0],"hiddenfromstudents",!1);b.removeClass("hidden");I(c,"i/hide","hidefromothers","format_"+e,"hide")}if(d.modules!==void 0){for(var h in d.modules){L(d.modules[h])}}if(d.section_availability!==void 0){b.find(".section_availability").first().replaceWith(d.section_availability)}var k=p.state.section.get(f);if(k!==void 0){p.dispatch("sectionState",[f])}}else if("setmarker"===g){var i=a(s.SECTIONLI+".current"),j=i.find(s.SECTIONACTIONMENU+" a[data-action=removemarker]");i.removeClass("current");I(j,"i/marker","highlight","core","setmarker");b.addClass("current");I(c,"i/marked","highlightoff","core","removemarker");p.dispatch("legacySectionAction",g,f);O(b[0],"highlighted",!0)}else if("removemarker"===g){b.removeClass("current");I(c,"i/marker","highlight","core","setmarker");p.dispatch("legacySectionAction",g,f);O(b[0],"highlighted",!1)}},K=function(a){var b=document.getElementById(a);if(!b||!b.contains(document.activeElement)){return}if(b.querySelector(s.ACTIONAREA).contains(document.activeElement)){return"".concat(s.ACTIONAREA," [tabindex=\"0\"]")}if(document.activeElement.id){return"#".concat(document.activeElement.id)}},L=function(b){a("
"+b+"
").find(s.ACTIVITYLI).each(function(){var c=a(this).attr("id"),d=K(c);a(s.ACTIVITYLI+"#"+c).replaceWith(b);B(c);if(d){var e,f=document.getElementById(c);null===(e=f.querySelector(d))||void 0===e?void 0:e.focus()}})},N=function(c,e,f,g){var h=f.attr("data-action"),i=f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0;if(p.supportComponents&&o.includes(h)){return!1}var j=x(c),k=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:i}}],!0),l=y(c);a.when.apply(a,k).done(function(b){var d=a.parseJSON(b);z(c,j);A(l);c.find(s.SECTIONACTIONMENU).find(s.TOGGLE).focus();var i=a.Event("coursesectionedited",{ajaxreturn:d,action:h});c.trigger(i);if(!i.isDefaultPrevented()){J(c,f,d,g,e)}}).fail(function(b){z(c,j);A(l);var f=a.Event("coursesectioneditfailed",{exception:b,action:h});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}});return!0},O=function(a,b,e){var f=a.querySelector(s.SECTIONBADGES);if(!f){return}if(e){c.render("core_courseformat/local/content/section/badges",_defineProperty({},b,1)).then(function(a,b){c.prependNodeContents(f,a,b);return!0}).catch(d.exception)}else{var g=f.querySelector("[data-type=\""+b+"\"]");g.remove()}};g.use("moodle-course-coursebase",function(){M.course.coursebase.register_module({set_visibility_resource_ui:function set_visibility_resource_ui(b){var c=a(b.element.getDOMNode()),d=u(c);if(d){var e=c.find("."+r.EDITINGMOVE).attr("data-sectionreturn");F(c,d,e)}},updateMovedCmState:function updateMovedCmState(a){var b=p.state,c=b.cm.get(a.cmid);if(c!==void 0){p.dispatch("sectionState",[c.sectionid])}p.dispatch("cmState",[a.cmid])},updateMovedSectionState:function updateMovedSectionState(){p.dispatch("courseState")}})});p.addMutations({legacyActivityAction:function legacyActivityAction(a,b,c,d){var e=a.state,f=e.cm.get(c);if(f===void 0){return}var g=e.section.get(f.sectionid);if(g===void 0){return}p.dispatch("cmLock",[f.id],!0);a.setReadOnly(!1);f.locked=!1;switch(b){case"delete":g.cmlist=g.cmlist.reduce(function(a,b){if(b!=c){a.push(b)}return a},[]);e.cm.delete(c);break;case"hide":case"show":f.visible="show"===b?!0:!1;break;case"duplicate":p.dispatch("cmState",d);break;}a.setReadOnly(!0)},legacySectionAction:function legacySectionAction(a,b,c){var d=a.state,e=d.section.get(c);if(e===void 0){return}a.setReadOnly(!1);e.locked=!0;a.setReadOnly(!0);a.setReadOnly(!1);e.locked=!1;switch(b){case"setmarker":d.section.forEach(function(a){if(a.id!=c){a.current=!1}});e.current=!0;break;case"removemarker":e.current=!1;break;}a.setReadOnly(!0)}});return{initCoursePage:function initCoursePage(b){q=b;a("body").on("click keypress",s.ACTIVITYLI+" "+s.ACTIVITYACTION+"[data-action]",function(b){if("keypress"===b.type&&13!==b.keyCode){return}var c=a(this),d=c.closest(s.ACTIVITYLI),e=c.attr("data-action"),f=u(d);switch(e){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return;}if(!f){return}b.preventDefault();if("delete"===e){G(d,function(){E(d,f,c)})}else{E(d,f,c)}});a("body").on("click keypress",s.SECTIONLI+" "+s.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",function(c){if("keypress"===c.type&&13!==c.keyCode){return}var d=a(this),e=d.closest(s.SECTIONLI),f=d.closest(s.SECTIONACTIONMENU).attr("data-sectionid"),g=!0;if(d.attr("data-confirm")){H(d.attr("data-confirm"),function(){g=N(e,f,d,b)})}else{g=N(e,f,d,b)}if(g){c.preventDefault()}});a("body").on("updated","".concat(s.SECTIONLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){var b=p.state,c=b.section.get(a.ajaxreturn.itemid);if(c!==void 0){p.dispatch("sectionState",[a.ajaxreturn.itemid])}}});a("body").on("updated","".concat(s.ACTIVITYLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){p.dispatch("cmState",[a.ajaxreturn.itemid])}});if(p.supportComponents&&o.includes("addSection")){return}e.get_string("numberweeks").done(function(b){var c=a(s.ADDSECTIONS),d=c.attr("data-add-sections"),e=c.attr("data-new-sections"),f=a("
");f.find("label").html(b);h.create({title:d,type:h.types.SAVE_CANCEL,body:f.html()},c).done(function(b){var e=a(b.getBody()).find("#add_section_numsections"),f=function(){if(""+parseInt(e.val())===e.val()&&1<=parseInt(e.val())){document.location=c.attr("href")+"&numsections="+parseInt(e.val())}};b.setSaveButtonText(d);b.getRoot().on(i.shown,function(){e.focus().select().on("keydown",function(a){if(a.keyCode===j.enter){f()}})});b.getRoot().on(i.save,function(a){a.preventDefault();f()})})})},replaceSectionActionItem:function replaceSectionActionItem(a,b,c,d,e,f){k.debug("replaceSectionActionItem() is deprecated and will be removed.");var g=a.find(s.SECTIONACTIONMENU+" "+b);I(g,c,d,e,f)},refreshModule:F,refreshSection:function refreshSection(c,e,f){if(f===void 0){f=p.sectionReturn}var g=a(c),h="refresh",i=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:f}}],!0),j=x(g);return new Promise(function(b,c){a.when.apply(a,i).done(function(c){z(g,j);var d=a.parseJSON(c),f=a(d.content);g.replaceWith(f);a("".concat(s.SECTIONLI,"#").concat(e," ").concat(s.ACTIVITYLI)).each(function(a,b){B(b.data("id"))});var i=t(n.sectionRefreshed,{ajaxreturn:d,action:h,newSectionElement:f.get(0)},f);if(!i.defaultPrevented){J(f,a(s.SECTIONLI+"#"+e),d,q,e)}b(d)}).fail(function(a){var b=t("coursesectionrefreshfailed",{exception:a,action:h},g);if(!b.defaultPrevented){d.exception(a)}c()})})}}}); +function _defineProperty(a,b,c){if(b in a){Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0})}else{a[b]=c}return a}define ("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core_course/events"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){var o=["moveSection","moveCm","addSection","deleteSection"],p=l.getCurrentCourseEditor(),q,r={EDITINPROGRESS:"editinprogress",SECTIONDRAGGABLE:"sectiondraggable",EDITINGMOVE:"editing_move"},s={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:"[data-region=\"sectionbadges\"]"};g.use("moodle-course-coursebase",function(){var a=M.course.format.get_section_selector();if(a){s.SECTIONLI=a}});var t=function(a,b,c,d){if(!(c instanceof Element)&&c.get!==void 0){c=c.get(0)}return m.dispatchEvent(a,b,c,d)},u=function(a){var b=a.get(0);if(b.dataset.id){return b.dataset.id}var c;g.use("moodle-course-util",function(a){c=a.Moodle.core_course.util.cm.getId(a.Node(b))});return c},v=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getName(c.Node(a.get(0)))});var c=p.state,d=u(a);if(!b&&c&&d){var e;b=null===(e=c.cm.get(d))||void 0===e?void 0:e.name}return b},w=function(a){a.addClass(r.EDITINPROGRESS);var b=a.find(s.ACTIONAREA).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();if(a.data("id")!==void 0){p.dispatch("cmLock",[a.data("id")],!0)}return c}return null},x=function(a){a.addClass(r.EDITINPROGRESS);var b=a.find(s.SECTIONACTIONMENU).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();if(a.data("id")!==void 0){p.dispatch("sectionLock",[a.data("id")],!0)}return c}return null},y=function(a){var b=a.get(0),c=M.util.add_lightbox(g,g.Node(b));if("section"==b.dataset.for&&b.dataset.id){p.dispatch("sectionLock",[b.dataset.id],!0);c.setAttribute("data-state","section");c.setAttribute("data-state-id",b.dataset.id)}c.show();return c},z=function(a,b,c){window.setTimeout(function(){a.removeClass(r.EDITINPROGRESS);if(b){b.hide()}if(a.data("id")!==void 0){var c="section"===a.data("for")?"sectionLock":"cmLock";p.dispatch(c,[a.data("id")],!1)}},c)},A=function(a,b){if(a){window.setTimeout(function(){a.hide();if(a.getAttribute("data-state")){p.dispatch("".concat(a.getAttribute("data-state"),"Lock"),[a.getAttribute("data-state-id")],!1)}},b)}},B=function(a){g.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource","#"+a)});if(M.core.actionmenu&&M.core.actionmenu.newDOMNode){M.core.actionmenu.newDOMNode(g.one("#"+a))}},C=function(b,c){var d=a("#"+b),e="[data-action="+c+"]";if("groupsseparate"===c||"groupsvisible"===c||"groupsnone"===c){e="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"}if(d.find(e).is(":visible")){d.find(e).focus()}else{d.find(s.MENU).find(s.TOGGLE).focus()}},D=function(b){var c=a("a:visible"),d=!1,e=null;c.each(function(){if(a.contains(b[0],this)){d=!0}else if(d){e=this;return!1}return!0});return e},E=function(c,e,f){var g=f.attr("data-action"),h=w(c),i=b.call([{methodname:"core_course_edit_module",args:{id:e,action:g,sectionreturn:f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0}}],!0),j;if("duplicate"===g){j=y(f.closest(s.SECTIONLI))}a.when.apply(a,i).done(function(b){var d=D(c);c.replaceWith(b);var f=[];a("
"+b+"
").find(s.ACTIVITYLI).each(function(b){B(a(this).attr("id"));if(0===b){C(a(this).attr("id"),g);d=null}f.push(u(a(this)))});if(d){d.focus()}z(c,h,400);A(j,400);c.trigger(a.Event("coursemoduleedited",{ajaxreturn:b,action:g}));p.dispatch("legacyActivityAction",g,e,f)}).fail(function(b){z(c,h);A(j);var f=a.Event("coursemoduleeditfailed",{exception:b,action:g});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})},F=function(c,d,e){if(e===void 0){e=p.sectionReturn}var f=a(c),g=w(f),h=b.call([{methodname:"core_course_get_module",args:{id:d,sectionreturn:e}}],!0);return new Promise(function(b,c){a.when.apply(a,h).done(function(a){z(f,g,400);L(a);b(a)}).fail(function(){z(f,g);c()})})},G=function(a,b){var c=a.attr("class").match(/modtype_([^\s]*)/)[1],f=v(a);e.get_string("pluginname",c).done(function(a){e.get_strings([{key:"confirm",component:"core"},{key:null===f?"deletechecktype":"deletechecktypename",param:{type:a,name:f}},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],b)})})},H=function(a,b){e.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done(function(c){d.confirm(c[0],a,c[1],c[2],b)})},I=function(a,b,f,g,h){return e.get_strings([{key:f,component:g}]).then(function(d){a.find("span.menu-action-text").html(d[0]);return c.renderPix(b,"core")}).then(function(b){a.find(".icon").replaceWith(b);a.attr("data-action",h)}).catch(d.exception)},J=function(b,c,d,e,f){var g=c.attr("data-action");if("hide"===g||"show"===g){if("hide"===g){b.addClass("hidden");O(b[0],"hiddenfromstudents",!0);I(c,"i/show","showfromothers","format_"+e,"show")}else{O(b[0],"hiddenfromstudents",!1);b.removeClass("hidden");I(c,"i/hide","hidefromothers","format_"+e,"hide")}if(d.modules!==void 0){for(var h in d.modules){L(d.modules[h])}}if(d.section_availability!==void 0){b.find(".section_availability").first().replaceWith(d.section_availability)}var k=p.state.section.get(f);if(k!==void 0){p.dispatch("sectionState",[f])}}else if("setmarker"===g){var i=a(s.SECTIONLI+".current"),j=i.find(s.SECTIONACTIONMENU+" a[data-action=removemarker]");i.removeClass("current");I(j,"i/marker","highlight","core","setmarker");b.addClass("current");I(c,"i/marked","highlightoff","core","removemarker");p.dispatch("legacySectionAction",g,f);O(b[0],"highlighted",!0)}else if("removemarker"===g){b.removeClass("current");I(c,"i/marker","highlight","core","setmarker");p.dispatch("legacySectionAction",g,f);O(b[0],"highlighted",!1)}},K=function(a){var b=document.getElementById(a);if(!b||!b.contains(document.activeElement)){return}if(b.querySelector(s.ACTIONAREA).contains(document.activeElement)){return"".concat(s.ACTIONAREA," [tabindex=\"0\"]")}if(document.activeElement.id){return"#".concat(document.activeElement.id)}},L=function(b){a("
"+b+"
").find(s.ACTIVITYLI).each(function(){var c=a(this).attr("id"),d=K(c);a(s.ACTIVITYLI+"#"+c).replaceWith(b);B(c);if(d){var e,f=document.getElementById(c);null===(e=f.querySelector(d))||void 0===e?void 0:e.focus()}})},N=function(c,e,f,g){var h=f.attr("data-action"),i=f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0;if(p.supportComponents&&o.includes(h)){return!1}var j=x(c),k=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:i}}],!0),l=y(c);a.when.apply(a,k).done(function(b){var d=a.parseJSON(b);z(c,j);A(l);c.find(s.SECTIONACTIONMENU).find(s.TOGGLE).focus();var i=a.Event("coursesectionedited",{ajaxreturn:d,action:h});c.trigger(i);if(!i.isDefaultPrevented()){J(c,f,d,g,e)}}).fail(function(b){z(c,j);A(l);var f=a.Event("coursesectioneditfailed",{exception:b,action:h});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}});return!0},O=function(a,b,e){var f=a.querySelector(s.SECTIONBADGES);if(!f){return}if(e){c.render("core_courseformat/local/content/section/badges",_defineProperty({},b,1)).then(function(a,b){c.prependNodeContents(f,a,b);return!0}).catch(d.exception)}else{var g=f.querySelector("[data-type=\""+b+"\"]");g.remove()}};g.use("moodle-course-coursebase",function(){M.course.coursebase.register_module({set_visibility_resource_ui:function set_visibility_resource_ui(b){var c=a(b.element.getDOMNode()),d=u(c);if(d){var e=c.find("."+r.EDITINGMOVE).attr("data-sectionreturn");F(c,d,e)}},updateMovedCmState:function updateMovedCmState(a){var b=p.state,c=b.cm.get(a.cmid);if(c!==void 0){p.dispatch("sectionState",[c.sectionid])}p.dispatch("cmState",[a.cmid])},updateMovedSectionState:function updateMovedSectionState(){p.dispatch("courseState")}})});p.addMutations({legacyActivityAction:function legacyActivityAction(a,b,c,d){var e=a.state,f=e.cm.get(c);if(f===void 0){return}var g=e.section.get(f.sectionid);if(g===void 0){return}p.dispatch("cmLock",[f.id],!0);a.setReadOnly(!1);f.locked=!1;switch(b){case"delete":g.cmlist=g.cmlist.reduce(function(a,b){if(b!=c){a.push(b)}return a},[]);e.cm.delete(c);break;case"hide":case"show":case"duplicate":p.dispatch("cmState",d);break;}a.setReadOnly(!0)},legacySectionAction:function legacySectionAction(a,b,c){var d=a.state,e=d.section.get(c);if(e===void 0){return}a.setReadOnly(!1);e.locked=!0;a.setReadOnly(!0);a.setReadOnly(!1);e.locked=!1;switch(b){case"setmarker":d.section.forEach(function(a){if(a.id!=c){a.current=!1}});e.current=!0;break;case"removemarker":e.current=!1;break;}a.setReadOnly(!0)}});return{initCoursePage:function initCoursePage(b){q=b;a("body").on("click keypress",s.ACTIVITYLI+" "+s.ACTIVITYACTION+"[data-action]",function(b){if("keypress"===b.type&&13!==b.keyCode){return}var c=a(this),d=c.closest(s.ACTIVITYLI),e=c.attr("data-action"),f=u(d);switch(e){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return;}if(!f){return}b.preventDefault();if("delete"===e){G(d,function(){E(d,f,c)})}else{E(d,f,c)}});a("body").on("click keypress",s.SECTIONLI+" "+s.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",function(c){if("keypress"===c.type&&13!==c.keyCode){return}var d=a(this),e=d.closest(s.SECTIONLI),f=d.closest(s.SECTIONACTIONMENU).attr("data-sectionid"),g=!0;if(d.attr("data-confirm")){H(d.attr("data-confirm"),function(){g=N(e,f,d,b)})}else{g=N(e,f,d,b)}if(g){c.preventDefault()}});a("body").on("updated","".concat(s.SECTIONLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){var b=p.state,c=b.section.get(a.ajaxreturn.itemid);if(c!==void 0){p.dispatch("sectionState",[a.ajaxreturn.itemid])}}});a("body").on("updated","".concat(s.ACTIVITYLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){p.dispatch("cmState",[a.ajaxreturn.itemid])}});if(p.supportComponents&&o.includes("addSection")){return}e.get_string("numberweeks").done(function(b){var c=a(s.ADDSECTIONS),d=c.attr("data-add-sections"),e=c.attr("data-new-sections"),f=a("
");f.find("label").html(b);h.create({title:d,type:h.types.SAVE_CANCEL,body:f.html()},c).done(function(b){var e=a(b.getBody()).find("#add_section_numsections"),f=function(){if(""+parseInt(e.val())===e.val()&&1<=parseInt(e.val())){document.location=c.attr("href")+"&numsections="+parseInt(e.val())}};b.setSaveButtonText(d);b.getRoot().on(i.shown,function(){e.focus().select().on("keydown",function(a){if(a.keyCode===j.enter){f()}})});b.getRoot().on(i.save,function(a){a.preventDefault();f()})})})},replaceSectionActionItem:function replaceSectionActionItem(a,b,c,d,e,f){k.debug("replaceSectionActionItem() is deprecated and will be removed.");var g=a.find(s.SECTIONACTIONMENU+" "+b);I(g,c,d,e,f)},refreshModule:F,refreshSection:function refreshSection(c,e,f){if(f===void 0){f=p.sectionReturn}var g=a(c),h="refresh",i=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:f}}],!0),j=x(g);return new Promise(function(b,c){a.when.apply(a,i).done(function(c){z(g,j);var d=a.parseJSON(c),f=a(d.content);g.replaceWith(f);a("".concat(s.SECTIONLI,"#").concat(e," ").concat(s.ACTIVITYLI)).each(function(a,b){B(b.data("id"))});var i=t(n.sectionRefreshed,{ajaxreturn:d,action:h,newSectionElement:f.get(0)},f);if(!i.defaultPrevented){J(f,a(s.SECTIONLI+"#"+e),d,q,e)}b(d)}).fail(function(a){var b=t("coursesectionrefreshfailed",{exception:a,action:h},g);if(!b.defaultPrevented){d.exception(a)}c()})})}}}); //# sourceMappingURL=actions.min.js.map diff --git a/course/amd/build/actions.min.js.map b/course/amd/build/actions.min.js.map index 21d88e03ced..bab3b1c0b20 100644 --- a/course/amd/build/actions.min.js.map +++ b/course/amd/build/actions.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/actions.js"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalFactory","ModalEvents","KeyCodes","log","editor","EventDispatcher","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","EDITINPROGRESS","SECTIONDRAGGABLE","EDITINGMOVE","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","getModuleName","name","getName","state","cmid","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","focusActionItem","elementId","action","mainelement","selector","is","focus","findNextFocusable","mainElement","tabables","isInside","foundElement","each","contains","editModule","moduleElement","target","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","elementToFocus","replaceWith","affectedids","index","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","get_string","pluginname","get_strings","key","component","param","type","s","confirm","confirmEditSection","message","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","getActivityFocusedElement","document","getElementById","activeElement","querySelector","activityHTML","focusedPath","newItem","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","sectionbadges","render","js","prependNodeContents","badge","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","visible","legacySectionAction","forEach","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","isExecuted","itemid","strNumberSections","modalTitle","newSections","modalBody","create","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","event","sectionRefreshed","defaultPrevented"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Various actions on modules and sections in the editing mode - hiding, duplicating, deleting, etc.\n *\n * @module core_course/actions\n * @copyright 2016 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.3\n */\ndefine(\n [\n 'jquery',\n 'core/ajax',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/url',\n 'core/yui',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/key_codes',\n 'core/log',\n 'core_courseformat/courseeditor',\n 'core/event_dispatcher',\n 'core_course/events'\n ],\n function(\n $,\n ajax,\n templates,\n notification,\n str,\n url,\n Y,\n ModalFactory,\n ModalEvents,\n KeyCodes,\n log,\n editor,\n EventDispatcher,\n CourseEvents\n ) {\n\n // Eventually, core_courseformat/local/content/actions will handle all actions for\n // component compatible formats and the default actions.js won't be necessary anymore.\n // Meanwhile, we filter the migrated actions.\n const componentActions = ['moveSection', 'moveCm', 'addSection', 'deleteSection'];\n\n // The course reactive instance.\n const courseeditor = editor.getCurrentCourseEditor();\n\n // The current course format name (loaded on init).\n let formatname;\n\n var CSS = {\n EDITINPROGRESS: 'editinprogress',\n SECTIONDRAGGABLE: 'sectiondraggable',\n EDITINGMOVE: 'editing_move'\n };\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n ACTIONAREA: '.actions',\n ACTIVITYACTION: 'a.cm-edit-action',\n MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',\n TOGGLE: '.toggle-display,.dropdown-toggle',\n SECTIONLI: 'li.section',\n SECTIONACTIONMENU: '.section_action_menu',\n ADDSECTIONS: '.changenumsections [data-add-sections]',\n SECTIONBADGES: '[data-region=\"sectionbadges\"]',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n /**\n * Dispatch event wrapper.\n *\n * Old jQuery events will be replaced by native events gradually.\n *\n * @method dispatchEvent\n * @param {String} eventName The name of the event\n * @param {Object} detail Any additional details to pass into the eveent\n * @param {Node|HTMLElement} container The point at which to dispatch the event\n * @param {Object} options\n * @param {Boolean} options.bubbles Whether to bubble up the DOM\n * @param {Boolean} options.cancelable Whether preventDefault() can be called\n * @param {Boolean} options.composed Whether the event can bubble across the ShadowDOM boundary\n * @returns {CustomEvent}\n */\n const dispatchEvent = function(eventName, detail, container, options) {\n // Most actions still uses jQuery node instead of regular HTMLElement.\n if (!(container instanceof Element) && container.get !== undefined) {\n container = container.get(0);\n }\n return EventDispatcher.dispatchEvent(eventName, detail, container, options);\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n // Check if we have a data-id first.\n const item = element.get(0);\n if (item.dataset.id) {\n return item.dataset.id;\n }\n // Use YUI way if data-id is not present.\n let id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(item));\n });\n return id;\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getName\n *\n * @param {JQuery} element\n * @returns {String}\n */\n var getModuleName = function(element) {\n var name;\n Y.use('moodle-course-util', function(Y) {\n name = Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)));\n });\n // Check if we have the name in the course state.\n const state = courseeditor.state;\n const cmid = getModuleId(element);\n if (!name && state && cmid) {\n name = state.cm.get(cmid)?.name;\n }\n return name;\n };\n\n /**\n * Wrapper for M.util.add_spinner for an activity\n *\n * @param {JQuery} activity\n * @returns {Node}\n */\n var addActivitySpinner = function(activity) {\n activity.addClass(CSS.EDITINPROGRESS);\n var actionarea = activity.find(SELECTOR.ACTIONAREA).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the activity state element.\n if (activity.data('id') !== undefined) {\n courseeditor.dispatch('cmLock', [activity.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_spinner for a section\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionSpinner = function(sectionelement) {\n sectionelement.addClass(CSS.EDITINPROGRESS);\n var actionarea = sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the section state element.\n if (sectionelement.data('id') !== undefined) {\n courseeditor.dispatch('sectionLock', [sectionelement.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_lightbox\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionLightbox = function(sectionelement) {\n const item = sectionelement.get(0);\n var lightbox = M.util.add_lightbox(Y, Y.Node(item));\n if (item.dataset.for == 'section' && item.dataset.id) {\n courseeditor.dispatch('sectionLock', [item.dataset.id], true);\n lightbox.setAttribute('data-state', 'section');\n lightbox.setAttribute('data-state-id', item.dataset.id);\n }\n lightbox.show();\n return lightbox;\n };\n\n /**\n * Removes the spinner element\n *\n * @param {JQuery} element\n * @param {Node} spinner\n * @param {Number} delay\n */\n var removeSpinner = function(element, spinner, delay) {\n window.setTimeout(function() {\n element.removeClass(CSS.EDITINPROGRESS);\n if (spinner) {\n spinner.hide();\n }\n // Unlock the state element.\n if (element.data('id') !== undefined) {\n const mutation = (element.data('for') === 'section') ? 'sectionLock' : 'cmLock';\n courseeditor.dispatch(mutation, [element.data('id')], false);\n }\n }, delay);\n };\n\n /**\n * Removes the lightbox element\n *\n * @param {Node} lightbox lighbox YUI element returned by addSectionLightbox\n * @param {Number} delay\n */\n var removeLightbox = function(lightbox, delay) {\n if (lightbox) {\n window.setTimeout(function() {\n lightbox.hide();\n // Unlock state if necessary.\n if (lightbox.getAttribute('data-state')) {\n courseeditor.dispatch(\n `${lightbox.getAttribute('data-state')}Lock`,\n [lightbox.getAttribute('data-state-id')],\n false\n );\n }\n }, delay);\n }\n };\n\n /**\n * Initialise action menu for the element (section or module)\n *\n * @param {String} elementid CSS id attribute of the element\n */\n var initActionMenu = function(elementid) {\n // Initialise action menu in the new activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);\n });\n if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {\n M.core.actionmenu.newDOMNode(Y.one('#' + elementid));\n }\n };\n\n /**\n * Returns focus to the element that was clicked or \"Edit\" link if element is no longer visible.\n *\n * @param {String} elementId CSS id attribute of the element\n * @param {String} action data-action property of the element that was clicked\n */\n var focusActionItem = function(elementId, action) {\n var mainelement = $('#' + elementId);\n var selector = '[data-action=' + action + ']';\n if (action === 'groupsseparate' || action === 'groupsvisible' || action === 'groupsnone') {\n // New element will have different data-action.\n selector = '[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]';\n }\n if (mainelement.find(selector).is(':visible')) {\n mainelement.find(selector).focus();\n } else {\n // Element not visible, focus the \"Edit\" link.\n mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus();\n }\n };\n\n /**\n * Find next after the element\n *\n * @param {JQuery} mainElement element that is about to be deleted\n * @returns {JQuery}\n */\n var findNextFocusable = function(mainElement) {\n var tabables = $(\"a:visible\");\n var isInside = false;\n var foundElement = null;\n tabables.each(function() {\n if ($.contains(mainElement[0], this)) {\n isInside = true;\n } else if (isInside) {\n foundElement = this;\n return false; // Returning false in .each() is equivalent to \"break;\" inside the loop in php.\n }\n return true;\n });\n return foundElement;\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} moduleElement activity element we perform action on\n * @param {Number} cmid\n * @param {JQuery} target the element (menu item) that was clicked\n */\n var editModule = function(moduleElement, cmid, target) {\n var action = target.attr('data-action');\n var spinner = addActivitySpinner(moduleElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_module',\n args: {id: cmid,\n action: action,\n sectionreturn: target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0\n }\n }], true);\n\n var lightbox;\n if (action === 'duplicate') {\n lightbox = addSectionLightbox(target.closest(SELECTOR.SECTIONLI));\n }\n $.when.apply($, promises)\n .done(function(data) {\n var elementToFocus = findNextFocusable(moduleElement);\n moduleElement.replaceWith(data);\n let affectedids = [];\n // Initialise action menu for activity(ies) added as a result of this.\n $('
' + data + '
').find(SELECTOR.ACTIVITYLI).each(function(index) {\n initActionMenu($(this).attr('id'));\n if (index === 0) {\n focusActionItem($(this).attr('id'), action);\n elementToFocus = null;\n }\n // Save any activity id in cmids.\n affectedids.push(getModuleId($(this)));\n });\n // In case of activity deletion focus the next focusable element.\n if (elementToFocus) {\n elementToFocus.focus();\n }\n // Remove spinner and lightbox with a delay.\n removeSpinner(moduleElement, spinner, 400);\n removeLightbox(lightbox, 400);\n // Trigger event that can be observed by course formats.\n moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n // Modify cm state.\n courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(moduleElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n moduleElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n };\n\n /**\n * Requests html for the module via WS core_course_get_module and updates the module on the course page\n *\n * Used after d&d of the module to another section\n *\n * @param {JQuery|Element} element\n * @param {Number} cmid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshModule = function(element, cmid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const activityElement = $(element);\n var spinner = addActivitySpinner(activityElement);\n var promises = ajax.call([{\n methodname: 'core_course_get_module',\n args: {id: cmid, sectionreturn: sectionreturn}\n }], true);\n\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n resolve(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\n reject();\n });\n });\n };\n\n /**\n * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n *\n * @param {JQuery|Element} element\n * @param {Number} sectionid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshSection = function(element, sectionid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const sectionElement = $(element);\n const action = 'refresh';\n const promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action, sectionreturn},\n }], true);\n\n var spinner = addSectionSpinner(sectionElement);\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(dataencoded => {\n\n removeSpinner(sectionElement, spinner);\n const data = $.parseJSON(dataencoded);\n\n const newSectionElement = $(data.content);\n sectionElement.replaceWith(newSectionElement);\n\n // Init modules menus.\n $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n (index, activity) => {\n initActionMenu(activity.data('id'));\n }\n );\n\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n CourseEvents.sectionRefreshed,\n {\n ajaxreturn: data,\n action: action,\n newSectionElement: newSectionElement.get(0),\n },\n newSectionElement\n );\n\n if (!event.defaultPrevented) {\n defaultEditSectionHandler(\n newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n data,\n formatname,\n sectionid\n );\n }\n resolve(data);\n }).fail(ex => {\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n 'coursesectionrefreshfailed',\n {exception: ex, action: action},\n sectionElement\n );\n if (!event.defaultPrevented) {\n notification.exception(ex);\n }\n reject();\n });\n });\n };\n\n /**\n * Displays the delete confirmation to delete a module\n *\n * @param {JQuery} mainelement activity element we perform action on\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmDeleteModule = function(mainelement, onconfirm) {\n var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n var modulename = getModuleName(mainelement);\n\n str.get_string('pluginname', modtypename).done(function(pluginname) {\n var plugindata = {\n type: pluginname,\n name: modulename\n };\n str.get_strings([\n {key: 'confirm', component: 'core'},\n {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n }\n );\n });\n };\n\n /**\n * Displays the delete confirmation to delete a section\n *\n * @param {String} message confirmation message\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmEditSection = function(message, onconfirm) {\n str.get_strings([\n {key: 'confirm'}, // TODO link text\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], message, s[1], s[2], onconfirm);\n }\n );\n };\n\n /**\n * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * @param {JQuery} actionitem\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n * @return {Promise} promise which is resolved when the replacement has completed\n */\n var replaceActionItem = function(actionitem, image, stringname,\n stringcomponent, newaction) {\n\n var stringRequests = [{key: stringname, component: stringcomponent}];\n // Do not provide an icon with duplicate, different text to the menu item.\n\n return str.get_strings(stringRequests).then(function(strings) {\n actionitem.find('span.menu-action-text').html(strings[0]);\n\n return templates.renderPix(image, 'core');\n }).then(function(pixhtml) {\n actionitem.find('.icon').replaceWith(pixhtml);\n actionitem.attr('data-action', newaction);\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Default post-processing for section AJAX edit actions.\n *\n * This can be overridden in course formats by listening to event coursesectionedited:\n *\n * $('body').on('coursesectionedited', 'li.section', function(e) {\n * var action = e.action,\n * sectionElement = $(e.target),\n * data = e.ajaxreturn;\n * // ... Do some processing here.\n * e.preventDefault(); // Prevent default handler.\n * });\n *\n * @param {JQuery} sectionElement\n * @param {JQuery} actionItem\n * @param {Object} data\n * @param {String} courseformat\n * @param {Number} sectionid\n */\n var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n var action = actionItem.attr('data-action');\n if (action === 'hide' || action === 'show') {\n if (action === 'hide') {\n sectionElement.addClass('hidden');\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', true);\n replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', false);\n sectionElement.removeClass('hidden');\n replaceActionItem(actionItem, 'i/hide',\n 'hidefromothers', 'format_' + courseformat, 'hide');\n }\n // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n if (data.modules !== undefined) {\n for (var i in data.modules) {\n replaceActivityHtmlWith(data.modules[i]);\n }\n }\n // Replace the section availability information.\n if (data.section_availability !== undefined) {\n sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n }\n // Modify course state.\n const section = courseeditor.state.section.get(sectionid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [sectionid]);\n }\n } else if (action === 'setmarker') {\n var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n oldmarker.removeClass('current');\n replaceActionItem(oldActionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n sectionElement.addClass('current');\n replaceActionItem(actionItem, 'i/marked',\n 'highlightoff', 'core', 'removemarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'highlighted', true);\n } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'highlighted', false);\n }\n };\n\n /**\n * Get the focused element path in an activity if any.\n *\n * This method is used to restore focus when the activity HTML is refreshed.\n * Only the main course editor elements can be refocused as they are always present\n * even if the activity content changes.\n *\n * @param {String} id the element id the activity element\n * @return {String|undefined} the inner path of the focused element or undefined\n */\n const getActivityFocusedElement = function(id) {\n const element = document.getElementById(id);\n if (!element || !element.contains(document.activeElement)) {\n return undefined;\n }\n // Check if the actions menu toggler is focused.\n if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n }\n // Return the current element id if any.\n if (document.activeElement.id) {\n return `#${document.activeElement.id}`;\n }\n return undefined;\n };\n\n /**\n * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n *\n * @param {String} activityHTML\n */\n var replaceActivityHtmlWith = function(activityHTML) {\n $('
' + activityHTML + '
').find(SELECTOR.ACTIVITYLI).each(function() {\n // Extract id from the new activity html.\n var id = $(this).attr('id');\n // Check if the current focused element is inside the activity.\n let focusedPath = getActivityFocusedElement(id);\n // Find the existing element with the same id and replace its contents with new html.\n $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n // Initialise action menu.\n initActionMenu(id);\n // Re-focus the previous elements.\n if (focusedPath) {\n const newItem = document.getElementById(id);\n newItem.querySelector(focusedPath)?.focus();\n }\n\n });\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {Nunmber} sectionid\n * @param {JQuery} target the element (menu item) that was clicked\n * @param {String} courseformat\n * @return {boolean} true the action call is sent to the server or false if it is ignored.\n */\n var editSection = function(sectionElement, sectionid, target, courseformat) {\n var action = target.attr('data-action'),\n sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0;\n\n // Filter direct component handled actions.\n if (courseeditor.supportComponents && componentActions.includes(action)) {\n return false;\n }\n\n var spinner = addSectionSpinner(sectionElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n }], true);\n\n var lightbox = addSectionLightbox(sectionElement);\n $.when.apply($, promises)\n .done(function(dataencoded) {\n var data = $.parseJSON(dataencoded);\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n }\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n return true;\n };\n\n /**\n * Sets the section badge in the section header.\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {String} badgetype the type of badge this is for\n * @param {bool} add true to add, false to remove\n */\n var setSectionBadge = function(sectionElement, badgetype, add) {\n const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n if (!sectionbadges) {\n return;\n }\n if (add) {\n templates.render('core_courseformat/local/content/section/badges', {[badgetype]: 1})\n .then(function(html, js) {\n templates.prependNodeContents(sectionbadges, html, js);\n return true;\n }).catch(notification.exception);\n } else {\n const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n badge.remove();\n }\n };\n\n // Register a function to be executed after D&D of an activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.register_module({\n // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n // eslint-disable-next-line camelcase\n set_visibility_resource_ui: function(args) {\n var mainelement = $(args.element.getDOMNode());\n var cmid = getModuleId(mainelement);\n if (cmid) {\n var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n refreshModule(mainelement, cmid, sectionreturn);\n }\n },\n /**\n * Update the course state when some cm is moved via YUI.\n * @param {*} params\n */\n updateMovedCmState: (params) => {\n const state = courseeditor.state;\n\n // Update old section.\n const cm = state.cm.get(params.cmid);\n if (cm !== undefined) {\n courseeditor.dispatch('sectionState', [cm.sectionid]);\n }\n // Update cm state.\n courseeditor.dispatch('cmState', [params.cmid]);\n },\n /**\n * Update the course state when some section is moved via YUI.\n */\n updateMovedSectionState: () => {\n courseeditor.dispatch('courseState');\n },\n });\n });\n\n // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n // This means all method from this \"actions\" module will be deprecated when all the course\n // interface is migrated to reactive components.\n // Most legacy actions did not provide enough information to regenarate the course so they\n // use the mutations courseState, sectionState and cmState to get the updated state from\n // the server. However, some activity actions where we can prevent an extra webservice\n // call by implementing an adhoc mutation.\n courseeditor.addMutations({\n /**\n * Compatibility function to update Moodle 4.0 course state using legacy actions.\n *\n * This method only updates some actions which does not require to use cmState mutation\n * to get updated data form the server.\n *\n * @param {Object} statemanager the current state in read write mode\n * @param {String} action the performed action\n * @param {Number} cmid the affected course module id\n * @param {Array} affectedids all affected cm ids (for duplicate action)\n */\n legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n const state = statemanager.state;\n const cm = state.cm.get(cmid);\n if (cm === undefined) {\n return;\n }\n const section = state.section.get(cm.sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked.\n courseeditor.dispatch('cmLock', [cm.id], true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This unlocked will take effect when the read only is restored.\n cm.locked = false;\n\n switch (action) {\n case 'delete':\n // Remove from section.\n section.cmlist = section.cmlist.reduce(\n (cmlist, current) => {\n if (current != cmid) {\n cmlist.push(current);\n }\n return cmlist;\n },\n []\n );\n // Delete form list.\n state.cm.delete(cmid);\n break;\n\n case 'hide':\n case 'show':\n cm.visible = (action === 'show') ? true : false;\n break;\n\n case 'duplicate':\n // Duplicate requires to get extra data from the server.\n courseeditor.dispatch('cmState', affectedids);\n break;\n }\n statemanager.setReadOnly(true);\n },\n legacySectionAction: function(statemanager, action, sectionid) {\n\n const state = statemanager.state;\n const section = state.section.get(sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked. Reactive events are only triggered when the state\n // read only mode is restored. We want to notify the interface the element is\n // locked so we need to do a quick lock operation before performing the rest\n // of the mutation.\n statemanager.setReadOnly(false);\n section.locked = true;\n statemanager.setReadOnly(true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This locked will take effect when the read only is restored.\n section.locked = false;\n\n switch (action) {\n case 'setmarker':\n // Remove previous marker.\n state.section.forEach((current) => {\n if (current.id != sectionid) {\n current.current = false;\n }\n });\n section.current = true;\n break;\n\n case 'removemarker':\n section.current = false;\n break;\n }\n statemanager.setReadOnly(true);\n },\n });\n\n return /** @alias module:core_course/actions */ {\n\n /**\n * Initialises course page\n *\n * @method init\n * @param {String} courseformat name of the current course format (for fetching strings)\n */\n initCoursePage: function(courseformat) {\n\n formatname = courseformat;\n\n // Add a handler for course module actions.\n $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n action = actionItem.attr('data-action'),\n moduleId = getModuleId(moduleElement);\n switch (action) {\n case 'moveleft':\n case 'moveright':\n case 'delete':\n case 'duplicate':\n case 'hide':\n case 'stealth':\n case 'show':\n case 'groupsseparate':\n case 'groupsvisible':\n case 'groupsnone':\n break;\n default:\n // Nothing to do here!\n return;\n }\n if (!moduleId) {\n return;\n }\n e.preventDefault();\n if (action === 'delete') {\n // Deleting requires confirmation.\n confirmDeleteModule(moduleElement, function() {\n editModule(moduleElement, moduleId, actionItem);\n });\n } else {\n editModule(moduleElement, moduleId, actionItem);\n }\n });\n\n // Add a handler for section show/hide actions.\n $('body').on('click keypress', SELECTOR.SECTIONLI + ' ' +\n SELECTOR.SECTIONACTIONMENU + '[data-sectionid] ' +\n 'a[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr('data-sectionid');\n\n let isExecuted = true;\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n }\n // Prevent any other module from capturing the action if it is already in execution.\n if (isExecuted) {\n e.preventDefault();\n }\n });\n\n // The section and activity names are edited using inplace editable.\n // The \"update\" jQuery event must be captured in order to update the course state.\n $('body').on('updated', `${SELECTOR.SECTIONLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n const state = courseeditor.state;\n const section = state.section.get(e.ajaxreturn.itemid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);\n }\n }\n });\n $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);\n }\n });\n\n // Component-based formats don't use modals to create sections.\n if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n return;\n }\n\n // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n str.get_string('numberweeks').done(function(strNumberSections) {\n var trigger = $(SELECTOR.ADDSECTIONS),\n modalTitle = trigger.attr('data-add-sections'),\n newSections = trigger.attr('data-new-sections');\n var modalBody = $('
' +\n '
');\n modalBody.find('label').html(strNumberSections);\n ModalFactory.create({\n title: modalTitle,\n type: ModalFactory.types.SAVE_CANCEL,\n body: modalBody.html()\n }, trigger)\n .done(function(modal) {\n var numSections = $(modal.getBody()).find('#add_section_numsections'),\n addSections = function() {\n // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n // to adding a section script.\n if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n }\n };\n modal.setSaveButtonText(modalTitle);\n modal.getRoot().on(ModalEvents.shown, function() {\n // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n numSections.focus().select().on('keydown', function(e) {\n if (e.keyCode === KeyCodes.enter) {\n addSections();\n }\n });\n });\n modal.getRoot().on(ModalEvents.save, function(e) {\n // When modal \"Add\" button is pressed.\n e.preventDefault();\n addSections();\n });\n });\n });\n },\n\n /**\n * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * This method can be used by course formats in their listener to the coursesectionedited event\n *\n * @deprecated since Moodle 3.9\n * @param {JQuery} sectionelement\n * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n */\n replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n stringcomponent, newaction) {\n log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n },\n // Method to refresh a module.\n refreshModule,\n refreshSection,\n };\n });\n"],"file":"actions.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/actions.js"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalFactory","ModalEvents","KeyCodes","log","editor","EventDispatcher","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","EDITINPROGRESS","SECTIONDRAGGABLE","EDITINGMOVE","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","getModuleName","name","getName","state","cmid","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","focusActionItem","elementId","action","mainelement","selector","is","focus","findNextFocusable","mainElement","tabables","isInside","foundElement","each","contains","editModule","moduleElement","target","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","elementToFocus","replaceWith","affectedids","index","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","get_string","pluginname","get_strings","key","component","param","type","s","confirm","confirmEditSection","message","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","getActivityFocusedElement","document","getElementById","activeElement","querySelector","activityHTML","focusedPath","newItem","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","sectionbadges","render","js","prependNodeContents","badge","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","legacySectionAction","forEach","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","isExecuted","itemid","strNumberSections","modalTitle","newSections","modalBody","create","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","event","sectionRefreshed","defaultPrevented"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Various actions on modules and sections in the editing mode - hiding, duplicating, deleting, etc.\n *\n * @module core_course/actions\n * @copyright 2016 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.3\n */\ndefine(\n [\n 'jquery',\n 'core/ajax',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/url',\n 'core/yui',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/key_codes',\n 'core/log',\n 'core_courseformat/courseeditor',\n 'core/event_dispatcher',\n 'core_course/events'\n ],\n function(\n $,\n ajax,\n templates,\n notification,\n str,\n url,\n Y,\n ModalFactory,\n ModalEvents,\n KeyCodes,\n log,\n editor,\n EventDispatcher,\n CourseEvents\n ) {\n\n // Eventually, core_courseformat/local/content/actions will handle all actions for\n // component compatible formats and the default actions.js won't be necessary anymore.\n // Meanwhile, we filter the migrated actions.\n const componentActions = ['moveSection', 'moveCm', 'addSection', 'deleteSection'];\n\n // The course reactive instance.\n const courseeditor = editor.getCurrentCourseEditor();\n\n // The current course format name (loaded on init).\n let formatname;\n\n var CSS = {\n EDITINPROGRESS: 'editinprogress',\n SECTIONDRAGGABLE: 'sectiondraggable',\n EDITINGMOVE: 'editing_move'\n };\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n ACTIONAREA: '.actions',\n ACTIVITYACTION: 'a.cm-edit-action',\n MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',\n TOGGLE: '.toggle-display,.dropdown-toggle',\n SECTIONLI: 'li.section',\n SECTIONACTIONMENU: '.section_action_menu',\n ADDSECTIONS: '.changenumsections [data-add-sections]',\n SECTIONBADGES: '[data-region=\"sectionbadges\"]',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n /**\n * Dispatch event wrapper.\n *\n * Old jQuery events will be replaced by native events gradually.\n *\n * @method dispatchEvent\n * @param {String} eventName The name of the event\n * @param {Object} detail Any additional details to pass into the eveent\n * @param {Node|HTMLElement} container The point at which to dispatch the event\n * @param {Object} options\n * @param {Boolean} options.bubbles Whether to bubble up the DOM\n * @param {Boolean} options.cancelable Whether preventDefault() can be called\n * @param {Boolean} options.composed Whether the event can bubble across the ShadowDOM boundary\n * @returns {CustomEvent}\n */\n const dispatchEvent = function(eventName, detail, container, options) {\n // Most actions still uses jQuery node instead of regular HTMLElement.\n if (!(container instanceof Element) && container.get !== undefined) {\n container = container.get(0);\n }\n return EventDispatcher.dispatchEvent(eventName, detail, container, options);\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n // Check if we have a data-id first.\n const item = element.get(0);\n if (item.dataset.id) {\n return item.dataset.id;\n }\n // Use YUI way if data-id is not present.\n let id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(item));\n });\n return id;\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getName\n *\n * @param {JQuery} element\n * @returns {String}\n */\n var getModuleName = function(element) {\n var name;\n Y.use('moodle-course-util', function(Y) {\n name = Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)));\n });\n // Check if we have the name in the course state.\n const state = courseeditor.state;\n const cmid = getModuleId(element);\n if (!name && state && cmid) {\n name = state.cm.get(cmid)?.name;\n }\n return name;\n };\n\n /**\n * Wrapper for M.util.add_spinner for an activity\n *\n * @param {JQuery} activity\n * @returns {Node}\n */\n var addActivitySpinner = function(activity) {\n activity.addClass(CSS.EDITINPROGRESS);\n var actionarea = activity.find(SELECTOR.ACTIONAREA).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the activity state element.\n if (activity.data('id') !== undefined) {\n courseeditor.dispatch('cmLock', [activity.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_spinner for a section\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionSpinner = function(sectionelement) {\n sectionelement.addClass(CSS.EDITINPROGRESS);\n var actionarea = sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the section state element.\n if (sectionelement.data('id') !== undefined) {\n courseeditor.dispatch('sectionLock', [sectionelement.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_lightbox\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionLightbox = function(sectionelement) {\n const item = sectionelement.get(0);\n var lightbox = M.util.add_lightbox(Y, Y.Node(item));\n if (item.dataset.for == 'section' && item.dataset.id) {\n courseeditor.dispatch('sectionLock', [item.dataset.id], true);\n lightbox.setAttribute('data-state', 'section');\n lightbox.setAttribute('data-state-id', item.dataset.id);\n }\n lightbox.show();\n return lightbox;\n };\n\n /**\n * Removes the spinner element\n *\n * @param {JQuery} element\n * @param {Node} spinner\n * @param {Number} delay\n */\n var removeSpinner = function(element, spinner, delay) {\n window.setTimeout(function() {\n element.removeClass(CSS.EDITINPROGRESS);\n if (spinner) {\n spinner.hide();\n }\n // Unlock the state element.\n if (element.data('id') !== undefined) {\n const mutation = (element.data('for') === 'section') ? 'sectionLock' : 'cmLock';\n courseeditor.dispatch(mutation, [element.data('id')], false);\n }\n }, delay);\n };\n\n /**\n * Removes the lightbox element\n *\n * @param {Node} lightbox lighbox YUI element returned by addSectionLightbox\n * @param {Number} delay\n */\n var removeLightbox = function(lightbox, delay) {\n if (lightbox) {\n window.setTimeout(function() {\n lightbox.hide();\n // Unlock state if necessary.\n if (lightbox.getAttribute('data-state')) {\n courseeditor.dispatch(\n `${lightbox.getAttribute('data-state')}Lock`,\n [lightbox.getAttribute('data-state-id')],\n false\n );\n }\n }, delay);\n }\n };\n\n /**\n * Initialise action menu for the element (section or module)\n *\n * @param {String} elementid CSS id attribute of the element\n */\n var initActionMenu = function(elementid) {\n // Initialise action menu in the new activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);\n });\n if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {\n M.core.actionmenu.newDOMNode(Y.one('#' + elementid));\n }\n };\n\n /**\n * Returns focus to the element that was clicked or \"Edit\" link if element is no longer visible.\n *\n * @param {String} elementId CSS id attribute of the element\n * @param {String} action data-action property of the element that was clicked\n */\n var focusActionItem = function(elementId, action) {\n var mainelement = $('#' + elementId);\n var selector = '[data-action=' + action + ']';\n if (action === 'groupsseparate' || action === 'groupsvisible' || action === 'groupsnone') {\n // New element will have different data-action.\n selector = '[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]';\n }\n if (mainelement.find(selector).is(':visible')) {\n mainelement.find(selector).focus();\n } else {\n // Element not visible, focus the \"Edit\" link.\n mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus();\n }\n };\n\n /**\n * Find next
after the element\n *\n * @param {JQuery} mainElement element that is about to be deleted\n * @returns {JQuery}\n */\n var findNextFocusable = function(mainElement) {\n var tabables = $(\"a:visible\");\n var isInside = false;\n var foundElement = null;\n tabables.each(function() {\n if ($.contains(mainElement[0], this)) {\n isInside = true;\n } else if (isInside) {\n foundElement = this;\n return false; // Returning false in .each() is equivalent to \"break;\" inside the loop in php.\n }\n return true;\n });\n return foundElement;\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} moduleElement activity element we perform action on\n * @param {Number} cmid\n * @param {JQuery} target the element (menu item) that was clicked\n */\n var editModule = function(moduleElement, cmid, target) {\n var action = target.attr('data-action');\n var spinner = addActivitySpinner(moduleElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_module',\n args: {id: cmid,\n action: action,\n sectionreturn: target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0\n }\n }], true);\n\n var lightbox;\n if (action === 'duplicate') {\n lightbox = addSectionLightbox(target.closest(SELECTOR.SECTIONLI));\n }\n $.when.apply($, promises)\n .done(function(data) {\n var elementToFocus = findNextFocusable(moduleElement);\n moduleElement.replaceWith(data);\n let affectedids = [];\n // Initialise action menu for activity(ies) added as a result of this.\n $('
' + data + '
').find(SELECTOR.ACTIVITYLI).each(function(index) {\n initActionMenu($(this).attr('id'));\n if (index === 0) {\n focusActionItem($(this).attr('id'), action);\n elementToFocus = null;\n }\n // Save any activity id in cmids.\n affectedids.push(getModuleId($(this)));\n });\n // In case of activity deletion focus the next focusable element.\n if (elementToFocus) {\n elementToFocus.focus();\n }\n // Remove spinner and lightbox with a delay.\n removeSpinner(moduleElement, spinner, 400);\n removeLightbox(lightbox, 400);\n // Trigger event that can be observed by course formats.\n moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n // Modify cm state.\n courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(moduleElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n moduleElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n };\n\n /**\n * Requests html for the module via WS core_course_get_module and updates the module on the course page\n *\n * Used after d&d of the module to another section\n *\n * @param {JQuery|Element} element\n * @param {Number} cmid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshModule = function(element, cmid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const activityElement = $(element);\n var spinner = addActivitySpinner(activityElement);\n var promises = ajax.call([{\n methodname: 'core_course_get_module',\n args: {id: cmid, sectionreturn: sectionreturn}\n }], true);\n\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n resolve(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\n reject();\n });\n });\n };\n\n /**\n * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n *\n * @param {JQuery|Element} element\n * @param {Number} sectionid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshSection = function(element, sectionid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const sectionElement = $(element);\n const action = 'refresh';\n const promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action, sectionreturn},\n }], true);\n\n var spinner = addSectionSpinner(sectionElement);\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(dataencoded => {\n\n removeSpinner(sectionElement, spinner);\n const data = $.parseJSON(dataencoded);\n\n const newSectionElement = $(data.content);\n sectionElement.replaceWith(newSectionElement);\n\n // Init modules menus.\n $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n (index, activity) => {\n initActionMenu(activity.data('id'));\n }\n );\n\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n CourseEvents.sectionRefreshed,\n {\n ajaxreturn: data,\n action: action,\n newSectionElement: newSectionElement.get(0),\n },\n newSectionElement\n );\n\n if (!event.defaultPrevented) {\n defaultEditSectionHandler(\n newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n data,\n formatname,\n sectionid\n );\n }\n resolve(data);\n }).fail(ex => {\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n 'coursesectionrefreshfailed',\n {exception: ex, action: action},\n sectionElement\n );\n if (!event.defaultPrevented) {\n notification.exception(ex);\n }\n reject();\n });\n });\n };\n\n /**\n * Displays the delete confirmation to delete a module\n *\n * @param {JQuery} mainelement activity element we perform action on\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmDeleteModule = function(mainelement, onconfirm) {\n var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n var modulename = getModuleName(mainelement);\n\n str.get_string('pluginname', modtypename).done(function(pluginname) {\n var plugindata = {\n type: pluginname,\n name: modulename\n };\n str.get_strings([\n {key: 'confirm', component: 'core'},\n {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n }\n );\n });\n };\n\n /**\n * Displays the delete confirmation to delete a section\n *\n * @param {String} message confirmation message\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmEditSection = function(message, onconfirm) {\n str.get_strings([\n {key: 'confirm'}, // TODO link text\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], message, s[1], s[2], onconfirm);\n }\n );\n };\n\n /**\n * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * @param {JQuery} actionitem\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n * @return {Promise} promise which is resolved when the replacement has completed\n */\n var replaceActionItem = function(actionitem, image, stringname,\n stringcomponent, newaction) {\n\n var stringRequests = [{key: stringname, component: stringcomponent}];\n // Do not provide an icon with duplicate, different text to the menu item.\n\n return str.get_strings(stringRequests).then(function(strings) {\n actionitem.find('span.menu-action-text').html(strings[0]);\n\n return templates.renderPix(image, 'core');\n }).then(function(pixhtml) {\n actionitem.find('.icon').replaceWith(pixhtml);\n actionitem.attr('data-action', newaction);\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Default post-processing for section AJAX edit actions.\n *\n * This can be overridden in course formats by listening to event coursesectionedited:\n *\n * $('body').on('coursesectionedited', 'li.section', function(e) {\n * var action = e.action,\n * sectionElement = $(e.target),\n * data = e.ajaxreturn;\n * // ... Do some processing here.\n * e.preventDefault(); // Prevent default handler.\n * });\n *\n * @param {JQuery} sectionElement\n * @param {JQuery} actionItem\n * @param {Object} data\n * @param {String} courseformat\n * @param {Number} sectionid\n */\n var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n var action = actionItem.attr('data-action');\n if (action === 'hide' || action === 'show') {\n if (action === 'hide') {\n sectionElement.addClass('hidden');\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', true);\n replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', false);\n sectionElement.removeClass('hidden');\n replaceActionItem(actionItem, 'i/hide',\n 'hidefromothers', 'format_' + courseformat, 'hide');\n }\n // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n if (data.modules !== undefined) {\n for (var i in data.modules) {\n replaceActivityHtmlWith(data.modules[i]);\n }\n }\n // Replace the section availability information.\n if (data.section_availability !== undefined) {\n sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n }\n // Modify course state.\n const section = courseeditor.state.section.get(sectionid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [sectionid]);\n }\n } else if (action === 'setmarker') {\n var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n oldmarker.removeClass('current');\n replaceActionItem(oldActionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n sectionElement.addClass('current');\n replaceActionItem(actionItem, 'i/marked',\n 'highlightoff', 'core', 'removemarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'highlighted', true);\n } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'highlighted', false);\n }\n };\n\n /**\n * Get the focused element path in an activity if any.\n *\n * This method is used to restore focus when the activity HTML is refreshed.\n * Only the main course editor elements can be refocused as they are always present\n * even if the activity content changes.\n *\n * @param {String} id the element id the activity element\n * @return {String|undefined} the inner path of the focused element or undefined\n */\n const getActivityFocusedElement = function(id) {\n const element = document.getElementById(id);\n if (!element || !element.contains(document.activeElement)) {\n return undefined;\n }\n // Check if the actions menu toggler is focused.\n if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n }\n // Return the current element id if any.\n if (document.activeElement.id) {\n return `#${document.activeElement.id}`;\n }\n return undefined;\n };\n\n /**\n * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n *\n * @param {String} activityHTML\n */\n var replaceActivityHtmlWith = function(activityHTML) {\n $('
' + activityHTML + '
').find(SELECTOR.ACTIVITYLI).each(function() {\n // Extract id from the new activity html.\n var id = $(this).attr('id');\n // Check if the current focused element is inside the activity.\n let focusedPath = getActivityFocusedElement(id);\n // Find the existing element with the same id and replace its contents with new html.\n $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n // Initialise action menu.\n initActionMenu(id);\n // Re-focus the previous elements.\n if (focusedPath) {\n const newItem = document.getElementById(id);\n newItem.querySelector(focusedPath)?.focus();\n }\n\n });\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {Nunmber} sectionid\n * @param {JQuery} target the element (menu item) that was clicked\n * @param {String} courseformat\n * @return {boolean} true the action call is sent to the server or false if it is ignored.\n */\n var editSection = function(sectionElement, sectionid, target, courseformat) {\n var action = target.attr('data-action'),\n sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0;\n\n // Filter direct component handled actions.\n if (courseeditor.supportComponents && componentActions.includes(action)) {\n return false;\n }\n\n var spinner = addSectionSpinner(sectionElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n }], true);\n\n var lightbox = addSectionLightbox(sectionElement);\n $.when.apply($, promises)\n .done(function(dataencoded) {\n var data = $.parseJSON(dataencoded);\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n }\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n return true;\n };\n\n /**\n * Sets the section badge in the section header.\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {String} badgetype the type of badge this is for\n * @param {bool} add true to add, false to remove\n */\n var setSectionBadge = function(sectionElement, badgetype, add) {\n const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n if (!sectionbadges) {\n return;\n }\n if (add) {\n templates.render('core_courseformat/local/content/section/badges', {[badgetype]: 1})\n .then(function(html, js) {\n templates.prependNodeContents(sectionbadges, html, js);\n return true;\n }).catch(notification.exception);\n } else {\n const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n badge.remove();\n }\n };\n\n // Register a function to be executed after D&D of an activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.register_module({\n // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n // eslint-disable-next-line camelcase\n set_visibility_resource_ui: function(args) {\n var mainelement = $(args.element.getDOMNode());\n var cmid = getModuleId(mainelement);\n if (cmid) {\n var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n refreshModule(mainelement, cmid, sectionreturn);\n }\n },\n /**\n * Update the course state when some cm is moved via YUI.\n * @param {*} params\n */\n updateMovedCmState: (params) => {\n const state = courseeditor.state;\n\n // Update old section.\n const cm = state.cm.get(params.cmid);\n if (cm !== undefined) {\n courseeditor.dispatch('sectionState', [cm.sectionid]);\n }\n // Update cm state.\n courseeditor.dispatch('cmState', [params.cmid]);\n },\n /**\n * Update the course state when some section is moved via YUI.\n */\n updateMovedSectionState: () => {\n courseeditor.dispatch('courseState');\n },\n });\n });\n\n // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n // This means all method from this \"actions\" module will be deprecated when all the course\n // interface is migrated to reactive components.\n // Most legacy actions did not provide enough information to regenarate the course so they\n // use the mutations courseState, sectionState and cmState to get the updated state from\n // the server. However, some activity actions where we can prevent an extra webservice\n // call by implementing an adhoc mutation.\n courseeditor.addMutations({\n /**\n * Compatibility function to update Moodle 4.0 course state using legacy actions.\n *\n * This method only updates some actions which does not require to use cmState mutation\n * to get updated data form the server.\n *\n * @param {Object} statemanager the current state in read write mode\n * @param {String} action the performed action\n * @param {Number} cmid the affected course module id\n * @param {Array} affectedids all affected cm ids (for duplicate action)\n */\n legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n const state = statemanager.state;\n const cm = state.cm.get(cmid);\n if (cm === undefined) {\n return;\n }\n const section = state.section.get(cm.sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked.\n courseeditor.dispatch('cmLock', [cm.id], true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This unlocked will take effect when the read only is restored.\n cm.locked = false;\n\n switch (action) {\n case 'delete':\n // Remove from section.\n section.cmlist = section.cmlist.reduce(\n (cmlist, current) => {\n if (current != cmid) {\n cmlist.push(current);\n }\n return cmlist;\n },\n []\n );\n // Delete form list.\n state.cm.delete(cmid);\n break;\n\n case 'hide':\n case 'show':\n case 'duplicate':\n courseeditor.dispatch('cmState', affectedids);\n break;\n }\n statemanager.setReadOnly(true);\n },\n legacySectionAction: function(statemanager, action, sectionid) {\n\n const state = statemanager.state;\n const section = state.section.get(sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked. Reactive events are only triggered when the state\n // read only mode is restored. We want to notify the interface the element is\n // locked so we need to do a quick lock operation before performing the rest\n // of the mutation.\n statemanager.setReadOnly(false);\n section.locked = true;\n statemanager.setReadOnly(true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This locked will take effect when the read only is restored.\n section.locked = false;\n\n switch (action) {\n case 'setmarker':\n // Remove previous marker.\n state.section.forEach((current) => {\n if (current.id != sectionid) {\n current.current = false;\n }\n });\n section.current = true;\n break;\n\n case 'removemarker':\n section.current = false;\n break;\n }\n statemanager.setReadOnly(true);\n },\n });\n\n return /** @alias module:core_course/actions */ {\n\n /**\n * Initialises course page\n *\n * @method init\n * @param {String} courseformat name of the current course format (for fetching strings)\n */\n initCoursePage: function(courseformat) {\n\n formatname = courseformat;\n\n // Add a handler for course module actions.\n $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n action = actionItem.attr('data-action'),\n moduleId = getModuleId(moduleElement);\n switch (action) {\n case 'moveleft':\n case 'moveright':\n case 'delete':\n case 'duplicate':\n case 'hide':\n case 'stealth':\n case 'show':\n case 'groupsseparate':\n case 'groupsvisible':\n case 'groupsnone':\n break;\n default:\n // Nothing to do here!\n return;\n }\n if (!moduleId) {\n return;\n }\n e.preventDefault();\n if (action === 'delete') {\n // Deleting requires confirmation.\n confirmDeleteModule(moduleElement, function() {\n editModule(moduleElement, moduleId, actionItem);\n });\n } else {\n editModule(moduleElement, moduleId, actionItem);\n }\n });\n\n // Add a handler for section show/hide actions.\n $('body').on('click keypress', SELECTOR.SECTIONLI + ' ' +\n SELECTOR.SECTIONACTIONMENU + '[data-sectionid] ' +\n 'a[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr('data-sectionid');\n\n let isExecuted = true;\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n }\n // Prevent any other module from capturing the action if it is already in execution.\n if (isExecuted) {\n e.preventDefault();\n }\n });\n\n // The section and activity names are edited using inplace editable.\n // The \"update\" jQuery event must be captured in order to update the course state.\n $('body').on('updated', `${SELECTOR.SECTIONLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n const state = courseeditor.state;\n const section = state.section.get(e.ajaxreturn.itemid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);\n }\n }\n });\n $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);\n }\n });\n\n // Component-based formats don't use modals to create sections.\n if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n return;\n }\n\n // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n str.get_string('numberweeks').done(function(strNumberSections) {\n var trigger = $(SELECTOR.ADDSECTIONS),\n modalTitle = trigger.attr('data-add-sections'),\n newSections = trigger.attr('data-new-sections');\n var modalBody = $('
' +\n '
');\n modalBody.find('label').html(strNumberSections);\n ModalFactory.create({\n title: modalTitle,\n type: ModalFactory.types.SAVE_CANCEL,\n body: modalBody.html()\n }, trigger)\n .done(function(modal) {\n var numSections = $(modal.getBody()).find('#add_section_numsections'),\n addSections = function() {\n // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n // to adding a section script.\n if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n }\n };\n modal.setSaveButtonText(modalTitle);\n modal.getRoot().on(ModalEvents.shown, function() {\n // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n numSections.focus().select().on('keydown', function(e) {\n if (e.keyCode === KeyCodes.enter) {\n addSections();\n }\n });\n });\n modal.getRoot().on(ModalEvents.save, function(e) {\n // When modal \"Add\" button is pressed.\n e.preventDefault();\n addSections();\n });\n });\n });\n },\n\n /**\n * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * This method can be used by course formats in their listener to the coursesectionedited event\n *\n * @deprecated since Moodle 3.9\n * @param {JQuery} sectionelement\n * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n */\n replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n stringcomponent, newaction) {\n log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n },\n // Method to refresh a module.\n refreshModule,\n refreshSection,\n };\n });\n"],"file":"actions.min.js"} \ No newline at end of file diff --git a/course/amd/src/actions.js b/course/amd/src/actions.js index 83a00151a16..0e2bc0f7504 100644 --- a/course/amd/src/actions.js +++ b/course/amd/src/actions.js @@ -846,11 +846,7 @@ define( case 'hide': case 'show': - cm.visible = (action === 'show') ? true : false; - break; - case 'duplicate': - // Duplicate requires to get extra data from the server. courseeditor.dispatch('cmState', affectedids); break; } diff --git a/course/format/amd/build/local/courseindex/cm.min.js b/course/format/amd/build/local/courseindex/cm.min.js index 3977fd99485..6609da78b73 100644 --- a/course/format/amd/build/local/courseindex/cm.min.js +++ b/course/format/amd/build/local/courseindex/cm.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseindex/cm",["exports","core_courseformat/local/courseeditor/dndcmitem","core/templates","core/prefetch","core/config"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=f(b);c=f(c);d=f(d);e=f(e);function f(a){return a&&a.__esModule?a:{default:a}}function g(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){g=function(a){return typeof a}}else{g=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return g(a)}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}function j(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function k(a,b){for(var c=0,d;c.\n\n/**\n * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem';\nimport Templates from 'core/templates';\nimport Prefetch from 'core/prefetch';\nimport Config from 'core/config';\n\n// Prefetch the completion icons template.\nconst completionTemplate = 'core_courseformat/local/courseindex/cmcompletion';\nPrefetch.prefetchTemplate(completionTemplate);\n\nexport default class Component extends DndCmItem {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // Default query selectors.\n this.selectors = {\n CM_NAME: `[data-for='cm_name']`,\n CM_COMPLETION: `[data-for='cm_completion']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n CMHIDDEN: 'dimmed',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'rectrictions',\n PAGEITEM: 'pageitem',\n };\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the course state.\n */\n stateReady(state) {\n this.configDragDrop(this.id);\n const cm = state.cm.get(this.id);\n const course = state.course;\n // Refresh completion icon.\n this._refreshCompletion({\n state,\n element: cm,\n });\n // Check if the current url is the cm url.\n if (window.location.href == cm.url || window.location.href == `${course.baseurl}#${cm.anchor}`) {\n this.reactive.dispatch('setPageItem', 'cm', this.id);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Check if this we are displaying this activity page.\n if (Config.contextid != Config.courseContextId && Config.contextInstanceId == this.id) {\n this.reactive.dispatch('setPageItem', 'cm', this.id, true);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Add anchor logic if the element is not user visible.\n if (!cm.uservisible) {\n this.addEventListener(\n this.getElement(this.selectors.CM_NAME),\n 'click',\n this._activityAnchor,\n );\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n {watch: `cm[${this.id}]:updated`, handler: this._refreshCm},\n {watch: `cm[${this.id}].completionstate:updated`, handler: this._refreshCompletion},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Update a course index cm using the state information.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCm({element}) {\n // Update classes.\n this.element.classList.toggle(this.classes.CMHIDDEN, !element.visible);\n this.getElement(this.selectors.CM_NAME).innerHTML = element.name;\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrectrictions ?? false);\n this.locked = element.locked;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element}) {\n if (!element.pageItem) {\n return;\n }\n const isPageId = (element.pageItem.type == 'cm' && element.pageItem.id == this.id);\n this.element.classList.toggle(this.classes.PAGEITEM, isPageId);\n if (isPageId && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Update the activity completion icon.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data\n * @param {Object} details.element the element data\n */\n async _refreshCompletion({state, element}) {\n // No completion icons are displayed in edit mode.\n if (this.reactive.isEditing || !element.istrackeduser) {\n return;\n }\n // Check if the completion value has changed.\n const completionElement = this.getElement(this.selectors.CM_COMPLETION);\n if (completionElement.dataset.value == element.completionstate) {\n return;\n }\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.cmCompletion(state, element);\n\n try {\n const {html, js} = await Templates.renderForPromise(completionTemplate, data);\n Templates.replaceNode(completionElement, html, js);\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * The activity anchor event.\n *\n * @param {Event} event\n */\n _activityAnchor(event) {\n const cm = this.reactive.get('cm', this.id);\n // If the user cannot access the element but the element is present in the page\n // the new url should be an anchor link.\n const element = document.getElementById(cm.anchor);\n if (element) {\n // Marc the element as page item once the event is handled.\n setTimeout(() => {\n this.reactive.dispatch('setPageItem', 'cm', cm.id);\n }, 50);\n return;\n }\n // If the element is not present in the page we need to go to the specific section.\n const course = this.reactive.get('course');\n const section = this.reactive.get('section', cm.sectionid);\n if (!section) {\n return;\n }\n const url = `${course.baseurl}§ion=${section.number}#${cm.anchor}`;\n event.preventDefault();\n window.location = url;\n }\n}\n"],"file":"cm.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseindex/cm.js"],"names":["Prefetch","prefetchTemplate","Component","name","selectors","CM_NAME","CM_COMPLETION","classes","CMHIDDEN","LOCKED","RESTRICTIONS","PAGEITEM","id","element","dataset","state","configDragDrop","cm","get","course","_refreshCompletion","window","location","href","url","baseurl","anchor","reactive","dispatch","scrollIntoView","block","Config","contextid","courseContextId","contextInstanceId","uservisible","addEventListener","getElement","_activityAnchor","watch","handler","remove","_refreshCm","_refreshPageItem","classList","toggle","visible","innerHTML","DRAGGING","dragging","locked","hascmrestrictions","pageItem","isPageId","type","isEditing","istrackeduser","completionElement","value","completionstate","exporter","getExporter","data","cmCompletion","Templates","renderForPromise","html","js","replaceNode","event","document","getElementById","setTimeout","section","sectionid","number","preventDefault","target","DndCmItem"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem';\nimport Templates from 'core/templates';\nimport Prefetch from 'core/prefetch';\nimport Config from 'core/config';\n\n// Prefetch the completion icons template.\nconst completionTemplate = 'core_courseformat/local/courseindex/cmcompletion';\nPrefetch.prefetchTemplate(completionTemplate);\n\nexport default class Component extends DndCmItem {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // Default query selectors.\n this.selectors = {\n CM_NAME: `[data-for='cm_name']`,\n CM_COMPLETION: `[data-for='cm_completion']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n CMHIDDEN: 'dimmed',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n };\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the course state.\n */\n stateReady(state) {\n this.configDragDrop(this.id);\n const cm = state.cm.get(this.id);\n const course = state.course;\n // Refresh completion icon.\n this._refreshCompletion({\n state,\n element: cm,\n });\n // Check if the current url is the cm url.\n if (window.location.href == cm.url || window.location.href == `${course.baseurl}#${cm.anchor}`) {\n this.reactive.dispatch('setPageItem', 'cm', this.id);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Check if this we are displaying this activity page.\n if (Config.contextid != Config.courseContextId && Config.contextInstanceId == this.id) {\n this.reactive.dispatch('setPageItem', 'cm', this.id, true);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Add anchor logic if the element is not user visible.\n if (!cm.uservisible) {\n this.addEventListener(\n this.getElement(this.selectors.CM_NAME),\n 'click',\n this._activityAnchor,\n );\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n {watch: `cm[${this.id}]:updated`, handler: this._refreshCm},\n {watch: `cm[${this.id}].completionstate:updated`, handler: this._refreshCompletion},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Update a course index cm using the state information.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCm({element}) {\n // Update classes.\n this.element.classList.toggle(this.classes.CMHIDDEN, !element.visible);\n this.getElement(this.selectors.CM_NAME).innerHTML = element.name;\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrestrictions ?? false);\n this.locked = element.locked;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element}) {\n if (!element.pageItem) {\n return;\n }\n const isPageId = (element.pageItem.type == 'cm' && element.pageItem.id == this.id);\n this.element.classList.toggle(this.classes.PAGEITEM, isPageId);\n if (isPageId && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Update the activity completion icon.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data\n * @param {Object} details.element the element data\n */\n async _refreshCompletion({state, element}) {\n // No completion icons are displayed in edit mode.\n if (this.reactive.isEditing || !element.istrackeduser) {\n return;\n }\n // Check if the completion value has changed.\n const completionElement = this.getElement(this.selectors.CM_COMPLETION);\n if (completionElement.dataset.value == element.completionstate) {\n return;\n }\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.cmCompletion(state, element);\n\n try {\n const {html, js} = await Templates.renderForPromise(completionTemplate, data);\n Templates.replaceNode(completionElement, html, js);\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * The activity anchor event.\n *\n * @param {Event} event\n */\n _activityAnchor(event) {\n const cm = this.reactive.get('cm', this.id);\n // If the user cannot access the element but the element is present in the page\n // the new url should be an anchor link.\n const element = document.getElementById(cm.anchor);\n if (element) {\n // Marc the element as page item once the event is handled.\n setTimeout(() => {\n this.reactive.dispatch('setPageItem', 'cm', cm.id);\n }, 50);\n return;\n }\n // If the element is not present in the page we need to go to the specific section.\n const course = this.reactive.get('course');\n const section = this.reactive.get('section', cm.sectionid);\n if (!section) {\n return;\n }\n const url = `${course.baseurl}§ion=${section.number}#${cm.anchor}`;\n event.preventDefault();\n window.location = url;\n }\n}\n"],"file":"cm.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/section.min.js b/course/format/amd/build/local/courseindex/section.min.js index a84ffe3f236..1940edeedbf 100644 --- a/course/format/amd/build/local/courseindex/section.min.js +++ b/course/format/amd/build/local/courseindex/section.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseindex/section",["exports","core_courseformat/local/courseindex/sectiontitle","core_courseformat/local/courseeditor/dndsection"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){e=function(a){return typeof a}}else{e=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return e(a)}function f(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function g(a){for(var b=1,c;b.\n\n/**\n * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/section\n * @class core_courseformat/local/courseindex/section\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport SectionTitle from 'core_courseformat/local/courseindex/sectiontitle';\nimport DndSection from 'core_courseformat/local/courseeditor/dndsection';\n\nexport default class Component extends DndSection {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_section';\n // Default query selectors.\n this.selectors = {\n SECTION_ITEM: `[data-for='section_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n CM_LAST: `[data-for=\"cm\"]:last-child`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'rectrictions',\n PAGEITEM: 'pageitem',\n };\n\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n this.isPageItem = false;\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element passing the full section as affected region.\n const titleitem = new SectionTitle({\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(titleitem);\n }\n // Check if the current url is the section url.\n const section = state.section.get(this.id);\n if (window.location.href == section.sectionurl.replace(/&/g, \"&\")) {\n this.reactive.dispatch('setPageItem', 'section', this.id);\n sectionItem.scrollIntoView();\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `section[${this.id}]:deleted`, handler: this.remove},\n {watch: `section[${this.id}]:updated`, handler: this._refreshSection},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null}\n */\n getLastCm() {\n return this.getElement(this.selectors.CM_LAST);\n }\n\n /**\n * Update a course index section using the state information.\n *\n * @param {Object} param details the update details.\n * @param {Object} param.element the section element\n */\n _refreshSection({element}) {\n // Update classes.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.SECTIONHIDDEN, !element.visible);\n this.element.classList.toggle(this.classes.SECTIONCURRENT, element.current);\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.element.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false);\n this.locked = element.locked;\n // Update title.\n this.getElement(this.selectors.SECTION_TITLE).innerHTML = element.title;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element.pageItem) {\n return;\n }\n if (element.pageItem.sectionId !== this.id && this.isPageItem) {\n this.pageItem = false;\n this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);\n return;\n }\n const section = state.section.get(this.id);\n if (section.indexcollapsed && !element.pageItem?.isStatic) {\n this.pageItem = (element.pageItem?.sectionId == this.id);\n } else {\n this.pageItem = (element.pageItem.type == 'section' && element.pageItem.id == this.id);\n }\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.PAGEITEM, this.pageItem ?? false);\n if (this.pageItem && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n}\n"],"file":"section.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseindex/section.js"],"names":["Component","name","selectors","SECTION_ITEM","SECTION_TITLE","CM_LAST","classes","SECTIONHIDDEN","SECTIONCURRENT","LOCKED","RESTRICTIONS","PAGEITEM","id","element","dataset","isPageItem","state","configState","sectionItem","getElement","reactive","isEditing","supportComponents","titleitem","SectionTitle","fullregion","configDragDrop","section","get","window","location","href","sectionurl","replace","dispatch","scrollIntoView","watch","handler","remove","_refreshSection","_refreshPageItem","classList","toggle","visible","hasrestrictions","current","DRAGGING","dragging","locked","innerHTML","title","pageItem","sectionId","indexcollapsed","isStatic","type","block","target","document","getElementById","DndSection"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/section\n * @class core_courseformat/local/courseindex/section\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport SectionTitle from 'core_courseformat/local/courseindex/sectiontitle';\nimport DndSection from 'core_courseformat/local/courseeditor/dndsection';\n\nexport default class Component extends DndSection {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_section';\n // Default query selectors.\n this.selectors = {\n SECTION_ITEM: `[data-for='section_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n CM_LAST: `[data-for=\"cm\"]:last-child`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n };\n\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n this.isPageItem = false;\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element passing the full section as affected region.\n const titleitem = new SectionTitle({\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(titleitem);\n }\n // Check if the current url is the section url.\n const section = state.section.get(this.id);\n if (window.location.href == section.sectionurl.replace(/&/g, \"&\")) {\n this.reactive.dispatch('setPageItem', 'section', this.id);\n sectionItem.scrollIntoView();\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `section[${this.id}]:deleted`, handler: this.remove},\n {watch: `section[${this.id}]:updated`, handler: this._refreshSection},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null}\n */\n getLastCm() {\n return this.getElement(this.selectors.CM_LAST);\n }\n\n /**\n * Update a course index section using the state information.\n *\n * @param {Object} param details the update details.\n * @param {Object} param.element the section element\n */\n _refreshSection({element}) {\n // Update classes.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.SECTIONHIDDEN, !element.visible);\n sectionItem.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false);\n this.element.classList.toggle(this.classes.SECTIONCURRENT, element.current);\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.locked = element.locked;\n // Update title.\n this.getElement(this.selectors.SECTION_TITLE).innerHTML = element.title;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element.pageItem) {\n return;\n }\n if (element.pageItem.sectionId !== this.id && this.isPageItem) {\n this.pageItem = false;\n this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);\n return;\n }\n const section = state.section.get(this.id);\n if (section.indexcollapsed && !element.pageItem?.isStatic) {\n this.pageItem = (element.pageItem?.sectionId == this.id);\n } else {\n this.pageItem = (element.pageItem.type == 'section' && element.pageItem.id == this.id);\n }\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.PAGEITEM, this.pageItem ?? false);\n if (this.pageItem && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n}\n"],"file":"section.min.js"} \ No newline at end of file diff --git a/course/format/amd/src/local/courseindex/cm.js b/course/format/amd/src/local/courseindex/cm.js index ee39aa66ae2..eb7906549fd 100644 --- a/course/format/amd/src/local/courseindex/cm.js +++ b/course/format/amd/src/local/courseindex/cm.js @@ -50,7 +50,7 @@ export default class Component extends DndCmItem { this.classes = { CMHIDDEN: 'dimmed', LOCKED: 'editinprogress', - RESTRICTIONS: 'rectrictions', + RESTRICTIONS: 'restrictions', PAGEITEM: 'pageitem', }; // We need our id to watch specific events. @@ -131,7 +131,7 @@ export default class Component extends DndCmItem { this.getElement(this.selectors.CM_NAME).innerHTML = element.name; this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false); this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false); - this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrectrictions ?? false); + this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrestrictions ?? false); this.locked = element.locked; } diff --git a/course/format/amd/src/local/courseindex/section.js b/course/format/amd/src/local/courseindex/section.js index 6dca16386fb..4864e3b84b4 100644 --- a/course/format/amd/src/local/courseindex/section.js +++ b/course/format/amd/src/local/courseindex/section.js @@ -46,7 +46,7 @@ export default class Component extends DndSection { SECTIONHIDDEN: 'dimmed', SECTIONCURRENT: 'current', LOCKED: 'editinprogress', - RESTRICTIONS: 'rectrictions', + RESTRICTIONS: 'restrictions', PAGEITEM: 'pageitem', }; @@ -127,10 +127,10 @@ export default class Component extends DndSection { // Update classes. const sectionItem = this.getElement(this.selectors.SECTION_ITEM); sectionItem.classList.toggle(this.classes.SECTIONHIDDEN, !element.visible); + sectionItem.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false); this.element.classList.toggle(this.classes.SECTIONCURRENT, element.current); this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false); this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false); - this.element.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false); this.locked = element.locked; // Update title. this.getElement(this.selectors.SECTION_TITLE).innerHTML = element.title; diff --git a/course/format/classes/output/local/state/cm.php b/course/format/classes/output/local/state/cm.php index c41e4ba1ab8..eaa6897e3d2 100644 --- a/course/format/classes/output/local/state/cm.php +++ b/course/format/classes/output/local/state/cm.php @@ -86,26 +86,13 @@ class cm implements renderable { 'sectionid' => $section->id, 'sectionnumber' => $section->section, 'uservisible' => $cm->uservisible, + 'hascmrestrictions' => $this->get_has_restrictions(), ]; // Check the user access type to this cm. $info = new info_module($cm); $data->accessvisible = ($data->visible && $info->is_available_for_all()); - // Check if restriction access are visible to the user. - $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $cm->context); - if (!empty($CFG->enableavailability) && $canviewhidden) { - if (has_capability('moodle/course:manageactivities', $cm->context)) { - // Course editors can see all restrictions. - $data->hascmrectrictions = !empty($cm->availability); - } else { - // Some users can see restrictions even if it does not apply to them. - $data->hascmrectrictions = !empty($cm->availableinfo); - } - } else { - $data->hascmrectrictions = !$data->accessvisible || !$cm->uservisible; - } - // Add url if the activity is compatible. $url = $cm->url; if ($url) { @@ -126,4 +113,33 @@ class cm implements renderable { return $data; } + + /** + * Return if the activity has a restrictions icon displayed or not. + * + * @return bool if the activity has visible restrictions for the user. + */ + protected function get_has_restrictions(): bool { + global $CFG; + $cm = $this->cm; + + if (empty($cm->visible) || empty($CFG->enableavailability)) { + return false; + } + // Nothing to be displayed to the user. + if (!$cm->is_visible_on_course_page()) { + return false; + } + // Not allowed to see the module but might be allowed to see some availability. + if (!$cm->uservisible) { + return !empty($cm->availableinfo); + } + // Content editors can see all restrictions if the activity is visible. + if (has_capability('moodle/course:viewhiddenactivities', $cm->context)) { + $ci = new info_module($cm); + return !empty($ci->get_full_information()); + } + // Regular users can only see restrictions if apply to them. + return false; + } } diff --git a/course/format/classes/output/local/state/section.php b/course/format/classes/output/local/state/section.php index f2294a26637..c73c9769c9a 100644 --- a/course/format/classes/output/local/state/section.php +++ b/course/format/classes/output/local/state/section.php @@ -117,21 +117,19 @@ class section implements renderable { $context = context_course::instance($course->id); // Hidden sections have no restriction indicator displayed. - if (!$section->visible || empty($CFG->enableavailability)) { + if (empty($section->visible) || empty($CFG->enableavailability)) { return false; } - if (!has_capability('moodle/course:viewhiddenactivities', $context)) { - // If the section is visible but not user visible means it has some locking rule. - return empty($section->uservisible); - } - - if (has_capability('moodle/course:manageactivities', $context)) { - // Course editors can see all restrictions if the section is visible. - $ci = new info_section($section); - return !empty($ci->get_full_information()); - } else { - // Some users can see restrictions even if it does not apply to them. + // The activity is not visible to the user but it may have some availability information. + if (!$section->uservisible) { return !empty($section->availableinfo); } + // Course editors can see all restrictions if the section is visible. + if (has_capability('moodle/course:viewhiddensections', $context)) { + $ci = new info_section($section); + return !empty($ci->get_full_information()); + } + // Regular users can only see restrictions if apply to them. + return false; } } diff --git a/course/format/templates/local/courseindex/cm.mustache b/course/format/templates/local/courseindex/cm.mustache index b7b3b828421..b935868e025 100644 --- a/course/format/templates/local/courseindex/cm.mustache +++ b/course/format/templates/local/courseindex/cm.mustache @@ -28,12 +28,12 @@ "isactive": 1, "uniqid": "0", "accessvisible": 1, - "hascmrectrictions": 0 + "hascmrestrictions": 0 } }}
  • setup_hasrestrictions_scenario($format, $addcanviewhidden, $rolename, $hasavailability, $available); + $data = $this->setup_hasrestrictions_scenario($format, $rolename, $hasavailability, $available); // Get the cm state. $courseformat = $data->courseformat; @@ -78,14 +76,13 @@ class cm_test extends \advanced_testcase { ); $state = $cmstate->export_for_template($renderer); - $this->assertEquals($expected, $state->hascmrectrictions); + $this->assertEquals($expected, $state->hascmrestrictions); } /** * Setup section or cm has restrictions scenario. * * @param string $format the course format - * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. * @param string $rolename the user role name (editingteacher or student) * @param bool $hasavailability if the activity|section has availability * @param bool $available if the activity availability condition is available or not to the user @@ -93,7 +90,6 @@ class cm_test extends \advanced_testcase { */ private function setup_hasrestrictions_scenario( string $format = 'topics', - bool $addcanviewhidden = false, string $rolename = 'editingteacher', bool $hasavailability = false, bool $available = false @@ -101,6 +97,8 @@ class cm_test extends \advanced_testcase { global $PAGE, $DB; $this->resetAfterTest(); + set_config('enableavailability', 1); + $course = $this->getDataGenerator()->create_course(['numsections' => 1, 'format' => $format]); // Create and enrol user. @@ -112,13 +110,6 @@ class cm_test extends \advanced_testcase { ); $this->setUser($user); - // Add capabilities if necessary. - if ($addcanviewhidden) { - $roleid = $DB->get_field('role', 'id', array('shortname' => $rolename), MUST_EXIST); - $coursecontext = context_course::instance($course->id); - assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleid, $coursecontext->id); - } - // Create an activity. $activity = $this->getDataGenerator()->create_module('page', ['course' => $course->id], [ 'section' => 1, @@ -165,225 +156,198 @@ class cm_test extends \advanced_testcase { public function hasrestrictions_state_provider(): array { return [ // Teacher scenarios (topics). - 'Topics, can edit, has availability and is available' => [ + 'Teacher, Topics, can edit, has availability and is available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Topics, can edit, has availability and is not available' => [ + 'Teacher, Topics, can edit, has availability and is not available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Topics, can edit and has not availability' => [ + 'Teacher, Topics, can edit and has not availability' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Teacher scenarios (weeks). - 'Weeks, can edit, has availability and is available' => [ + 'Teacher, Weeks, can edit, has availability and is available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Weeks, can edit, has availability and is not available' => [ + 'Teacher, Weeks, can edit, has availability and is not available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Weeks, can edit and has not availability' => [ + 'Teacher, Weeks, can edit and has not availability' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Teacher scenarios (mock format). - 'Mock format, can edit, has availability and is available' => [ + 'Teacher, Mock format, can edit, has availability and is available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Mock format, can edit, has availability and is not available' => [ + 'Teacher, Mock format, can edit, has availability and is not available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Mock format, can edit and has not availability' => [ + 'Teacher, Mock format, can edit and has not availability' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], + // Non editing teacher scenarios (topics). + 'Non editing teacher, Topics, can edit, has availability and is available' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Non editing teacher, Topics, can edit, has availability and is not available' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Non editing teacher, Topics, can edit and has not availability' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Non editing teacher scenarios (weeks). + 'Non editing teacher, Weeks, can edit, has availability and is available' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Non editing teacher, Weeks, can edit, has availability and is not available' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Non editing teacher, Weeks, can edit and has not availability' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Non editing teacher scenarios (mock format). + 'Non editing teacher, Mock format, can edit, has availability and is available' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => true, + ], + 'Non editing teacher, Mock format, can edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => true, + ], + 'Non editing teacher, Mock format, can edit and has not availability' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], // Student scenarios (topics). - 'Topics, cannot edit, has availability and is available' => [ + 'Student, Topics, cannot edit, has availability and is available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, 'expected' => false, ], - 'Topics, cannot edit, has availability and is not available' => [ + 'Student, Topics, cannot edit, has availability and is not available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Topics, cannot edit and has not availability' => [ + 'Student, Topics, cannot edit and has not availability' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Student scenarios (weeks). - 'Weeks, cannot edit, has availability and is available' => [ + 'Student, Weeks, cannot edit, has availability and is available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, 'expected' => false, ], - 'Weeks, cannot edit, has availability and is not available' => [ + 'Student, Weeks, cannot edit, has availability and is not available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Weeks, cannot edit and has not availability' => [ + 'Student, Weeks, cannot edit and has not availability' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Student scenarios (mock format). - 'Mock format, cannot edit, has availability and is available' => [ + 'Student, Mock format, cannot edit, has availability and is available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, 'expected' => false, ], - 'Mock format, cannot edit, has availability and is not available' => [ + 'Student, Mock format, cannot edit, has availability and is not available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Mock format, cannot edit and has not availability' => [ + 'Student, Mock format, cannot edit and has not availability' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (topics). - 'Topics, can view hidden but not edit, has availability and is available' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Topics, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Topics, can view hidden but not edit and has not availability' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (weeks). - 'Weeks, can view hidden but not edit, has availability and is available' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Weeks, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Weeks, can view hidden but not edit and has not availability' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (mock format). - 'Mock format, can view hidden but not edit, has availability and is available' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Mock format, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Mock format, can view hidden but not edit and has not availability' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, diff --git a/course/format/tests/output/local/state/section_test.php b/course/format/tests/output/local/state/section_test.php index c7a5fcdf0ad..ef12f894a75 100644 --- a/course/format/tests/output/local/state/section_test.php +++ b/course/format/tests/output/local/state/section_test.php @@ -49,7 +49,6 @@ class section_test extends \advanced_testcase { * @covers ::export_for_template * * @param string $format the course format - * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. * @param string $rolename the user role name (editingteacher or student) * @param bool $hasavailability if the activity|section has availability * @param bool $available if the activity availability condition is available or not to the user @@ -57,13 +56,12 @@ class section_test extends \advanced_testcase { */ public function test_section_hasrestrictions_state( string $format = 'topics', - bool $addcanviewhidden = false, string $rolename = 'editingteacher', bool $hasavailability = false, bool $available = false, bool $expected = false ) { - $data = $this->setup_hasrestrictions_scenario($format, $addcanviewhidden, $rolename, $hasavailability, $available); + $data = $this->setup_hasrestrictions_scenario($format, $rolename, $hasavailability, $available); // Get the cm state. $courseformat = $data->courseformat; @@ -84,7 +82,6 @@ class section_test extends \advanced_testcase { * Setup section or cm has restrictions scenario. * * @param string $format the course format - * @param bool $addcanviewhidden if the canviewhidden capabilities must be added to the role. * @param string $rolename the user role name (editingteacher or student) * @param bool $hasavailability if the section has availability * @param bool $available if the section availability condition is available or not to the user @@ -92,7 +89,6 @@ class section_test extends \advanced_testcase { */ private function setup_hasrestrictions_scenario( string $format = 'topics', - bool $addcanviewhidden = false, string $rolename = 'editingteacher', bool $hasavailability = false, bool $available = false @@ -111,13 +107,6 @@ class section_test extends \advanced_testcase { ); $this->setUser($user); - // Add capabilities if necessary. - if ($addcanviewhidden) { - $roleid = $DB->get_field('role', 'id', array('shortname' => $rolename), MUST_EXIST); - $coursecontext = context_course::instance($course->id); - assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleid, $coursecontext->id); - } - // Set up the availability settings. if ($hasavailability) { $operation = ($available) ? condition::DIRECTION_UNTIL : condition::DIRECTION_FROM; @@ -159,84 +148,140 @@ class section_test extends \advanced_testcase { public function hasrestrictions_state_provider(): array { return [ // Teacher scenarios (topics). - 'Topics, can edit, has availability and is available' => [ + 'Teacher, Topics, can edit, has availability and is available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Topics, can edit, has availability and is not available' => [ + 'Teacher, Topics, can edit, has availability and is not available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Topics, can edit and has not availability' => [ + 'Teacher, Topics, can edit and has not availability' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Teacher scenarios (weeks). - 'Weeks, can edit, has availability and is available' => [ + 'Teacher, Weeks, can edit, has availability and is available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Weeks, can edit, has availability and is not available' => [ + 'Teacher, Weeks, can edit, has availability and is not available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Weeks, can edit and has not availability' => [ + 'Teacher, Weeks, can edit and has not availability' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], // Teacher scenarios (mock format). - 'Mock format, can edit, has availability and is available' => [ + 'Teacher, Mock format, can edit, has availability and is available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => true, 'expected' => true, ], - 'Mock format, can edit, has availability and is not available' => [ + 'Teacher, Mock format, can edit, has availability and is not available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => true, 'available' => false, 'expected' => true, ], - 'Mock format, can edit and has not availability' => [ + 'Teacher, Mock format, can edit and has not availability' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'editingteacher', 'hasavailability' => false, 'available' => true, 'expected' => false, ], + // Non editing teacher scenarios (topics). + 'Non editing teacher, Topics, can edit, has availability and is available' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Non editing teacher, Topics, can edit, has availability and is not available' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => false, + ], + 'Non editing teacher, Topics, can edit and has not availability' => [ + 'format' => 'topics', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Non editing teacher scenarios (weeks). + 'Non editing teacher, Weeks, can edit, has availability and is available' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Non editing teacher, Weeks, can edit, has availability and is not available' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => false, + ], + 'Non editing teacher, Weeks, can edit and has not availability' => [ + 'format' => 'weeks', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], + // Non editing teacher scenarios (mock format). + 'Non editing teacher, Mock format, can edit, has availability and is available' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => true, + 'expected' => false, + ], + 'Non editing teacher, Mock format, can edit, has availability and is not available' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => true, + 'available' => false, + 'expected' => false, + ], + 'Non editing teacher, Mock format, can edit and has not availability' => [ + 'format' => 'theunittest', + 'rolename' => 'teacher', + 'hasavailability' => false, + 'available' => true, + 'expected' => false, + ], // Student scenarios (topics). 'Topics, cannot edit, has availability and is available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, @@ -244,7 +289,6 @@ class section_test extends \advanced_testcase { ], 'Topics, cannot edit, has availability and is not available' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, @@ -252,7 +296,6 @@ class section_test extends \advanced_testcase { ], 'Topics, cannot edit and has not availability' => [ 'format' => 'topics', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, @@ -261,7 +304,6 @@ class section_test extends \advanced_testcase { // Student scenarios (weeks). 'Weeks, cannot edit, has availability and is available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, @@ -269,7 +311,6 @@ class section_test extends \advanced_testcase { ], 'Weeks, cannot edit, has availability and is not available' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, @@ -277,7 +318,6 @@ class section_test extends \advanced_testcase { ], 'Weeks, cannot edit and has not availability' => [ 'format' => 'weeks', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, @@ -286,7 +326,6 @@ class section_test extends \advanced_testcase { // Student scenarios (mock format). 'Mock format, cannot edit, has availability and is available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => true, @@ -294,7 +333,6 @@ class section_test extends \advanced_testcase { ], 'Mock format, cannot edit, has availability and is not available' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, 'rolename' => 'student', 'hasavailability' => true, 'available' => false, @@ -302,82 +340,6 @@ class section_test extends \advanced_testcase { ], 'Mock format, cannot edit and has not availability' => [ 'format' => 'theunittest', - 'addcanviewhidden' => false, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (topics). - 'Topics, can view hidden but not edit, has availability and is available' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Topics, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Topics, can view hidden but not edit and has not availability' => [ - 'format' => 'topics', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (weeks). - 'Weeks, can view hidden but not edit, has availability and is available' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Weeks, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Weeks, can view hidden but not edit and has not availability' => [ - 'format' => 'weeks', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => false, - 'available' => true, - 'expected' => false, - ], - // Students with view hidden activities capabilities (mock format). - 'Mock format, can view hidden but not edit, has availability and is available' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => true, - 'expected' => false, - ], - 'Mock format, can view hidden but not edit, has availability and is not available' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, - 'rolename' => 'student', - 'hasavailability' => true, - 'available' => false, - 'expected' => true, - ], - 'Mock format, can view hidden but not edit and has not availability' => [ - 'format' => 'theunittest', - 'addcanviewhidden' => true, 'rolename' => 'student', 'hasavailability' => false, 'available' => true, diff --git a/theme/boost/scss/moodle/courseindex.scss b/theme/boost/scss/moodle/courseindex.scss index 35d587c533a..8a7715a2054 100644 --- a/theme/boost/scss/moodle/courseindex.scss +++ b/theme/boost/scss/moodle/courseindex.scss @@ -89,7 +89,7 @@ $courseindex-item-current: $primary !default; .courseindex-locked { display: none; } - &.rectrictions { + &.restrictions { .courseindex-locked { display: block; } diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 75e798e952f..d76da133bb7 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -21376,7 +21376,7 @@ div.editor_atto_toolbar button .icon { color: #6a737b; } .courseindex .courseindex-item .courseindex-locked { display: none; } - .courseindex .courseindex-item.rectrictions .courseindex-locked { + .courseindex .courseindex-item.restrictions .courseindex-locked { display: block; } .courseindex .courseindex-item.pageitem { background-color: #0f6cbf; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index e162bbc2b86..97b73c15b7f 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -21322,7 +21322,7 @@ div.editor_atto_toolbar button .icon { color: #6a737b; } .courseindex .courseindex-item .courseindex-locked { display: none; } - .courseindex .courseindex-item.rectrictions .courseindex-locked { + .courseindex .courseindex-item.restrictions .courseindex-locked { display: block; } .courseindex .courseindex-item.pageitem { background-color: #0f6cbf;