Merge branch 'MDL-73683-master' of https://github.com/ferranrecio/moodle

This commit is contained in:
Jake Dallimore 2022-02-24 14:26:53 +08:00
commit f167418fc9
25 changed files with 821 additions and 71 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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;

View File

@ -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.
*

View File

@ -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);

View File

@ -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;
}

View File

@ -86,21 +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) {
// 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) {
@ -121,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;
}
}

View File

@ -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;
}
}

View File

@ -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,31 +86,50 @@ 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;
}
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;
}
}
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 (empty($section->visible) || empty($CFG->enableavailability)) {
return false;
}
// 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;
}
}

View File

@ -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;
}

View File

@ -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));

View File

@ -28,12 +28,12 @@
"isactive": 1,
"uniqid": "0",
"accessvisible": 1,
"hascmrectrictions": 0
"hascmrestrictions": 0
}
}}
<li class="courseindex-item
{{#isactive}}active{{/isactive}}
{{#hascmrectrictions}}rectrictions{{/hascmrectrictions}}
{{#hascmrestrictions}}restrictions{{/hascmrestrictions}}
{{^accessvisible}}dimmed{{/accessvisible}}
{{#url}} d-flex {{/url}} {{^url}} d-flex-noedit {{/url}}"
id="{{uniqid}}-course-index-cm-{{id}}"

View File

@ -29,7 +29,7 @@
"indexcollapsed": 0,
"current": 1,
"visible": 1,
"hasrectrictions": 0,
"hasrestrictions": 0,
"cms": [
{
"id": 10,
@ -66,7 +66,7 @@
>
<div class="courseindex-item d-flex
{{^visible}}dimmed{{/visible}}
{{#hasrestrictions}}rectrictions{{/hasrestrictions}}
{{#hasrestrictions}}restrictions{{/hasrestrictions}}
courseindex-section-title"
id="courseindexsection{{number}}"
data-for="section_item"

View File

@ -0,0 +1,358 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 <ferran@moodle.com>
* @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 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',
string $rolename = 'editingteacher',
bool $hasavailability = false,
bool $available = false,
bool $expected = false
) {
$data = $this->setup_hasrestrictions_scenario($format, $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->hascmrestrictions);
}
/**
* Setup section or cm has restrictions scenario.
*
* @param string $format the course format
* @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',
string $rolename = 'editingteacher',
bool $hasavailability = false,
bool $available = false
): stdClass {
global $PAGE, $DB;
$this->resetAfterTest();
set_config('enableavailability', 1);
$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);
// 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).
'Teacher, Topics, can edit, has availability and is available' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Topics, can edit, has availability and is not available' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Topics, can edit and has not availability' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Teacher scenarios (weeks).
'Teacher, Weeks, can edit, has availability and is available' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Weeks, can edit, has availability and is not available' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Weeks, can edit and has not availability' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Teacher scenarios (mock format).
'Teacher, Mock format, can edit, has availability and is available' => [
'format' => 'theunittest',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Mock format, can edit, has availability and is not available' => [
'format' => 'theunittest',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Mock format, can edit and has not availability' => [
'format' => 'theunittest',
'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).
'Student, Topics, cannot edit, has availability and is available' => [
'format' => 'topics',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Student, Topics, cannot edit, has availability and is not available' => [
'format' => 'topics',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Student, Topics, cannot edit and has not availability' => [
'format' => 'topics',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Student scenarios (weeks).
'Student, Weeks, cannot edit, has availability and is available' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Student, Weeks, cannot edit, has availability and is not available' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Student, Weeks, cannot edit and has not availability' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Student scenarios (mock format).
'Student, Mock format, cannot edit, has availability and is available' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Student, Mock format, cannot edit, has availability and is not available' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Student, Mock format, cannot edit and has not availability' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
];
}
}

View File

@ -0,0 +1,350 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 <ferran@moodle.com>
* @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 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',
string $rolename = 'editingteacher',
bool $hasavailability = false,
bool $available = false,
bool $expected = false
) {
$data = $this->setup_hasrestrictions_scenario($format, $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 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',
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);
// 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).
'Teacher, Topics, can edit, has availability and is available' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Topics, can edit, has availability and is not available' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Topics, can edit and has not availability' => [
'format' => 'topics',
'rolename' => 'editingteacher',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Teacher scenarios (weeks).
'Teacher, Weeks, can edit, has availability and is available' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Weeks, can edit, has availability and is not available' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Weeks, can edit and has not availability' => [
'format' => 'weeks',
'rolename' => 'editingteacher',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Teacher scenarios (mock format).
'Teacher, Mock format, can edit, has availability and is available' => [
'format' => 'theunittest',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => true,
'expected' => true,
],
'Teacher, Mock format, can edit, has availability and is not available' => [
'format' => 'theunittest',
'rolename' => 'editingteacher',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Teacher, Mock format, can edit and has not availability' => [
'format' => 'theunittest',
'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',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Topics, cannot edit, has availability and is not available' => [
'format' => 'topics',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Topics, cannot edit and has not availability' => [
'format' => 'topics',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Student scenarios (weeks).
'Weeks, cannot edit, has availability and is available' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Weeks, cannot edit, has availability and is not available' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Weeks, cannot edit and has not availability' => [
'format' => 'weeks',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
// Student scenarios (mock format).
'Mock format, cannot edit, has availability and is available' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => true,
'available' => true,
'expected' => false,
],
'Mock format, cannot edit, has availability and is not available' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => true,
'available' => false,
'expected' => true,
],
'Mock format, cannot edit and has not availability' => [
'format' => 'theunittest',
'rolename' => 'student',
'hasavailability' => false,
'available' => true,
'expected' => false,
],
];
}
}

View File

@ -98,7 +98,7 @@ $courseindex-item-current: $primary !default;
.courseindex-locked {
display: none;
}
&.rectrictions {
&.restrictions {
.courseindex-locked {
display: block;
}

View File

@ -21389,7 +21389,7 @@ div.editor_atto_toolbar button .icon {
color: #fff; }
.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;

View File

@ -21335,7 +21335,7 @@ div.editor_atto_toolbar button .icon {
color: #fff; }
.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;