From 6a36b8526ee42c19bfeffe5227325520cb69f2c2 Mon Sep 17 00:00:00 2001 From: Jakob Date: Thu, 12 Jan 2017 17:16:25 -0800 Subject: [PATCH] MDL-37765 course: Add capability to ignore availability restrictions. Allow a role to view activities without addressing the restriction rules. --- availability/classes/info_module.php | 2 +- availability/classes/info_section.php | 2 +- availability/tests/fixtures/mock_info.php | 2 +- availability/tests/info_test.php | 4 +- course/tests/courseformat_test.php | 53 ++++++++++++++++++++--- lang/en/role.php | 1 + lib/db/access.php | 12 +++++ lib/modinfolib.php | 10 +++-- 8 files changed, 73 insertions(+), 13 deletions(-) diff --git a/availability/classes/info_module.php b/availability/classes/info_module.php index 5b1c8d4f358..f20f536f0c1 100644 --- a/availability/classes/info_module.php +++ b/availability/classes/info_module.php @@ -110,7 +110,7 @@ class info_module extends info { } protected function get_view_hidden_capability() { - return 'moodle/course:viewhiddenactivities'; + return 'moodle/course:ignoreavailabilityrestrictions'; } public function get_user_list_sql($onlyactive = true) { diff --git a/availability/classes/info_section.php b/availability/classes/info_section.php index 767891b71f4..d65729ae38d 100644 --- a/availability/classes/info_section.php +++ b/availability/classes/info_section.php @@ -57,7 +57,7 @@ class info_section extends info { } protected function get_view_hidden_capability() { - return 'moodle/course:viewhiddensections'; + return 'moodle/course:ignoreavailabilityrestrictions'; } protected function set_in_database($availability) { diff --git a/availability/tests/fixtures/mock_info.php b/availability/tests/fixtures/mock_info.php index d7ef76e39d1..d89355fe4f1 100644 --- a/availability/tests/fixtures/mock_info.php +++ b/availability/tests/fixtures/mock_info.php @@ -61,7 +61,7 @@ class mock_info extends info { } protected function get_view_hidden_capability() { - return 'moodle/course:viewhiddensections'; + return 'moodle/course:ignoreavailabilityrestrictions'; } protected function set_in_database($availability) { diff --git a/availability/tests/info_test.php b/availability/tests/info_test.php index d349b9c2dc0..e8044df1380 100644 --- a/availability/tests/info_test.php +++ b/availability/tests/info_test.php @@ -492,7 +492,7 @@ class info_testcase extends advanced_testcase { // If the students have viewhiddenactivities, they get past the module // restriction. role_change_permission($studentroleid, context_module::instance($page2->cmid), - 'moodle/course:viewhiddenactivities', CAP_ALLOW); + 'moodle/course:ignoreavailabilityrestrictions', CAP_ALLOW); $expected = array($u1->id, $u2->id); $this->assertEquals($expected, array_keys($info->filter_user_list($allusers))); list ($sql, $params) = $info->get_user_list_sql(true); @@ -503,7 +503,7 @@ class info_testcase extends advanced_testcase { // If they have viewhiddensections, they also get past the section // restriction. role_change_permission($studentroleid, context_course::instance($course->id), - 'moodle/course:viewhiddensections', CAP_ALLOW); + 'moodle/course:ignoreavailabilityrestrictions', CAP_ALLOW); $expected = array($u1->id, $u2->id, $u3->id); $this->assertEquals($expected, array_keys($info->filter_user_list($allusers))); list ($sql, $params) = $info->get_user_list_sql(true); diff --git a/course/tests/courseformat_test.php b/course/tests/courseformat_test.php index 47d7dabdef8..5f37025ff83 100644 --- a/course/tests/courseformat_test.php +++ b/course/tests/courseformat_test.php @@ -40,13 +40,32 @@ class core_course_courseformat_testcase extends advanced_testcase { course_create_sections_if_missing($course1, array(0, 1)); $assign0 = $generator->create_module('assign', array('course' => $course1, 'section' => 0)); $assign1 = $generator->create_module('assign', array('course' => $course1, 'section' => 1)); + $assign2 = $generator->create_module('assign', array('course' => $course1, 'section' => 0, 'visible' => 0)); - // Enrol student and teacher. - $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); + // Create a courseoverview role based on the student role. + $roleattr = array('name' => 'courseoverview', 'shortname' => 'courseoverview', 'archetype' => 'student'); + $generator->create_role($roleattr); + + // Create user student, editingteacher, teacher and courseoverview. $student = $generator->create_user(); - $generator->enrol_user($student->id, $course1->id, $roleids['student']); $teacher = $generator->create_user(); - $generator->enrol_user($teacher->id, $course1->id, $roleids['editingteacher']); + $editingteacher = $generator->create_user(); + $courseoverviewuser = $generator->create_user(); + + // Enrol users into their roles. + $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); + $generator->enrol_user($student->id, $course1->id, $roleids['student']); + $generator->enrol_user($teacher->id, $course1->id, $roleids['teacher']); + $generator->enrol_user($editingteacher->id, $course1->id, $roleids['editingteacher']); + $generator->enrol_user($courseoverviewuser->id, $course1->id, $roleids['courseoverview']); + + // Remove the ignoreavailabilityrestrictions from the teacher role. + role_change_permission($roleids['teacher'], context_system::instance(0), + 'moodle/course:ignoreavailabilityrestrictions', CAP_PREVENT); + + // Allow the courseoverview role to ingore available restriction. + role_change_permission($roleids['courseoverview'], context_system::instance(0), + 'moodle/course:ignoreavailabilityrestrictions', CAP_ALLOW); // Make sure that initially both sections and both modules are available and visible for a student. $modinfostudent = get_fast_modinfo($course1, $student->id); @@ -55,6 +74,7 @@ class core_course_courseformat_testcase extends advanced_testcase { $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->uservisible); $this->assertTrue($modinfostudent->get_cm($assign1->cmid)->available); $this->assertTrue($modinfostudent->get_cm($assign1->cmid)->uservisible); + $this->assertFalse($modinfostudent->get_cm($assign2->cmid)->uservisible); // Set 'hideoddsections' for the course to 1. // Section1 and assign1 will be unavailable, uservisible will be false for student and true for teacher. @@ -68,15 +88,38 @@ class core_course_courseformat_testcase extends advanced_testcase { $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->uservisible); $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->available); $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->uservisible); + $this->assertFalse($modinfostudent->get_cm($assign2->cmid)->uservisible); $modinfoteacher = get_fast_modinfo($course1, $teacher->id); $this->assertFalse($modinfoteacher->get_section_info(1)->available); $this->assertEmpty($modinfoteacher->get_section_info(1)->availableinfo); + $this->assertFalse($modinfoteacher->get_section_info(1)->uservisible); + $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->available); + $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->uservisible); + $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available); + $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->uservisible); + $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->available); + $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->uservisible); + + $modinfoteacher = get_fast_modinfo($course1, $editingteacher->id); + $this->assertFalse($modinfoteacher->get_section_info(1)->available); + $this->assertEmpty($modinfoteacher->get_section_info(1)->availableinfo); $this->assertTrue($modinfoteacher->get_section_info(1)->uservisible); $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->available); $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->uservisible); $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available); $this->assertTrue($modinfoteacher->get_cm($assign1->cmid)->uservisible); + $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->uservisible); + + $modinfocourseoverview = get_fast_modinfo($course1, $courseoverviewuser->id); + $this->assertFalse($modinfocourseoverview->get_section_info(1)->available); + $this->assertEmpty($modinfocourseoverview->get_section_info(1)->availableinfo); + $this->assertTrue($modinfocourseoverview->get_section_info(1)->uservisible); + $this->assertTrue($modinfocourseoverview->get_cm($assign0->cmid)->available); + $this->assertTrue($modinfocourseoverview->get_cm($assign0->cmid)->uservisible); + $this->assertFalse($modinfocourseoverview->get_cm($assign1->cmid)->available); + $this->assertTrue($modinfocourseoverview->get_cm($assign1->cmid)->uservisible); + $this->assertFalse($modinfocourseoverview->get_cm($assign2->cmid)->uservisible); // Set 'hideoddsections' for the course to 2. // Section1 and assign1 will be unavailable, uservisible will be false for student and true for teacher. @@ -92,7 +135,7 @@ class core_course_courseformat_testcase extends advanced_testcase { $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->available); $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->uservisible); - $modinfoteacher = get_fast_modinfo($course1, $teacher->id); + $modinfoteacher = get_fast_modinfo($course1, $editingteacher->id); $this->assertFalse($modinfoteacher->get_section_info(1)->available); $this->assertNotEmpty($modinfoteacher->get_section_info(1)->availableinfo); $this->assertTrue($modinfoteacher->get_section_info(1)->uservisible); diff --git a/lang/en/role.php b/lang/en/role.php index 2af8989dd87..76470f90408 100644 --- a/lang/en/role.php +++ b/lang/en/role.php @@ -160,6 +160,7 @@ $string['course:changeshortname'] = 'Change course short name'; $string['course:changesummary'] = 'Change course summary'; $string['course:enrolconfig'] = 'Configure enrol instances in courses'; $string['course:enrolreview'] = 'Review course enrolments'; +$string['course:ignoreavailabilityrestrictions'] = 'Ignore availability restrictions'; $string['course:ignorefilesizelimits'] = 'Use files larger than any file size restrictions'; $string['course:isincompletionreports'] = 'Be shown on completion reports'; $string['course:manageactivities'] = 'Manage activities'; diff --git a/lib/db/access.php b/lib/db/access.php index 8154cf28353..f8efdb9b819 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -921,6 +921,18 @@ $capabilities = array( ) ), + 'moodle/course:ignoreavailabilityrestrictions' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'manager' => CAP_ALLOW, + 'coursecreator' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + ), + 'clonepermissionsfrom' => 'moodle/course:viewhiddenactivities' + ), + 'moodle/course:ignorefilesizelimits' => array( 'captype' => 'write', diff --git a/lib/modinfolib.php b/lib/modinfolib.php index f8a06616dc2..f7179b4f3a1 100644 --- a/lib/modinfolib.php +++ b/lib/modinfolib.php @@ -1930,8 +1930,9 @@ class cm_info implements IteratorAggregate { // If the user cannot access the activity set the uservisible flag to false. // Additional checks are required to determine whether the activity is entirely hidden or just greyed out. - if ((!$this->visible or !$this->get_available()) and - !has_capability('moodle/course:viewhiddenactivities', $this->get_context(), $userid)) { + if ((!$this->visible && !has_capability('moodle/course:viewhiddenactivities', $this->get_context(), $userid)) || + (!$this->get_available() && + !has_capability('moodle/course:ignoreavailabilityrestrictions', $this->get_context(), $userid))) { $this->uservisible = false; } @@ -2760,7 +2761,10 @@ class section_info implements IteratorAggregate { $this->_uservisible = true; if (!$this->_visible || !$this->get_available()) { $coursecontext = context_course::instance($this->get_course()); - if (!has_capability('moodle/course:viewhiddensections', $coursecontext, $userid)) { + if (!$this->_visible && !has_capability('moodle/course:viewhiddensections', $coursecontext, $userid) || + (!$this->get_available() && + !has_capability('moodle/course:ignoreavailabilityrestrictions', $coursecontext, $userid))) { + $this->_uservisible = false; } }