MDL-80220 core_courseformat: Add section badge template

* Adjust badge style to look similar to the one in the activity
* Add visibility selection dropdown to section
This commit is contained in:
Laurent David 2023-12-26 11:40:53 +01:00
parent c895def59b
commit 27a681c6fe
8 changed files with 294 additions and 20 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

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

@ -1193,6 +1193,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

@ -29024,6 +29024,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

@ -29024,6 +29024,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;