MDL-65974 course: move format renderers to outputs

This commit is contained in:
Ferran Recio 2020-10-22 09:22:54 +02:00
parent 1c839f9072
commit 8e7dc42440
49 changed files with 3553 additions and 1958 deletions

View file

@ -9,6 +9,10 @@ below.
If you want to store information in the database for your format, or control
access to features of your format, you need some of the optional files too.
If you want to override some standard course output component (located in
coure/classes/output/{course|section|cm}_format/*) you need to create an
extend class inside your course/format/yourformat/classes/output.
All names below assume that your format is called 'yourformat'.
@ -139,3 +143,31 @@ Optional file (styles)
If this file exists it will be included in the CSS Moodle generates.
Optional files (outputs)
----------------------
By default, the format renderer will use those output classes:
* core_course\output\course_format: for the general course structure
* core_course\output\course_format\*: to render specific course structure parts
* core_course\output\section_format: for the complete section output
* core_course\output\section_format\*: to render specific section parts
* core_course\output\cm_format: for output an activity inside a section
* core_course\output\cm_format\*: for speficis parts of the cm output
Your format can override any of this output classes just by creating class
inside your format_yourformat\output\ classes. We recommend to extend the
original class to ensure all element will work as expected.
For example: if you want to change the section header, you should create
format_yourformat\output\section_format\header, which will extend the original
core_course\output\section_format\header class.
By default, only two format renderer methods are needed to render a course:
- render_course_format to render a full course
- course_section_updated_cm_item used by the course editor to refresh a specific cm item
Formats can override those two methods to use different templates to render a course.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/course/format/renderer.php');
/**
* Basic renderer for singleactivity format.
@ -31,7 +32,7 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2013 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_singleactivity_renderer extends plugin_renderer_base {
class format_singleactivity_renderer extends format_section_renderer_base {
/**
* Displays the activities list in cases when course view page is not
@ -42,20 +43,28 @@ class format_singleactivity_renderer extends plugin_renderer_base {
* if true displays all other activities
*/
public function display($course, $orphaned) {
$courserenderer = $this->page->get_renderer('core', 'course');
$format = course_get_format($course);
$modinfo = $format->get_modinfo();
$cmlistclass = $format->get_output_classname('section_format\\cmlist');
$output = '';
$modinfo = get_fast_modinfo($course);
if ($orphaned) {
if (!empty($modinfo->sections[1])) {
$output .= $this->output->heading(get_string('orphaned', 'format_singleactivity'), 3, 'sectionname');
$output .= $this->output->box(get_string('orphanedwarning', 'format_singleactivity'));
$output .= $courserenderer->course_section_cm_list($course, 1, 1);
$section = $modinfo->get_section_info(1);
$output .= $this->render(new $cmlistclass($format, $section));
}
} else {
$output .= $courserenderer->course_section_cm_list($course, 0, 0);
$section = $modinfo->get_section_info(0);
$output .= $this->render(new $cmlistclass($format, $section));
if (empty($modinfo->sections[0]) && course_get_format($course)->activity_has_subtypes()) {
// Course format was unable to automatically redirect to add module page.
$output .= $courserenderer->course_section_add_cm_control($course, 0, 0);
$output .= $this->course_section_add_cm_control($course, 0, 0);
}
}
return $output;

View file

@ -0,0 +1,118 @@
<?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/>.
/**
* Contains the default section controls output class.
*
* @package format_topics
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace format_topics\output\section_format;
use context_course;
/**
* Base class to render a course section menu.
*
* @package format_topics
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class controlmenu extends \core_course\output\section_format\controlmenu {
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
protected $section;
/**
* Generate the edit control items of a section.
*
* This method must remain public until the final deprecation of section_edit_control_items.
*
* @return array of edit control items
*/
public function section_control_items() {
$format = $this->format;
$section = $this->section;
$course = $format->get_course();
$sectionreturn = $format->get_section_number();
$coursecontext = context_course::instance($course->id);
if ($sectionreturn) {
$url = course_get_url($course, $section->section);
} else {
$url = course_get_url($course);
}
$url->param('sesskey', sesskey());
$controls = [];
if ($section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
if ($course->marker == $section->section) { // Show the "light globe" on/off.
$url->param('marker', 0);
$highlightoff = get_string('highlightoff');
$controls['highlight'] = [
'url' => $url,
'icon' => 'i/marked',
'name' => $highlightoff,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'editing_highlight',
'data-action' => 'removemarker'
],
];
} else {
$url->param('marker', $section->section);
$highlight = get_string('highlight');
$controls['highlight'] = [
'url' => $url,
'icon' => 'i/marker',
'name' => $highlight,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'editing_highlight',
'data-action' => 'setmarker'
],
];
}
}
$parentcontrols = parent::section_control_items();
// If the edit key exists, we are going to insert our controls after it.
if (array_key_exists("edit", $parentcontrols)) {
$merged = [];
// We can't use splice because we are using associative arrays.
// Step through the array and merge the arrays.
foreach ($parentcontrols as $key => $action) {
$merged[$key] = $action;
if ($key == "edit") {
// If we have come to the edit key, merge these controls here.
$merged = array_merge($merged, $controls);
}
}
return $merged;
} else {
return array_merge($controls, $parentcontrols);
}
}
}

View file

@ -37,9 +37,10 @@ if ($topic = optional_param('topic', 0, PARAM_INT)) {
}
// End backwards-compatible aliasing.
$context = context_course::instance($course->id);
// Retrieve course format option fields and add them to the $course object.
$course = course_get_format($course)->get_course();
$format = course_get_format($course);
$course = $format->get_course();
$context = context_course::instance($course->id);
if (($marker >= 0) && has_capability('moodle/course:setcurrentsection', $context) && confirm_sesskey()) {
$course->marker = $marker;
@ -52,10 +53,11 @@ course_create_sections_if_missing($course, 0);
$renderer = $PAGE->get_renderer('format_topics');
if (!empty($displaysection)) {
$renderer->print_single_section_page($course, null, null, null, null, $displaysection);
} else {
$renderer->print_multiple_section_page($course, null, null, null, null);
$format->set_section_number($displaysection);
}
$outputclass = $format->get_output_classname('course_format');
$widget = new $outputclass($format);
echo $renderer->render($widget);
// Include course format js module.
$PAGE->requires->js('/course/format/topics/format.js');

View file

@ -35,7 +35,7 @@ use core\output\inplace_editable;
* @copyright 2012 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_topics extends format_base {
class format_topics extends core_course\course_format {
/**
* Returns true if this course format uses sections.
@ -68,7 +68,7 @@ class format_topics extends format_base {
* Returns the default section name for the topics course format.
*
* If the section number is 0, it will use the string with key = section0name from the course format's lang file.
* If the section number is not 0, the base implementation of format_base::get_default_section_name which uses
* If the section number is not 0, the base implementation of course_format::get_default_section_name which uses
* the string with the key = 'sectionname' from the course format's lang file + the section number will be used.
*
* @param stdClass $section Section object from database or just field course_sections section
@ -79,12 +79,21 @@ class format_topics extends format_base {
// Return the general section.
return get_string('section0name', 'format_topics');
} else {
// Use format_base::get_default_section_name implementation which
// Use course_format::get_default_section_name implementation which
// will display the section name in "Topic n" format.
return parent::get_default_section_name($section);
}
}
/**
* Generate the title for this section page.
*
* @return string the page title
*/
public function page_title(): string {
return get_string('topicoutline');
}
/**
* The URL to use for the specified course (with section).
*
@ -412,7 +421,15 @@ class format_topics extends format_base {
// For show/hide actions call the parent method and return the new content for .section_availability element.
$rv = parent::section_action($section, $action, $sr);
$renderer = $PAGE->get_renderer('format_topics');
$rv['section_availability'] = $renderer->section_availability($this->get_section($section));
if (!($section instanceof section_info)) {
$modinfo = $this->get_modinfo();
$section = $modinfo->get_section_info($section->section);
}
$elementclass = $this->get_output_classname('section_format\\availability');
$availability = new $elementclass($this, $section);
$rv['section_availability'] = $renderer->render($availability);
return $rv;
}

View file

@ -49,33 +49,6 @@ class format_topics_renderer extends format_section_renderer_base {
$page->set_other_editing_capability('moodle/course:setcurrentsection');
}
/**
* Generate the starting container html for a list of sections.
*
* @return string HTML to output.
*/
protected function start_section_list() {
return html_writer::start_tag('ul', ['class' => 'topics']);
}
/**
* Generate the closing container html for a list of sections.
*
* @return string HTML to output.
*/
protected function end_section_list() {
return html_writer::end_tag('ul');
}
/**
* Generate the title for this section page.
*
* @return string the page title
*/
protected function page_title() {
return get_string('topicoutline');
}
/**
* Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page.
*
@ -98,77 +71,4 @@ class format_topics_renderer extends format_section_renderer_base {
return $this->render(course_get_format($course)->inplace_editable_render_section_name($section, false));
}
/**
* Generate the edit control items of a section.
*
* @param int|stdClass $course The course entry from DB
* @param section_info|stdClass $section The course_section entry from DB
* @param bool $onsectionpage true if being printed on a section page
* @return array of edit control items
*/
protected function section_edit_control_items($course, $section, $onsectionpage = false) {
if (!$this->page->user_is_editing()) {
return [];
}
$coursecontext = context_course::instance($course->id);
if ($onsectionpage) {
$url = course_get_url($course, $section->section);
} else {
$url = course_get_url($course);
}
$url->param('sesskey', sesskey());
$controls = [];
if ($section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
if ($course->marker == $section->section) { // Show the "light globe" on/off.
$url->param('marker', 0);
$highlightoff = get_string('highlightoff');
$controls['highlight'] = [
'url' => $url,
'icon' => 'i/marked',
'name' => $highlightoff,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'editing_highlight',
'data-action' => 'removemarker'
],
];
} else {
$url->param('marker', $section->section);
$highlight = get_string('highlight');
$controls['highlight'] = [
'url' => $url,
'icon' => 'i/marker',
'name' => $highlight,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'editing_highlight',
'data-action' => 'setmarker'
],
];
}
}
$parentcontrols = parent::section_edit_control_items($course, $section, $onsectionpage);
// If the edit key exists, we are going to insert our controls after it.
if (array_key_exists("edit", $parentcontrols)) {
$merged = [];
// We can't use splice because we are using associative arrays.
// Step through the array and merge the arrays.
foreach ($parentcontrols as $key => $action) {
$merged[$key] = $action;
if ($key == "edit") {
// If we have come to the edit key, merge these controls here.
$merged = array_merge($merged, $controls);
}
}
return $merged;
} else {
return array_merge($controls, $parentcontrols);
}
}
}

View file

@ -37,16 +37,19 @@ if ($week = optional_param('week', 0, PARAM_INT)) {
}
// End backwards-compatible aliasing..
$format = course_get_format($course);
// Make sure section 0 is created.
$course = course_get_format($course)->get_course();
course_create_sections_if_missing($course, 0);
course_create_sections_if_missing($format->get_course(), 0);
$renderer = $PAGE->get_renderer('format_weeks');
if (!empty($displaysection)) {
$renderer->print_single_section_page($course, null, null, null, null, $displaysection);
} else {
$renderer->print_multiple_section_page($course, null, null, null, null);
$format->set_section_number($displaysection);
}
$outputclass = $format->get_output_classname('course_format');
$output = new $outputclass($format);
echo $renderer->render($output);
$PAGE->requires->js('/course/format/weeks/format.js');

View file

@ -45,6 +45,14 @@ class format_weeks extends core_course\course_format {
return true;
}
/**
* Generate the title for this section page
* @return string the page title
*/
public function page_title(): string {
return get_string('weeklyoutline');
}
/**
* Returns the display name of the given section that the course prefers.
*
@ -505,7 +513,16 @@ class format_weeks extends core_course\course_format {
// Call the parent method and return the new content for .section_availability element.
$rv = parent::section_action($section, $action, $sr);
$renderer = $PAGE->get_renderer('format_weeks');
$rv['section_availability'] = $renderer->section_availability($this->get_section($section));
if (!($section instanceof section_info)) {
$modinfo = $this->get_modinfo();
$section = $modinfo->get_section_info($section->section);
}
$elementclass = $this->get_output_classname('section_format\\availability');
$availability = new $elementclass($this, $section);
$rv['section_availability'] = $renderer->render($availability);
return $rv;
}

View file

@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/course/format/renderer.php');
require_once($CFG->dirroot.'/course/format/weeks/lib.php');
use core_course\output\section_format\availability;
/**
* Basic renderer for weeks format.
@ -36,29 +37,6 @@ require_once($CFG->dirroot.'/course/format/weeks/lib.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_weeks_renderer extends format_section_renderer_base {
/**
* Generate the starting container html for a list of sections
* @return string HTML to output.
*/
protected function start_section_list() {
return html_writer::start_tag('ul', array('class' => 'weeks'));
}
/**
* Generate the closing container html for a list of sections
* @return string HTML to output.
*/
protected function end_section_list() {
return html_writer::end_tag('ul');
}
/**
* Generate the title for this section page
* @return string the page title
*/
protected function page_title() {
return get_string('weeklyoutline');
}
/**
* Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page