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

This commit is contained in:
Andrew Nicols
2022-03-01 09:35:59 +08:00
15 changed files with 619 additions and 332 deletions
+14
View File
@@ -319,6 +319,20 @@ abstract class base {
return $this->course;
}
/**
* Get the course display value for the current course.
*
* Formats extending topics or weeks will use coursedisplay as this setting name
* so they don't need to override the method. However, if the format uses a different
* display logic it must override this method to ensure the core renderers know
* if a COURSE_DISPLAY_MULTIPAGE or COURSE_DISPLAY_SINGLEPAGE is being used.
*
* @return int The current value (COURSE_DISPLAY_MULTIPAGE or COURSE_DISPLAY_SINGLEPAGE)
*/
public function get_course_display(): int {
return $this->get_course()->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
}
/**
* Return the current course modinfo.
*
@@ -28,7 +28,6 @@ use core_courseformat\base as course_format;
use course_modinfo;
use renderable;
use templatable;
use stdClass;
/**
* Base class to render a course format.
@@ -54,11 +54,16 @@ class addsection implements renderable, templatable {
/**
* 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
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
// If no editor must be displayed, just return an empty structure.
if (!$this->format->show_editor()) {
return new stdClass();
}
$format = $this->format;
$course = $format->get_course();
$options = $format->get_format_options();
@@ -66,63 +71,14 @@ class addsection implements renderable, templatable {
$lastsection = $format->get_last_section_number();
$maxsections = $format->get_max_sections();
$data = new stdClass();
// If no editor must be displayed, just retun an empty structure.
if (!$format->show_editor()) {
return $data;
}
// Component based formats handle add section button in the frontend.
$show = ($lastsection < $maxsections) || course_get_format($course)->supports_components();
$show = ($lastsection < $maxsections) || $format->supports_components();
$supportsnumsections = array_key_exists('numsections', $options);
if ($supportsnumsections) {
// Current course format has 'numsections' option, which is very confusing and we suggest course format
// developers to get rid of it (see MDL-57769 on how to do it).
if ($lastsection < $maxsections) {
$data->increase = (object) [
'url' => new moodle_url(
'/course/changenumsections.php',
['courseid' => $course->id, 'increase' => true, 'sesskey' => sesskey()]
),
];
}
if ($course->numsections > 0) {
$data->decrease = (object) [
'url' => new moodle_url(
'/course/changenumsections.php',
['courseid' => $course->id, 'increase' => false, 'sesskey' => sesskey()]
),
];
}
$data = $this->get_num_sections_data($output, $lastsection, $maxsections);
} else if (course_get_format($course)->uses_sections() && $show) {
// Current course format does not have 'numsections' option but it has multiple sections suppport.
// Display the "Add section" link that will insert a section in the end.
// Note to course format developers: inserting sections in the other positions should check both
// capabilities 'moodle/course:update' and 'moodle/course:movesections'.
if (get_string_manager()->string_exists('addsections', 'format_'.$course->format)) {
$addstring = get_string('addsections', 'format_'.$course->format);
} else {
$addstring = get_string('addsections');
}
$params = ['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()];
$singlesection = $this->format->get_section_number();
if ($singlesection) {
$params['sectionreturn'] = $singlesection;
}
$data->addsections = (object) [
'url' => new moodle_url('/course/changenumsections.php', $params),
'title' => $addstring,
'newsection' => $maxsections - $lastsection,
];
$data = $this->get_add_section_data($output, $lastsection, $maxsections);
}
if (count((array)$data)) {
@@ -131,4 +87,79 @@ class addsection implements renderable, templatable {
return $data;
}
/**
* Get the legacy num section add/remove section buttons data.
*
* Current course format has 'numsections' option, which is very confusing and we suggest course format
* developers to get rid of it (see MDL-57769 on how to do it).
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param int $lastsection the last section number
* @param int $maxsections the maximum number of sections
* @return stdClass data context for a mustache template
*/
protected function get_num_sections_data(\renderer_base $output, int $lastsection, int $maxsections): stdClass {
$format = $this->format;
$course = $format->get_course();
$data = new stdClass();
if ($lastsection < $maxsections) {
$data->increase = (object) [
'url' => new moodle_url(
'/course/changenumsections.php',
['courseid' => $course->id, 'increase' => true, 'sesskey' => sesskey()]
),
];
}
if ($course->numsections > 0) {
$data->decrease = (object) [
'url' => new moodle_url(
'/course/changenumsections.php',
['courseid' => $course->id, 'increase' => false, 'sesskey' => sesskey()]
),
];
}
return $data;
}
/**
* Get the add section button data.
*
* Current course format does not have 'numsections' option but it has multiple sections suppport.
* Display the "Add section" link that will insert a section in the end.
* Note to course format developers: inserting sections in the other positions should check both
* capabilities 'moodle/course:update' and 'moodle/course:movesections'.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param int $lastsection the last section number
* @param int $maxsections the maximum number of sections
* @return stdClass data context for a mustache template
*/
protected function get_add_section_data(\renderer_base $output, int $lastsection, int $maxsections): stdClass {
$format = $this->format;
$course = $format->get_course();
$data = new stdClass();
if (get_string_manager()->string_exists('addsections', 'format_' . $course->format)) {
$addstring = get_string('addsections', 'format_' . $course->format);
} else {
$addstring = get_string('addsections');
}
$params = ['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()];
$singlesection = $this->format->get_section_number();
if ($singlesection) {
$params['sectionreturn'] = $singlesection;
}
$data->addsections = (object) [
'url' => new moodle_url('/course/changenumsections.php', $params),
'title' => $addstring,
'newsection' => $maxsections - $lastsection,
];
return $data;
}
}
+177 -107
View File
@@ -25,16 +25,16 @@
namespace core_courseformat\output\local\content;
use cm_info;
use context_module;
use core\activity_dates;
use core_availability\info_module;
use core_completion\cm_completion_details;
use core_courseformat\base as course_format;
use core_course\output\activity_information;
use renderable;
use renderer_base;
use section_info;
use stdClass;
use templatable;
use \core_availability\info_module;
/**
* Base class to render a course module inside a course format.
@@ -84,9 +84,12 @@ class cm implements renderable, templatable {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
// Add extra display options.
$this->load_classes();
$displayoptions['linkclasses'] = $this->get_link_classes();
$displayoptions['textclasses'] = $this->get_text_classes();
$this->displayoptions = $displayoptions;
// Get the necessary classes.
$this->cmnameclass = $format->get_output_classname('content\\cm\\cmname');
@@ -97,138 +100,206 @@ class cm implements renderable, templatable {
/**
* 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
* @param renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
global $USER;
$format = $this->format;
$section = $this->section;
public function export_for_template(renderer_base $output): stdClass {
$mod = $this->mod;
$displayoptions = $this->displayoptions;
$course = $mod->get_course();
// Fetch completion details.
$showcompletionconditions = $course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS;
$completiondetails = cm_completion_details::get_instance($mod, $USER->id, $showcompletionconditions);
// Fetch activity dates.
$activitydates = [];
if ($course->showactivitydates) {
$activitydates = activity_dates::get_dates_for_module($mod, $USER->id);
}
$displayoptions['linkclasses'] = $this->get_link_classes();
$displayoptions['textclasses'] = $this->get_text_classes();
// Grouping activity.
$groupinglabel = $mod->get_grouping_label($displayoptions['textclasses']);
$activityinfodata = (object) ['hasdates' => false, 'hascompletion' => false];
// - There are activity dates to be shown; or
// - Completion info needs to be displayed
// * The activity tracks completion; AND
// * The showcompletionconditions setting is enabled OR an activity that tracks manual completion needs the manual
// completion button to be displayed on the course homepage.
$showcompletioninfo = $completiondetails->has_completion() && ($showcompletionconditions ||
(!$completiondetails->is_automatic() && $completiondetails->show_manual_completion()));
if ($showcompletioninfo || !empty($activitydates)) {
$activityinfo = new activity_information($mod, $completiondetails, $activitydates);
$activityinfodata = $activityinfo->export_for_template($output);
}
// Mod inplace name editable.
$cmname = new $this->cmnameclass(
$format,
$this->section,
$mod,
$format->show_editor(),
$this->displayoptions
);
// Mod availability.
$availability = new $this->availabilityclass(
$format,
$this->section,
$mod,
$this->displayoptions
);
$modavailability = $availability->export_for_template($output);
$data = (object)[
'cmname' => $cmname->export_for_template($output),
'grouping' => $groupinglabel,
'afterlink' => $mod->afterlink,
'altcontent' => $mod->get_formatted_content(['overflowdiv' => true, 'noclean' => true]),
'modavailability' => $mod->visible ? $modavailability : null,
'grouping' => $mod->get_grouping_label($displayoptions['textclasses']),
'modname' => get_string('pluginname', 'mod_' . $mod->modname),
'url' => $mod->url,
'activityinfo' => $activityinfodata,
'activityname' => $mod->get_formatted_name(),
'textclasses' => $displayoptions['textclasses'],
'classlist' => []
'classlist' => [],
];
if (!empty($mod->indent) && $format->uses_indentation()) {
$data->indent = $mod->indent;
if ($mod->indent > 15) {
$data->hugeindent = true;
}
}
$data->altcontent = (empty($data->altcontent)) ? false : $data->altcontent;
$data->hasname = !empty($data->cmname['displayvalue']);
// Add partial data segments.
$haspartials = [];
$haspartials['cmname'] = $this->add_cm_name_data($data, $output);
$haspartials['availability'] = $this->add_availability_data($data, $output);
$haspartials['alternative'] = $this->add_alternative_content_data($data, $output);
$haspartials['completion'] = $this->add_completion_data($data, $output);
$haspartials['editor'] = $this->add_editor_data($data, $output);
$this->add_format_data($data, $haspartials, $output);
// Calculated fields.
if (!empty($data->url)) {
$data->hasurl = true;
}
if (!$mod->visible) {
return $data;
}
/**
* Add course module name attributes to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_cm_name_data(stdClass &$data, renderer_base $output): bool {
// Mod inplace name editable.
$cmname = new $this->cmnameclass(
$this->format,
$this->section,
$this->mod,
$this->format->show_editor(),
$this->displayoptions
);
$data->cmname = $cmname->export_for_template($output);
$data->hasname = !empty($data->cmname['displayvalue']);
return $data->hasname;
}
/**
* Add the module availability to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has mod availability
*/
protected function add_availability_data(stdClass &$data, renderer_base $output): bool {
if (!$this->mod->visible) {
$data->modavailability = null;
return false;
}
// Mod availability output class.
$availability = new $this->availabilityclass(
$this->format,
$this->section,
$this->mod,
$this->displayoptions
);
$modavailability = $availability->export_for_template($output);
$data->modavailability = $modavailability;
return $availability->has_availability($output);
}
/**
* Add the alternative content to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has alternative content
*/
protected function add_alternative_content_data(stdClass &$data, renderer_base $output): bool {
$altcontent = $this->mod->get_formatted_content(
['overflowdiv' => true, 'noclean' => true]
);
$data->altcontent = (empty($altcontent)) ? false : $altcontent;
$data->afterlink = $this->mod->afterlink;
return !empty($data->altcontent);
}
/**
* Add activity completion information to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool the module has completion information
*/
protected function add_completion_data(stdClass &$data, renderer_base $output): bool {
global $USER;
$course = $this->mod->get_course();
// Fetch completion details.
$showcompletionconditions = $course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS;
$completiondetails = cm_completion_details::get_instance($this->mod, $USER->id, $showcompletionconditions);
// Fetch activity dates.
$activitydates = [];
if ($course->showactivitydates) {
$activitydates = activity_dates::get_dates_for_module($this->mod, $USER->id);
}
$activityinfodata = (object) ['hasdates' => false, 'hascompletion' => false];
// There are activity dates to be shown; or
// Completion info needs to be displayed
// * The activity tracks completion; AND
// * The showcompletionconditions setting is enabled OR an activity that tracks manual
// completion needs the manual completion button to be displayed on the course homepage.
$showcompletioninfo = $completiondetails->has_completion() && ($showcompletionconditions ||
(!$completiondetails->is_automatic() && $completiondetails->show_manual_completion()));
if ($showcompletioninfo || !empty($activitydates)) {
$activityinfo = new activity_information($this->mod, $completiondetails, $activitydates);
$activityinfodata = $activityinfo->export_for_template($output);
}
$data->activityinfo = $activityinfodata;
return $activityinfodata->hascompletion;
}
/**
* Add activity information to the data structure.
*
* @param stdClass $data the current cm data reference
* @param bool[] $haspartials the result of loading partial data elements
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has format data
*/
protected function add_format_data(stdClass &$data, array $haspartials, renderer_base $output): bool {
$result = false;
// Legacy indentation.
if (!empty($this->mod->indent) && $this->format->uses_indentation()) {
$data->indent = $this->mod->indent;
if ($this->mod->indent > 15) {
$data->hugeindent = true;
$result = true;
}
}
// Stealth and hidden from student.
if (!$this->mod->visible) {
// This module is hidden but current user has capability to see it.
$data->modhiddenfromstudents = true;
} else if ($mod->is_stealth()) {
$result = true;
} else if ($this->mod->is_stealth()) {
// This module is available but is normally not displayed on the course page
// (this user can see it because they can manage it).
$data->modstealth = true;
$result = true;
}
if ($mod->modname == 'label' &&
!$modavailability->hasmodavailability &&
!$activityinfodata->hascompletion &&
// Special inline activity format.
if (
$this->mod->has_custom_cmlist_item() &&
!$haspartials['availability'] &&
!$haspartials['completion'] &&
!isset($data->modhiddenfromstudents) &&
!isset($data->modstealth) &&
!$format->show_editor()
) {
!$this->format->show_editor()
) {
$data->modinline = true;
$result = true;
}
return $result;
}
$returnsection = $format->get_section_number();
if ($format->show_editor()) {
// Edit actions.
$controlmenu = new $this->controlmenuclass(
$format,
$this->section,
$mod,
$this->displayoptions
);
$data->controlmenu = $controlmenu->export_for_template($output);
if ($format->supports_components()) {
$data->moveicon = $output->pix_icon('i/dragdrop', '', 'moodle', ['class' => 'editing_move dragicon']);
} else {
// Add the legacy YUI move link.
$data->moveicon = course_get_cm_move($mod, $returnsection);
}
// Move and select options.
$modcontext = context_module::instance($mod->id);
/**
* Add course editor attributes to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has editor data
*/
protected function add_editor_data(stdClass &$data, renderer_base $output): bool {
if (!$this->format->show_editor()) {
return false;
}
return $data;
$returnsection = $this->format->get_section_number();
// Edit actions.
$controlmenu = new $this->controlmenuclass(
$this->format,
$this->section,
$this->mod,
$this->displayoptions
);
$data->controlmenu = $controlmenu->export_for_template($output);
if (!$this->format->supports_components()) {
// Add the legacy YUI move link.
$data->moveicon = course_get_cm_move($this->mod, $returnsection);
}
return true;
}
/**
@@ -236,7 +307,6 @@ class cm implements renderable, templatable {
*
*/
protected function load_classes() {
$mod = $this->mod;
$linkclasses = '';
@@ -53,6 +53,12 @@ class availability extends section_avalability {
/** @var array optional display options */
protected $displayoptions;
/** @var bool the has availability attribute name */
protected $hasavailabilityname;
/** @var stdClass|null the instance export data */
protected $data = null;
/**
* Constructor.
*
@@ -66,86 +72,91 @@ class availability extends section_avalability {
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
$this->hasavailabilityname = 'hasmodavailability';
}
/**
* Export this data so it can be used as the context for a mustache template.
* Get the availability data to be used as the context for a mustache template.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$data = (object)[
'info' => $this->get_info($output),
'hasmodavailability' => false,
];
if (!empty($data->info)) {
$data->hasmodavailability = true;
}
return $data;
}
/**
* Get the availability HTML form the course renderer.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return string the availability HTML
* @return array the availability data.
*/
protected function get_info(\renderer_base $output): array {
global $CFG;
if (!$this->mod->is_visible_on_course_page()) {
// Nothing to be displayed to the user.
return [];
}
if (!$this->mod->uservisible) {
return $this->user_availability_info($output);
}
$format = $this->format;
$mod = $this->mod;
$section = $this->section;
return $this->conditional_availability_info($output);
}
/**
* Get the current user availability data.
*
* This is a student who is not allowed to see the module but might be allowed
* to see availability info (i.e. "Available from ...").
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return array the availability data.
*/
protected function user_availability_info(\renderer_base $output): array {
if (empty($this->mod->availableinfo)) {
return [];
}
$info = [];
$formattedinfo = \core_availability\info::format_info(
$this->mod->availableinfo,
$this->mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, 'isrestricted');
return $info;
}
if (!$mod->is_visible_on_course_page()) {
// Nothing to be displayed to the user.
return $info;
}
if (!$mod->uservisible) {
// This is a student who is not allowed to see the module but might be allowed
// to see availability info (i.e. "Available from ...").
if (!empty($mod->availableinfo)) {
$formattedinfo = \core_availability\info::format_info(
$mod->availableinfo,
$mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, 'isrestricted');
}
return $info;
}
/**
* Get the activity availability data to display.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return array the availability data.
*/
protected function conditional_availability_info(\renderer_base $output): array {
global $CFG;
// This is a teacher who is allowed to see module but still should see the
// information that module is not available to all/some students.
$mod = $this->mod;
$modcontext = $mod->context;
$canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
if ($canviewhidden && !empty($CFG->enableavailability)) {
// Display information about conditional availability.
// Don't add availability information if user is not editing and activity is hidden.
if ($mod->visible || $format->show_editor()) {
$hidinfoclass = 'isrestricted isfullinfo';
if (!$mod->visible) {
$hidinfoclass .= ' hide';
}
$ci = new info_module($mod);
$fullinfo = $ci->get_full_information();
if ($fullinfo) {
$formattedinfo = info::format_info(
$fullinfo,
$mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, $hidinfoclass);
}
}
if (!$canviewhidden || empty($CFG->enableavailability)) {
return [];
}
// Display information about conditional availability.
// Don't add availability information if user is not editing and activity is hidden.
if (!$mod->visible && !$this->format->show_editor()) {
return [];
}
$ci = new info_module($mod);
$fullinfo = $ci->get_full_information();
if (!$fullinfo) {
return [];
}
$info = [];
$hidinfoclass = 'isrestricted isfullinfo';
if (!$mod->visible) {
$hidinfoclass .= ' hide';
}
$formattedinfo = info::format_info(
$fullinfo,
$mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, $hidinfoclass);
return $info;
}
}
@@ -27,9 +27,10 @@ namespace core_courseformat\output\local\content;
use core_courseformat\base as course_format;
use context_course;
use renderable;
use templatable;
use renderer_base;
use section_info;
use stdClass;
use templatable;
/**
* Base class to render a course section.
@@ -44,7 +45,7 @@ class section implements renderable, templatable {
protected $format;
/** @var section_info the section info */
protected $thissection;
protected $section;
/** @var section header output class */
protected $headerclass;
@@ -73,16 +74,23 @@ class section implements renderable, templatable {
/** @var bool if the title is hidden for some reason */
protected $hidecontrols = false;
/** @var bool if the section is considered stealth */
protected $isstealth = false;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $thissection the section info
* @param section_info $section the section info
*/
public function __construct(course_format $format, section_info $thissection) {
public function __construct(course_format $format, section_info $section) {
$this->format = $format;
$this->thissection = $thissection;
$this->section = $section;
if ($section->section > $format->get_last_section_number()) {
$this->isstealth = true;
}
// Load output classes names from format.
$this->headerclass = $format->get_output_classname('content\\section\\header');
@@ -118,129 +126,211 @@ class section implements renderable, templatable {
* @param renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
public function export_for_template(renderer_base $output): stdClass {
global $USER;
$format = $this->format;
$course = $format->get_course();
$thissection = $this->thissection;
$singlesection = $format->get_section_number();
$context = context_course::instance($course->id);
$section = $this->section;
$summary = new $this->summaryclass($format, $thissection);
$availability = new $this->availabilityclass($format, $thissection);
$summary = new $this->summaryclass($format, $section);
$data = (object)[
'num' => $thissection->section ?? '0',
'id' => $thissection->id,
'sectionreturnid' => $singlesection,
'num' => $section->section ?? '0',
'id' => $section->id,
'sectionreturnid' => $format->get_section_number(),
'insertafter' => false,
'summary' => $summary->export_for_template($output),
'availability' => $availability->export_for_template($output),
'restrictionlock' => !empty($thissection->availableinfo),
'highlightedlabel' => $format->get_section_highlighted_name(),
'sitehome' => $course->id == SITEID,
];
// Check if it is a stealth sections (orphaned).
if ($thissection->section > $format->get_last_section_number()) {
$data->isstealth = true;
$data->ishidden = true;
$haspartials = [];
$haspartials['availability'] = $this->add_availability_data($data, $output);
$haspartials['visibility'] = $this->add_visibility_data($data, $output);
$haspartials['editor'] = $this->add_editor_data($data, $output);
$haspartials['header'] = $this->add_header_data($data, $output);
$haspartials['cm'] = $this->add_cm_data($data, $output);
$this->add_format_data($data, $haspartials, $output);
return $data;
}
/**
* Add the section header to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_header_data(stdClass &$data, renderer_base $output): bool {
if (!empty($this->hidetitle)) {
return false;
}
if ($format->show_editor()) {
if (empty($this->hidecontrols)) {
$controlmenu = new $this->controlmenuclass($format, $thissection);
$data->controlmenu = $controlmenu->export_for_template($output);
}
if (empty($data->isstealth)) {
$data->cmcontrols = $output->course_section_add_cm_control($course, $thissection->section, $singlesection);
}
}
$section = $this->section;
$format = $this->format;
$coursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
$data->iscoursedisplaymultipage = ($coursedisplay == COURSE_DISPLAY_MULTIPAGE);
if ($data->num === 0 && !$data->iscoursedisplaymultipage) {
$data->collapsemenu = true;
}
if ($course->id == SITEID) {
$data->sitehome = true;
}
$data->contentcollapsed = false;
$preferences = $format->get_sections_preferences();
if (isset($preferences[$thissection->id])) {
$sectionpreferences = $preferences[$thissection->id];
if (!empty($sectionpreferences->contentcollapsed)) {
$data->contentcollapsed = true;
}
}
if ($thissection->section == 0) {
// Section zero is always visible only as a cmlist.
$cmlist = new $this->cmlistclass($format, $thissection);
$data->cmlist = $cmlist->export_for_template($output);
$header = new $this->headerclass($format, $thissection);
if (empty($this->hidetitle)) {
$data->header = $header->export_for_template($output);
}
return $data;
}
$header = new $this->headerclass($format, $section);
$headerdata = $header->export_for_template($output);
// When a section is displayed alone the title goes over the section, not inside it.
$header = new $this->headerclass($format, $thissection);
if ($section->section != 0 && $section->section == $format->get_section_number()) {
$data->singleheader = $headerdata;
} else {
$data->header = $headerdata;
}
return true;
}
if (!$thissection->visible) {
/**
* Add the section cm list to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_cm_data(stdClass &$data, renderer_base $output): bool {
$result = false;
$section = $this->section;
$format = $this->format;
$showsummary = ($section->section != 0 &&
$section->section != $format->get_section_number() &&
$format->get_course_display() == COURSE_DISPLAY_MULTIPAGE &&
!$format->show_editor()
);
$showcmlist = $section->uservisible;
// Add activities summary if necessary.
if ($showsummary) {
$cmsummary = new $this->cmsummaryclass($format, $section);
$data->cmsummary = $cmsummary->export_for_template($output);
$data->onlysummary = true;
$result = true;
if (!$format->is_section_current($section)) {
// In multipage, only the current section (and the section zero) has elements.
$showcmlist = false;
}
}
// Add the cm list.
if ($showcmlist) {
$cmlist = new $this->cmlistclass($format, $section);
$data->cmlist = $cmlist->export_for_template($output);
$result = true;
}
return $result;
}
/**
* Add the section availability to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_availability_data(stdClass &$data, renderer_base $output): bool {
$availability = new $this->availabilityclass($this->format, $this->section);
$data->availability = $availability->export_for_template($output);
$data->restrictionlock = !empty($this->section->availableinfo);
$data->hasavailability = $availability->has_availability($output);
return true;
}
/**
* Add the section vibility information to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
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;
$data->ishidden = true;
$result = true;
}
if (!$this->section->visible) {
$data->ishidden = true;
$data->notavailable = true;
if (has_capability('moodle/course:viewhiddensections', $context, $USER)) {
$data->hiddenfromstudents = true;
$data->notavailable = false;
$result = true;
}
}
return $result;
}
if ($thissection->section == $singlesection) {
if (empty($this->hidetitle)) {
$data->singleheader = $header->export_for_template($output);
}
} else {
if (empty($this->hidetitle)) {
$data->header = $header->export_for_template($output);
}
// Add activities summary if necessary.
if (!$format->show_editor() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$cmsummary = new $this->cmsummaryclass($format, $thissection);
$data->cmsummary = $cmsummary->export_for_template($output);
$data->onlysummary = true;
if (!$format->is_section_current($thissection)) {
// In multipage, only the current section (and the section zero) has elements.
return $data;
}
}
/**
* Add the section editor attributes to the data structure.
*
* @param stdClass $data the current cm data reference
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_editor_data(stdClass &$data, renderer_base $output): bool {
if (!$this->format->show_editor()) {
return false;
}
// Add the cm list.
if ($thissection->uservisible) {
$cmlist = new $this->cmlistclass($format, $thissection);
$data->cmlist = $cmlist->export_for_template($output);
$course = $this->format->get_course();
if (empty($this->hidecontrols)) {
$controlmenu = new $this->controlmenuclass($this->format, $this->section);
$data->controlmenu = $controlmenu->export_for_template($output);
}
$data->hasavailability = false;
if (isset($data->availability->hasavailability)) {
$data->hasavailability = $data->availability->hasavailability;
}
if ($format->is_section_current($thissection)) {
$data->highlighted = true;
$data->currentlink = get_accesshide(
get_string('currentsection', 'format_'.$format->get_format())
if (!$this->isstealth) {
$data->cmcontrols = $output->course_section_add_cm_control(
$course,
$this->section->section,
$this->format->get_section_number()
);
}
return true;
}
return $data;
/**
* Add the section format attributes to the data structure.
*
* @param stdClass $data the current cm data reference
* @param bool[] $haspartials the result of loading partial data elements
* @param renderer_base $output typically, the renderer that's calling this function
* @return bool if the cm has name data
*/
protected function add_format_data(stdClass &$data, array $haspartials, renderer_base $output): bool {
$section = $this->section;
$format = $this->format;
$data->iscoursedisplaymultipage = ($format->get_course_display() == COURSE_DISPLAY_MULTIPAGE);
if ($data->num === 0 && !$data->iscoursedisplaymultipage) {
$data->collapsemenu = true;
}
$data->contentcollapsed = false;
$preferences = $format->get_sections_preferences();
if (isset($preferences[$section->id])) {
$sectionpreferences = $preferences[$section->id];
if (!empty($sectionpreferences->contentcollapsed)) {
$data->contentcollapsed = true;
}
}
if ($format->is_section_current($section)) {
$data->highlighted = true;
$data->currentlink = get_accesshide(
get_string('currentsection', 'format_' . $format->get_format())
);
}
return true;
}
}
@@ -48,6 +48,12 @@ class availability implements renderable, templatable {
/** @var section_info the section object */
protected $section;
/** @var string the has availability attribute name */
protected $hasavailabilityname;
/** @var stdClass|null the instance export data */
protected $data = null;
/**
* Constructor.
*
@@ -57,6 +63,7 @@ class availability implements renderable, templatable {
public function __construct(course_format $format, section_info $section) {
$this->format = $format;
$this->section = $section;
$this->hasavailabilityname = 'hasavailability';
}
/**
@@ -66,16 +73,40 @@ class availability implements renderable, templatable {
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$this->build_export_data($output);
return $this->data;
}
/**
* Returns if the output has availability info to display.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return bool if the element has availability data to display
*/
public function has_availability(\renderer_base $output): bool {
$this->build_export_data($output);
$attributename = $this->hasavailabilityname;
return $this->data->$attributename;
}
/**
* Protected method to build the export data.
*
* @param \renderer_base $output typically, the renderer that's calling this function
*/
protected function build_export_data(\renderer_base $output) {
if (!empty($this->data)) {
return;
}
$data = (object)[
'info' => $this->get_info($output),
];
if (!empty($data->info)) {
$data->hasavailability = true;
}
$attributename = $this->hasavailabilityname;
$data->$attributename = !empty($data->info);
return $data;
$this->data = $data;
}
/**
@@ -75,7 +75,7 @@ class header implements renderable, templatable {
$data->title = $output->section_title_without_link($section, $course);
$coursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
$coursedisplay = $format->get_course_display();
$data->headerdisplaymultipage = false;
if ($coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$data->headerdisplaymultipage = true;
@@ -1038,7 +1038,7 @@ abstract class section_renderer extends core_course_renderer {
continue;
}
if (!$format->show_editor() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
if (!$format->show_editor() && $format->get_course_display() == COURSE_DISPLAY_MULTIPAGE) {
// Display section summary only.
echo $this->section_summary($thissection, $course, null);
} else {
+3 -3
View File
@@ -126,7 +126,7 @@ class format_legacy extends core_courseformat\base {
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
}
} else {
$usercoursedisplay = $course->coursedisplay;
$usercoursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
}
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$url->param('section', $sectionno);
@@ -268,11 +268,11 @@ class format_legacy extends core_courseformat\base {
'type' => PARAM_INT,
),
'hiddensections' => array(
'default' => $courseconfig->hiddensections,
'default' => $courseconfig->hiddensections ?? 0,
'type' => PARAM_INT,
),
'coursedisplay' => array(
'default' => $courseconfig->coursedisplay,
'default' => $courseconfig->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE,
'type' => PARAM_INT,
),
);
+1 -1
View File
@@ -135,7 +135,7 @@ class format_topics extends core_courseformat\base {
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
}
} else {
$usercoursedisplay = $course->coursedisplay;
$usercoursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
}
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$url->param('section', $sectionno);
+1
View File
@@ -6,6 +6,7 @@ Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
* New core_courseformat\base::uses_course_index() to define whether the course format uses course index or not.
* New core_courseformat\base::supports_components() to specify if the format is compatible with reactive components.
* New core_courseformat\base::uses_indentation() to specify if the format is compatible with activity indentation.
* New core_courseformat\base::get_course_display() if the course uses COURSE_DISPLAY_SINGLEPAGE or COURSE_DISPLAY_MULTIPAGE layout.
=== 3.10 ===
* Added the missing callback supports_ajax() to format_social.
+2 -2
View File
@@ -145,7 +145,7 @@ class format_weeks extends core_courseformat\base {
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
}
} else {
$usercoursedisplay = $course->coursedisplay;
$usercoursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
}
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$url->param('section', $sectionno);
@@ -268,7 +268,7 @@ class format_weeks extends core_courseformat\base {
'type' => PARAM_INT,
),
'coursedisplay' => array(
'default' => $courseconfig->coursedisplay,
'default' => $courseconfig->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE,
'type' => PARAM_INT,
),
'automaticenddate' => array(
+31
View File
@@ -1117,6 +1117,11 @@ class cm_info implements IteratorAggregate {
*/
private $contentisformatted;
/**
* @var bool True if the content has a special course item display like labels.
*/
private $customcmlistitem;
/**
* @var string
*/
@@ -1189,6 +1194,7 @@ class cm_info implements IteratorAggregate {
'course' => 'get_course_id',
'coursegroupmode' => 'get_course_groupmode',
'coursegroupmodeforce' => 'get_course_groupmodeforce',
'customcmlistitem' => 'has_custom_cmlist_item',
'effectivegroupmode' => 'get_effective_groupmode',
'extra' => false,
'groupingid' => false,
@@ -1400,6 +1406,19 @@ class cm_info implements IteratorAggregate {
return format_text($this->content, FORMAT_HTML, $options);
}
/**
* Return the module custom cmlist item flag.
*
* Activities like label uses this flag to indicate that it should be
* displayed as a custom course item instead of a tipical activity card.
*
* @return bool
*/
public function has_custom_cmlist_item(): bool {
$this->obtain_view_data();
return $this->customcmlistitem ?? false;
}
/**
* Getter method for property $name, ensures that dynamic data is obtained.
*
@@ -1788,6 +1807,18 @@ class cm_info implements IteratorAggregate {
$this->uservisible = $uservisible;
}
/**
* Sets the 'customcmlistitem' flag
*
* This can be used (by setting true) to prevent the course from rendering the
* activity item as a regular activity card. This is applied to activities like labels.
*
* @param bool $customcmlistitem if the cmlist item of that activity has a special dysplay other than a card.
*/
public function set_custom_cmlist_item(bool $customcmlistitem) {
$this->customcmlistitem = $customcmlistitem;
}
/**
* Sets the 'available' flag and related details. This flag is normally used to make
* course modules unavailable until a certain date or condition is met. (When a course
+9
View File
@@ -70,6 +70,15 @@ function label_add_instance($label) {
return $id;
}
/**
* Sets the special label display on course page.
*
* @param cm_info $cm Course-module object
*/
function label_cm_info_view(cm_info $cm) {
$cm->set_custom_cmlist_item(true);
}
/**
* Given an object containing all the necessary data,
* (defined by the form in mod_form.php) this function