MDL-82349 courseformat: add reactivity to main menu block

This commit is contained in:
ferranrecio 2024-10-09 09:05:57 +02:00 committed by ferran
parent c5133ddc47
commit 2f13ffcfb1
4 changed files with 197 additions and 220 deletions

View File

@ -21,8 +21,7 @@
* @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_site_main_menu extends block_list {
class block_site_main_menu extends block_base {
function init() {
$this->title = get_string('pluginname', 'block_site_main_menu');
}
@ -32,224 +31,37 @@ class block_site_main_menu extends block_list {
}
function get_content() {
global $USER, $CFG, $DB, $OUTPUT;
if ($this->content !== NULL) {
return $this->content;
}
$this->content = new stdClass();
$this->content->items = array();
$this->content->icons = array();
$this->content->text = '';
$this->content->footer = '';
if (empty($this->instance)) {
return $this->content;
}
require_once($CFG->dirroot . '/course/lib.php');
$course = get_site();
$format = course_get_format($course);
$courserenderer = $format->get_renderer($this->page);
$context = context_course::instance($course->id);
$isediting = $this->page->user_is_editing() && has_capability('moodle/course:manageactivities', $context);
// Output classes.
$cmnameclass = $format->get_output_classname('content\\cm\\cmname');
$controlmenuclass = $format->get_output_classname('content\\cm\\controlmenu');
$badgeattributes = [
'class' => 'badge rounded-pill bg-warning text-dark mt-2',
'data-region' => 'visibility'
];
// Extra fast view mode.
if (!$isediting) {
$modinfo = get_fast_modinfo($course);
if (!empty($modinfo->sections[0])) {
foreach($modinfo->sections[0] as $cmid) {
$cm = $modinfo->cms[$cmid];
if (!$cm->uservisible || !$cm->is_visible_on_course_page()) {
continue;
}
if ($cm->indent > 0) {
$indent = '<div class="mod-indent mod-indent-'.$cm->indent.'"></div>';
} else {
$indent = '';
}
$badges = '';
if (!$cm->visible) {
$badges = html_writer::tag(
'span',
get_string('hiddenfromstudents'),
$badgeattributes
);
}
if ($cm->is_stealth()) {
$badges = html_writer::tag(
'span',
get_string('hiddenoncoursepage'),
$badgeattributes
);
}
if (!$cm->url) {
$activitybasis = html_writer::div(
$indent . $cm->get_formatted_content(['overflowdiv' => true, 'noclean' => true]),
'activity-basis d-flex align-items-center');
$content = html_writer::div(
$activitybasis . $badges,
'contentwithoutlink activity-item activity',
['data-activityname' => $cm->name]
);
} else {
$cmname = new $cmnameclass($format, $cm->get_section_info(), $cm);
$activitybasis = html_writer::div(
$indent . $courserenderer->render($cmname),
'activity-basis d-flex align-items-center');
$content = html_writer::div(
$activitybasis . $badges,
'activity-item activity',
['data-activityname' => $cm->name]
);
}
$this->content->items[] = html_writer::div($content, 'main-menu-content section');
}
}
return $this->content;
}
// Slow & hacky editing mode.
$ismoving = ismoving($course->id);
course_create_sections_if_missing($course, 0);
$modinfo = get_fast_modinfo($course);
$format = course_get_format($course);
$modinfo = $format->get_modinfo();
$section = $modinfo->get_section_info(0);
if ($ismoving) {
$strmovefull = strip_tags(get_string('movefull', '', "'$USER->activitycopyname'"));
$strcancel= get_string('cancel');
} else {
$strmove = get_string('move');
}
$courserenderer = $format->get_renderer($this->page);
if ($ismoving) {
$this->content->icons[] = $OUTPUT->pix_icon('t/move', get_string('move'));
$this->content->items[] = $USER->activitycopyname.'&nbsp;(<a href="'.$CFG->wwwroot.'/course/mod.php?cancelcopy=true&amp;sesskey='.sesskey().'">'.$strcancel.'</a>)';
}
$output = new block_site_main_menu\output\mainsection($format, $section);
if (!empty($modinfo->sections[0])) {
foreach ($modinfo->sections[0] as $modnumber) {
$mod = $modinfo->cms[$modnumber];
if (!$mod->uservisible || !$mod->is_visible_on_course_page()) {
continue;
}
if (!$ismoving) {
$this->content->text = $courserenderer->render($output);
$controlmenu = new $controlmenuclass(
$format,
$mod->get_section_info(),
$mod
);
$menu = $controlmenu->get_action_menu($OUTPUT);
$moveaction = html_writer::link(
new moodle_url('/course/mod.php', ['sesskey' => sesskey(), 'copy' => $mod->id]),
$OUTPUT->pix_icon('i/dragdrop', $strmove),
['class' => 'editing_move_activity']
);
$editbuttons = html_writer::tag(
'div',
$courserenderer->render($controlmenu),
['class' => 'buttons activity-actions ms-auto']
);
} else {
$editbuttons = '';
$moveaction = '';
}
if ($mod->visible || has_capability('moodle/course:viewhiddenactivities', $mod->context)) {
if ($ismoving) {
if ($mod->id == $USER->activitycopy) {
continue;
}
$movingurl = new moodle_url('/course/mod.php', array('moveto' => $mod->id, 'sesskey' => sesskey()));
$this->content->items[] = html_writer::link($movingurl, '', array('title' => $strmovefull,
'class' => 'movehere'));
$this->content->icons[] = '';
}
if ($mod->indent > 0) {
$indent = '<div class="mod-indent mod-indent-'.$mod->indent.'"></div>';
} else {
$indent = '';
}
$badges = '';
if (!$mod->visible) {
$badges = html_writer::tag(
'span',
get_string('hiddenfromstudents'),
$badgeattributes
);
}
if ($mod->is_stealth()) {
$badges = html_writer::tag(
'span',
get_string('hiddenoncoursepage'),
$badgeattributes
);
}
if (!$mod->url) {
$activitybasis = html_writer::div(
$moveaction .
$indent .
$mod->get_formatted_content(['overflowdiv' => true, 'noclean' => true]) .
$editbuttons,
'activity-basis d-flex align-items-center');
$content = html_writer::div(
$activitybasis . $badges,
'contentwithoutlink activity-item activity',
['data-activityname' => $mod->name]
);
} else {
$cmname = new $cmnameclass($format, $mod->get_section_info(), $mod);
$activitybasis = html_writer::div(
$moveaction .
$indent .
$courserenderer->render($cmname) .
$editbuttons,
'activity-basis d-flex align-items-center');
$content = html_writer::div(
$activitybasis . $badges,
'activity-item activity',
['data-activityname' => $mod->name]
);
}
$this->content->items[] = html_writer::div($content, 'main-menu-content');
}
}
}
if ($ismoving) {
$movingurl = new moodle_url('/course/mod.php', array('movetosection' => $section->id, 'sesskey' => sesskey()));
$this->content->items[] = html_writer::link($movingurl, '', array('title' => $strmovefull, 'class' => 'movehere'));
$this->content->icons[] = '';
}
if ($this->page->course->id === SITEID) {
$this->content->footer = $courserenderer->course_section_add_cm_control($course,
0, null, array('inblock' => true));
}
$this->content->footer = $courserenderer->course_section_add_cm_control(
course: $course,
section: 0,
sectionreturn: null,
displayoptions: ['inblock' => true],
);
return $this->content;
}
}

View File

@ -0,0 +1,72 @@
<?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 block_site_main_menu\output;
use core_courseformat\base as courseformat;
use renderable;
use section_info;
use templatable;
/**
* Class mainsection
*
* @package block_site_main_menu
* @copyright 2024 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mainsection implements renderable, templatable {
/**
* The class constructor.
*
* @param courseformat $format the course format instance
* @param section_info $section the section to render
*/
public function __construct(
/** @var courseformat $format the course format instance. */
protected courseformat $format,
/** @var section_info $section the section to render. */
protected section_info $section,
) {
}
/**
* Export for template.
*
* @param \renderer_base $output
* @return array
*/
public function export_for_template(\renderer_base $output) {
$format = $this->format;
$course = $format->get_course();
$section = $this->section;
$sectionoutputclass = $format->get_output_classname('content\\section\\cmlist');
$sectionoutput = new $sectionoutputclass($format, $section);
$cmlist = $output->render($sectionoutput);
return [
'siteid' => $course->id,
'cmlist' => $cmlist,
'sectionid' => $section->id,
'sectionname' => $format->get_section_name($section),
'sectionnum' => $section->sectionnum,
'editing' => $format->show_editor(),
];
}
}

View File

@ -1,26 +1,34 @@
.block_site_main_menu li {
clear: both;
/* Imitate mobile grid for activity card. */
.block_site_main_menu .activity-item .activity-grid {
grid-template-columns: min-content 1fr min-content min-content min-content;
grid-template-rows: 1fr repeat(4, min-content);
grid-template-areas:
"icon name actions"
"visibility visibility visibility"
"dates dates dates"
"completion completion completion"
"altcontent altcontent altcontent"
"afterlink afterlink afterlink"
"availability availability availability";
}
.block_site_main_menu.block .content > .unlist > li > .column {
/* Made specific to win over .block.list_block .unlist > li > .column. */
width: 100%;
display: table;
margin-bottom: 0.5rem;
.block_site_main_menu .activity-item .activity-grid.noname-grid {
grid-template-columns: 1fr min-content;
grid-template-areas:
"actions"
"visibility"
"altcontent"
"groupmode"
"afterlink"
"completion"
"availability";
}
.block_site_main_menu li .buttons a img {
vertical-align: text-bottom;
.block_site_main_menu .activity-item .activity-grid.noname-grid .activity-actions {
justify-self: end;
}
.block_site_main_menu .footer {
margin-top: 1em;
}
.block_site_main_menu .section_add_menus noscript div {
display: inline;
}
.block_site_main_menu .instancename {
word-break: break-all;
/* Hide extra edit elements in block space. */
.block_site_main_menu .activity-groupmode-info {
display: none;
}

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 block_site_main_menu/mainsection
This mustache emulates a course single section structure inside a block.
It requires to include several divs to emulate the structure of a course format.
Example context (json):
{
"cmlist": "Sample",
"siteid": 1,
"sectionid": 1,
"sectionname": "Sample",
"sectionnum": 1,
"editing": true
}
}}
<div
id="block_site_main_menu_section"
class="course-content"
>
{{! The section list is used by the content module to init the sections.}}
<div
data-for="course_sectionlist"
>
{{! The section need some bottom padding and margin for the dropzone.
Otherwise the activities will cover the area. }}
<div
class="mainsection section pb-3"
data-for="section"
data-sectionid="{{sectionid}}"
data-id="{{sectionid}}"
data-number="{{sectionnum}}"
data-sectionname="{{sectionname}}"
>
{{#editing}}
{{! The section header is used as a dropzone when the section is empty.}}
<div
class="section-header"
data-for="section_title"
data-id="{{sectionid}}"
data-number="{{sectionnum}}"
>&nbsp;</div>
{{/editing}}
{{{cmlist}}}
</div>
</div>
</div>
{{#js}}
{{! The block should be fast to load, we only load the editor when needed.}}
{{#editing}}
require(
[
'core_courseformat/local/content',
'core_courseformat/courseeditor'
],
function(
Component,
Courseeditor
) {
{{! The block could be included static in other courses so we use Courseeditor.getCourseEditor. }}
new Component({
element: document.getElementById('block_site_main_menu_section'),
reactive: Courseeditor.getCourseEditor({{siteid}}),
});
}
);
{{/editing}}
{{/js}}