This commit is contained in:
Sara Arjona 2024-04-04 12:47:53 +02:00
commit 2f2eaf44c5
No known key found for this signature in database
9 changed files with 331 additions and 25 deletions

View File

@ -70,6 +70,9 @@ class section implements named_templatable, renderable {
/** @var optional move here output class */
protected $movehereclass;
/** @var optional visibility output class */
protected $visibilityclass;
/** @var bool if the title is hidden for some reason */
protected $hidetitle = false;
@ -104,6 +107,7 @@ class section implements named_templatable, renderable {
$this->controlmenuclass = $format->get_output_classname('content\\section\\controlmenu');
$this->availabilityclass = $format->get_output_classname('content\\section\\availability');
$this->movehereclass = $format->get_output_classname('content\\section\\movehere');
$this->visibilityclass = $format->get_output_classname('content\\section\\visibility');
}
/**
@ -256,8 +260,6 @@ class section implements named_templatable, renderable {
protected function add_visibility_data(stdClass &$data, renderer_base $output): bool {
global $USER;
$result = false;
$course = $this->format->get_course();
$context = context_course::instance($course->id);
// Check if it is a stealth sections (orphaned).
if ($this->isstealth) {
$data->isstealth = true;
@ -266,13 +268,16 @@ class section implements named_templatable, renderable {
}
if (!$this->section->visible) {
$data->ishidden = true;
$data->notavailable = true;
$course = $this->format->get_course();
$context = context_course::instance($course->id);
if (has_capability('moodle/course:viewhiddensections', $context, $USER)) {
$data->hiddenfromstudents = true;
$data->notavailable = false;
$result = true;
}
}
/* @var \core_courseformat\output\local\content\section\visibility $visibility By default the visibility class used
* here but can be overriden by any course format */
$visibility = new $this->visibilityclass($this->format, $this->section);
$data->visibility = $visibility->export_for_template($output);
return $result;
}

View File

@ -0,0 +1,133 @@
<?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\content\section;
use context_course;
use core\output\choicelist;
use core\output\local\dropdown\status;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use pix_icon;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a section visibility inside a course format.
*
* @package core_courseformat
* @copyright 2024 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class visibility implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
protected $section;
/**
* Constructor.
* @param course_format $format the course format
* @param section_info $section the section info
*/
public function __construct(course_format $format, section_info $section) {
$this->format = $format;
$this->section = $section;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass|null data context for a mustache template
*/
public function export_for_template(\renderer_base $output): ?stdClass {
global $USER;
$context = context_course::instance($this->section->course);
$data = new stdClass();
$data->editing = $this->format->show_editor();
if (!$this->section->visible) {
$data->notavailable = true;
if (has_capability('moodle/course:sectionvisibility', $context, $USER)) {
$data->hiddenfromstudents = true;
$data->notavailable = false;
$badgetext = $output->sr_text(get_string('availability'));
$badgetext .= get_string("hiddenfromstudents");
$icon = $this->get_icon('hide');
$choice = new choicelist();
$choice->add_option(
'show',
get_string("availability_show", 'core_courseformat'),
$this->get_option_data('show', 'sectionShow')
);
$choice->add_option(
'hide',
get_string('availability_hide', 'core_courseformat'),
$this->get_option_data('hide', 'sectionHide')
);
$choice->set_selected_value('hide');
$dropdown = new status(
$output->render($icon) . ' ' . $badgetext,
$choice,
['dialogwidth' => status::WIDTH['big']],
);
$data->dropwdown = $dropdown->export_for_template($output);
}
}
return $data;
}
/**
* Get the data for the option.
*
* @param string $name the name of the option
* @param string $action the state action of the option
* @return array
*/
private function get_option_data(string $name, string $action): array {
$baseurl = course_get_url($this->section->course, $this->section);
$baseurl->param('sesskey', sesskey());
$baseurl->param($action, $this->section->section);
return [
'description' => get_string("availability_{$name}_help", 'core_courseformat'),
'icon' => $this->get_icon($name),
// Non-ajax behat is not smart enough to discrimante hidden links
// so we need to keep providing the non-ajax links.
'url' => $baseurl,
'extras' => [
'data-id' => $this->section->id,
'data-action' => $action,
],
];
}
/**
* Get the icon for the section visibility.
* @param string $selected the visibility selected value
* @return pix_icon
*/
protected function get_icon(string $selected): pix_icon {
if ($selected === 'hide') {
return new pix_icon('t/show', '');
} else {
return new pix_icon('t/hide', '');
}
}
}

View File

@ -24,30 +24,59 @@
Example context (json):
{
"iscurrent" : true,
"hiddenfromstudents" : "1",
"notavailable" : "0",
"highlightedlabel" : "Highlighted"
"highlightedlabel" : "Highlighted",
"visibility": {
"editing": "1",
"dropwdown": {
"buttonid": "dropwdownbutton_648b1549b929e30",
"buttoncontent": "Show on course page",
"choices": {
"description": null,
"options": [
{
"value": "show",
"name": "Show on course page",
"description": "Available to students.",
"url": null,
"icon": null,
"disabled": false,
"hasicon": false,
"hasurl": false,
"selected": true,
"optionnumber": 1,
"first": true,
"optionuniqid": "choice_option_648b1549b929e31"
},
{
"value": "hide",
"name": "Hide on course page",
"description": "Not available to students.",
"url": null,
"icon": null,
"disabled": false,
"hasicon": false,
"hasurl": false,
"optionnumber": 2,
"first": false,
"optionuniqid": "choice_option_648b1549b929e32"
}
],
"hasoptions": true
}
}
}
}
}}
{{#editing}}
<span class="badge rounded-pill bg-primary text-white order-1 {{^iscurrent}}d-none{{/iscurrent}}" data-type="iscurrent">
{{ highlightedlabel }}
</span>
<span class="badge rounded-pill bg-warning text-dark order-2 {{^hiddenfromstudents}}d-none{{/hiddenfromstudents}}" data-type="hiddenfromstudents">
{{#str}}hiddenfromstudents{{/str}}
</span>
<span class="badge rounded-pill bg-secondary text-dark order-3 {{^notavailable}}d-none{{/notavailable}}">
{{#str}}notavailable{{/str}}
</span>
{{/editing}}
{{^editing}}
{{#iscurrent}}
<span class="badge rounded-pill bg-primary text-white order-1">{{ highlightedlabel }}</span>
{{/iscurrent}}
{{#hiddenfromstudents}}
<span class="badge rounded-pill bg-warning text-dark order-2">{{#str}}hiddenfromstudents{{/str}}</span>
{{/hiddenfromstudents}}
{{#notavailable}}
<span class="badge rounded-pill bg-secondary text-dark order-3">{{#str}}notavailable{{/str}}</span>
{{/notavailable}}
{{/editing}}
{{#visibility}}
{{>core_courseformat/local/content/section/visibility}}
{{/visibility}}

View File

@ -0,0 +1,85 @@
{{!
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/>.
}}
{{!
@template core_courseformat/local/content/section/visibility
Template to displays if an section is in hidden or not.
Example context (json):
{
"editing": "1",
"notavailable": 1,
"hiddenfromstudents": 0,
"dropwdown": {
"buttonid": "dropwdownbutton_648b1549b929e30",
"buttoncontent": "Show on course page",
"choices": {
"description": null,
"options": [
{
"value": "show",
"name": "Show on course page",
"description": "Available to students.",
"url": null,
"icon": null,
"disabled": false,
"hasicon": false,
"hasurl": false,
"selected": true,
"optionnumber": 1,
"first": true,
"optionuniqid": "choice_option_648b1549b929e31"
},
{
"value": "hide",
"name": "Hide on course page",
"description": "Not available to students.",
"url": null,
"icon": null,
"disabled": false,
"hasicon": false,
"hasurl": false,
"optionnumber": 2,
"first": false,
"optionuniqid": "choice_option_648b1549b929e32"
}
],
"hasoptions": true
}
}
}
}}
{{#editing}}
<div class="order-2" data-region="visibility">
{{#dropwdown}}
{{< core/local/dropdown/status}}
{{$ buttonclasses }} badge rounded-pill bg-secondary text-dark dropdown-toggle border-0{{/ buttonclasses }}
{{/ core/local/dropdown/status}}
{{/dropwdown}}
</div>
<span class="badge rounded-pill bg-secondary text-dark order-3 {{^notavailable}}d-none{{/notavailable}}">
{{#str}}notavailable{{/str}}
</span>
{{/editing}}
{{^editing}}
{{#hiddenfromstudents}}
<span class="badge rounded-pill bg-secondary text-dark order-2">{{#pix}}i/show, core{{/pix}}{{#str}}hiddenfromstudents{{/str}}</span>
{{/hiddenfromstudents}}
{{#notavailable}}
<span class="badge rounded-pill bg-secondary text-dark order-3">{{#str}}notavailable{{/str}}</span>
{{/notavailable}}
{{/editing}}

View File

@ -2,20 +2,22 @@
Feature: Varify section visibility interface
In order to edit the course sections visibility
As a teacher
I need to be able to see the updateds visibility information
I need to be able to see the updated visibility information
Background:
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| category | 0 |
| numsections | 1 |
| numsections | 3 |
| initsections | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section |
| assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
@ -34,14 +36,14 @@ Feature: Varify section visibility interface
@javascript
Scenario: Hide a section also hides the activities.
When I hide section "1"
Then I should see "Hidden from students" in the "New section" "section"
Then I should see "Hidden from students" in the "Section 1" "section"
And I should see "Hidden from students" in the "Activity sample 1" "activity"
And I show section "1"
And I should not see "Hidden from students" in the "New section" "section"
And I should not see "Hidden from students" in the "Section 1" "section"
And I should not see "Hidden from students" in the "Activity sample 1" "activity"
@javascript
Scenario: Hiden activities in hidden sections stay hidden when the section is shown.
Scenario: Hidden activities in hidden sections stay hidden when the section is shown.
Given I open "Activity sample 1" actions menu
And I choose "Hide" in the open action menu
And I should see "Hidden from students" in the "Activity sample 1" "activity"
@ -49,3 +51,33 @@ Feature: Varify section visibility interface
And I should see "Hidden from students" in the "Activity sample 1" "activity"
When I show section "1"
Then I should see "Hidden from students" in the "Activity sample 1" "activity"
@javascript
Scenario: Hidden sections can be shown and hidden using the drop down menu in the activity card.
Given I hide section "1"
When I click on "Hidden from students" "button" in the "Section 1" "section"
And I should see "Show on course page" in the "Section 1" "section"
And I should see "Hide on course page" in the "Section 1" "section"
And I click on "Show on course page" "link" in the "Section 1" "section"
Then I should not see "Hidden from students" in the "Section 1" "section"
@javascript
Scenario: Hidden sections are shown as "not available" to student when
the course is set to show hidden sections "not available". They are shown
as "hidden from students" to editing teachers.
Given the following "courses" exist:
| fullname | shortname | format | hiddensections | numsections | initsections |
| Course 2 | C2 | topics | 0 | 3 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C2 | student |
| teacher1 | C2 | editingteacher |
And I am on the "C2" "Course" page logged in as "admin"
And I turn editing mode on
And I hide section "1"
And I log out
When I am on the "C2" "Course" page logged in as "student1"
Then I should see "Not available" in the "Section 1" "section"
And I log out
And I am on the "C2" "Course" page logged in as "teacher1"
And I should see "Hidden from students" in the "Section 1" "section"

View File

@ -3,6 +3,10 @@ This files describes API changes for course formats
Overview of this plugin type at https://moodledev.io/docs/apis/plugintypes/format
=== 4.4 ===
* The core_courseformat\output\local\content\section::export_for_template() is not returning hiddenfromstudents and notavailable
directly in the data array anymore. Instead, it returns the visibility data in the 'visibility' key. It means that templates
derived from core should be adjusted to use $data->visibility->hiddenfromstudents and $data->visibility->notavailable instead
of $data->hiddenfromstudents and $data->notavailable.
* The state mutations sectionMove is deprecated. From now on sectionMoveAfter is the only
valid section move mutation.
* The state action core_courseformat\stateactions::section_move is deprecated and

View File

@ -1151,6 +1151,12 @@ $divider-hover-color: $primary !default;
.sectionbadges .badge {
margin-left: map-get($spacers, 2);
font-weight: normal;
.icon {
font-size: 12px;
width: 12px;
height: 12px;
}
}
.course-section-header.draggable {

View File

@ -28966,6 +28966,12 @@ span.editinstructions .alert-link {
}
.course-section .sectionbadges .badge {
margin-left: 0.5rem;
font-weight: normal;
}
.course-section .sectionbadges .badge .icon {
font-size: 12px;
width: 12px;
height: 12px;
}
.course-section .course-section-header.draggable {
cursor: move;

View File

@ -28966,6 +28966,12 @@ span.editinstructions .alert-link {
}
.course-section .sectionbadges .badge {
margin-left: 0.5rem;
font-weight: normal;
}
.course-section .sectionbadges .badge .icon {
font-size: 12px;
width: 12px;
height: 12px;
}
.course-section .course-section-header.draggable {
cursor: move;