first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,129 @@
<?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;
use cm_info;
use core_courseformat\output\local\courseformat_named_templatable;
use core\output\named_templatable;
use renderer_base;
use stdClass;
/**
* Base class to render an activity badge.
*
* Plugins can extend this class and override some methods to customize the content to be displayed in the activity badge.
*
* @package core_courseformat
* @copyright 2023 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activitybadge implements named_templatable, \renderable {
use courseformat_named_templatable;
/** @var array Badge defined styles. */
public const STYLES = [
'none' => 'badge-none',
'dark' => 'bg-dark text-white',
'danger' => 'bg-danger text-white',
'warning' => 'bg-warning text-dark',
'info' => 'bg-info text-white',
];
/** @var cm_info The course module information. */
protected $cminfo = null;
/** @var string The content to be displayed in the activity badge. */
protected $content = null;
/** @var string The style for the activity badge. */
protected $style = self::STYLES['none'];
/** @var \moodle_url An optional URL to redirect the user when the activity badge is clicked. */
protected $url = null;
/** @var string An optional element id in case the module wants to add some code for the activity badge (events, CSS...). */
protected $elementid = null;
/**
* @var array An optional array of extra HTML attributes to add to the badge element (for example, data attributes).
* The format for this array is [['name' => 'attr1', 'value' => 'attrval1'], ['name' => 'attr2', 'value' => 'attrval2']].
*/
protected $extraattributes = [];
/**
* Constructor.
*
* @param cm_info $cminfo The course module information.
*/
public function __construct(cm_info $cminfo) {
$this->cminfo = $cminfo;
}
/**
* 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 data context for a mustache template
*/
final public function export_for_template(renderer_base $output): stdClass {
$this->update_content();
if (empty($this->content)) {
return new stdClass();
}
$data = (object)[
'badgecontent' => $this->content,
'badgestyle' => $this->style,
];
if (!empty($this->url)) {
$data->badgeurl = $this->url->out();
}
if (!empty($this->elementid)) {
$data->badgeelementid = $this->elementid;
}
if (!empty($this->extraattributes)) {
$data->badgeextraattributes = $this->extraattributes;
}
return $data;
}
/**
* Creates an instance of activityclass for the given course module, in case it implements it.
*
* @param cm_info $cminfo
* @return self|null An instance of activityclass for the given module or null if the module doesn't implement it.
*/
final public static function create_instance(cm_info $cminfo): ?self {
$classname = '\mod_' . $cminfo->modname . '\output\courseformat\activitybadge';
if (!class_exists($classname)) {
return null;
}
return new $classname($cminfo);
}
/**
* This method will be called before exporting the template.
*
* It should be implemented by any module extending this class and will be in charge of updating any of the class attributes
* with the proper information that will be displayed in the activity badge (like the content or the badge style).
*/
abstract protected function update_content(): void;
}
@@ -0,0 +1,34 @@
<?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;
use core_courseformat\output\section_renderer;
/**
* Legacy course format renderer.
*
* Since Moodle 4.0, renderer.php file was optional (although highly recommended) for course formats. From Moodle 4.0 onwards,
* renderer is required to support the new course editor implementation.
* This legacy class has been created for backward compatibility, to avoid some errors with course formats (such as social)
* without this renderer.php file.
*
* @package core_courseformat
* @copyright 2021 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class legacy_renderer extends section_renderer {
}
@@ -0,0 +1,206 @@
<?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;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use course_modinfo;
use renderable;
/**
* Base class to render a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var \core_courseformat\base the course format class */
protected $format;
/** @var string the section format class */
protected $sectionclass;
/** @var string the add section output class name */
protected $addsectionclass;
/** @var string section navigation class name */
protected $sectionnavigationclass;
/** @var string section selector class name */
protected $sectionselectorclass;
/** @var string the section control menu class */
protected $sectioncontrolmenuclass;
/** @var string bulk editor bar toolbox */
protected $bulkedittoolsclass;
/** @var bool if uses add section */
protected $hasaddsection = true;
/**
* Constructor.
*
* @param course_format $format the coruse format
*/
public function __construct(course_format $format) {
$this->format = $format;
// Load output classes names from format.
$this->sectionclass = $format->get_output_classname('content\\section');
$this->addsectionclass = $format->get_output_classname('content\\addsection');
$this->sectionnavigationclass = $format->get_output_classname('content\\sectionnavigation');
$this->sectionselectorclass = $format->get_output_classname('content\\sectionselector');
$this->bulkedittoolsclass = $format->get_output_classname('content\\bulkedittools');
$this->sectioncontrolmenuclass = $format->get_output_classname('content\\section\\controlmenu');
}
/**
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
*
* @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) {
global $PAGE;
$format = $this->format;
$sections = $this->export_sections($output);
$initialsection = '';
$data = (object)[
'title' => $format->page_title(), // This method should be in the course_format class.
'initialsection' => $initialsection,
'sections' => $sections,
'format' => $format->get_format(),
'sectionreturn' => null,
];
// The single section format has extra navigation.
if ($this->format->get_sectionid()) {
$singlesectionnum = $this->format->get_sectionnum();
if (!$PAGE->theme->usescourseindex) {
$sectionnavigation = new $this->sectionnavigationclass($format, $singlesectionnum);
$data->sectionnavigation = $sectionnavigation->export_for_template($output);
$sectionselector = new $this->sectionselectorclass($format, $sectionnavigation);
$data->sectionselector = $sectionselector->export_for_template($output);
}
$data->hasnavigation = true;
$data->singlesection = array_shift($data->sections);
$data->sectionreturn = $singlesectionnum;
}
if ($this->hasaddsection) {
$addsection = new $this->addsectionclass($format);
$data->numsections = $addsection->export_for_template($output);
}
if ($format->show_editor()) {
$bulkedittools = new $this->bulkedittoolsclass($format);
$data->bulkedittools = $bulkedittools->export_for_template($output);
}
return $data;
}
/**
* Retrieves the action menu for the page header of the local content section.
*
* @param \renderer_base $output The renderer object used for rendering the action menu.
* @return string|null The rendered action menu HTML, null if page no action menu is available.
*/
public function get_page_header_action(\renderer_base $output): ?string {
$sectionid = $this->format->get_sectionid();
if ($sectionid !== null) {
$modinfo = $this->format->get_modinfo();
$sectioninfo = $modinfo->get_section_info_by_id($sectionid);
/** @var \core_courseformat\output\local\content\section\controlmenu */
$controlmenu = new $this->sectioncontrolmenuclass($this->format, $sectioninfo);
return $output->render($controlmenu->get_action_menu($output));
}
return null;
}
/**
* Export sections array data.
*
* @param renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
protected function export_sections(\renderer_base $output): array {
$format = $this->format;
$course = $format->get_course();
$modinfo = $this->format->get_modinfo();
// Generate section list.
$sections = [];
$stealthsections = [];
$numsections = $format->get_last_section_number();
foreach ($this->get_sections_to_display($modinfo) as $sectionnum => $thissection) {
// The course/view.php check the section existence but the output can be called
// from other parts so we need to check it.
if (!$thissection) {
throw new \moodle_exception('unknowncoursesection', 'error', course_get_url($course),
format_string($course->fullname));
}
$section = new $this->sectionclass($format, $thissection);
if ($sectionnum > $numsections) {
// Activities inside this section are 'orphaned', this section will be printed as 'stealth' below.
if (!empty($modinfo->sections[$sectionnum])) {
$stealthsections[] = $section->export_for_template($output);
}
continue;
}
if (!$format->is_section_visible($thissection)) {
continue;
}
$sections[] = $section->export_for_template($output);
}
if (!empty($stealthsections)) {
$sections = array_merge($sections, $stealthsections);
}
return $sections;
}
/**
* Return an array of sections to display.
*
* This method is used to differentiate between display a specific section
* or a list of them.
*
* @param course_modinfo $modinfo the current course modinfo object
* @return section_info[] an array of section_info to display
*/
private function get_sections_to_display(course_modinfo $modinfo): array {
$singlesectionid = $this->format->get_sectionid();
if ($singlesectionid) {
return [
$modinfo->get_section_info_by_id($singlesectionid),
];
}
return $modinfo->get_listed_section_info_all();
}
}
@@ -0,0 +1,164 @@
<?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 course format output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use moodle_url;
use renderable;
use stdClass;
/**
* Base class to render a course add section buttons.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class addsection implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/**
* Constructor.
*
* @param course_format $format the course format
*/
public function __construct(course_format $format) {
$this->format = $format;
}
/**
* 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 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(['moodle/course:update'])) {
return new stdClass();
}
$format = $this->format;
$course = $format->get_course();
$options = $format->get_format_options();
$lastsection = $format->get_last_section_number();
$maxsections = $format->get_max_sections();
// Component based formats handle add section button in the frontend.
$show = ($lastsection < $maxsections) || $format->supports_components();
$supportsnumsections = array_key_exists('numsections', $options);
if ($supportsnumsections) {
$data = $this->get_num_sections_data($output, $lastsection, $maxsections);
} else if (course_get_format($course)->uses_sections() && $show) {
$data = $this->get_add_section_data($output, $lastsection, $maxsections);
}
if (count((array)$data)) {
$data->showaddsection = true;
}
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();
$addstring = $format->get_format_string('addsection');
$params = ['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()];
$singlesection = $this->format->get_sectionnum();
if ($singlesection) {
$params['sectionreturn'] = $singlesection;
}
$data->addsections = (object) [
'url' => new moodle_url('/course/changenumsections.php', $params),
'title' => $addstring,
'newsection' => $maxsections - $lastsection,
];
return $data;
}
}
@@ -0,0 +1,69 @@
<?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;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
/**
* Course bulk edit mode toggler button.
*
* @package core_courseformat
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class bulkedittoggler implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var core_courseformat\base the course format class */
protected $format;
/**
* Constructor.
*
* @param course_format $format the course format
*/
public function __construct(course_format $format) {
$this->format = $format;
}
/**
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
*
* @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) {
$section = optional_param('section', 0, PARAM_INT);
$format = $this->format;
$course = $format->get_course();
$data = (object)[
'id' => $course->id,
'coursename' => format_string($course->fullname),
];
if ($section) {
$data->sectionname = get_string('sectionname', "format_$course->format");
$data->sectiontitle = get_section_name($course, $section);
}
return $data;
}
}
@@ -0,0 +1,200 @@
<?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;
use core\moodlenet\utilities;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use stdClass;
/**
* Contains the bulk editor tools bar.
*
* @package core_courseformat
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class bulkedittools implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var core_courseformat\base the course format class */
protected $format;
/**
* Constructor.
*
* @param course_format $format the course format
*/
public function __construct(course_format $format) {
$this->format = $format;
}
/**
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
*
* @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 {
$format = $this->format;
$course = $format->get_course();
$data = (object)[
'id' => $course->id,
'actions' => $this->get_toolbar_actions(),
];
$data->hasactions = !empty($data->actions);
return $data;
}
/**
* Get the toolbar actions.
* @return array the array of buttons
*/
protected function get_toolbar_actions(): array {
return array_merge(
array_values($this->section_control_items()),
array_values($this->cm_control_items()),
);
}
/**
* Generate the bulk edit control items of a course module.
*
* Format plugins can override the method to add or remove elements
* from the toolbar.
*
* @return array of edit control items
*/
protected function cm_control_items(): array {
global $CFG, $USER;
$format = $this->format;
$context = $format->get_context();
$user = $USER;
$controls = [];
if (has_capability('moodle/course:activityvisibility', $context, $user)) {
$controls['availability'] = [
'icon' => 't/show',
'action' => 'cmAvailability',
'name' => get_string('availability'),
'title' => get_string('cmavailability', 'core_courseformat'),
'bulk' => 'cm',
];
}
$duplicatecapabilities = ['moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport'];
if (has_all_capabilities($duplicatecapabilities, $context, $user)) {
$controls['duplicate'] = [
'icon' => 't/copy',
'action' => 'cmDuplicate',
'name' => get_string('duplicate'),
'title' => get_string('cmsduplicate', 'core_courseformat'),
'bulk' => 'cm',
];
}
$hasmanageactivities = has_capability('moodle/course:manageactivities', $context, $user);
if ($hasmanageactivities) {
$controls['move'] = [
'icon' => 'i/dragdrop',
'action' => 'moveCm',
'name' => get_string('move'),
'title' => get_string('cmsmove', 'core_courseformat'),
'bulk' => 'cm',
];
$controls['delete'] = [
'icon' => 'i/delete',
'action' => 'cmDelete',
'name' => get_string('delete'),
'title' => get_string('cmsdelete', 'core_courseformat'),
'bulk' => 'cm',
];
}
$usercanshare = utilities::can_user_share($context, $user->id, 'course');
if ($CFG->enablesharingtomoodlenet && $usercanshare) {
$controls['sharetomoodlenet'] = [
'id' => 'cmShareToMoodleNet',
'icon' => 'i/share',
'action' => 'cmShareToMoodleNet',
'name' => get_string('moodlenet:sharetomoodlenet'),
'title' => get_string('moodlenet:sharetomoodlenet'),
'bulk' => 'cm',
];
}
return $controls;
}
/**
* Generate the bulk edit control items of a section.
*
* Format plugins can override the method to add or remove elements
* from the toolbar.
*
* @return array of edit control items
*/
protected function section_control_items(): array {
global $USER;
$format = $this->format;
$context = $format->get_context();
$sectionreturn = $format->get_sectionnum();
$user = $USER;
$controls = [];
if (has_capability('moodle/course:sectionvisibility', $context, $user)) {
$controls['availability'] = [
'icon' => 't/show',
'action' => 'sectionAvailability',
'name' => get_string('availability'),
'title' => $this->format->get_format_string('sectionsavailability'),
'bulk' => 'section',
];
}
if (!$sectionreturn && has_capability('moodle/course:movesections', $context, $user)) {
$controls['move'] = [
'icon' => 'i/dragdrop',
'action' => 'moveSection',
'name' => get_string('move', 'moodle'),
'title' => $this->format->get_format_string('sectionsmove'),
'bulk' => 'section',
];
}
$deletecapabilities = ['moodle/course:movesections', 'moodle/course:update'];
if (!$sectionreturn && has_all_capabilities($deletecapabilities, $context, $user)) {
$controls['delete'] = [
'icon' => 'i/delete',
'action' => 'deleteSection',
'name' => get_string('delete'),
'title' => $this->format->get_format_string('sectionsdelete'),
'bulk' => 'section',
];
}
return $controls;
}
}
@@ -0,0 +1,410 @@
<?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 activity list from a section.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use cm_info;
use context_course;
use core\output\named_templatable;
use core_availability\info_module;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use renderer_base;
use section_info;
use stdClass;
/**
* Base class to render a course module inside a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cm implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
private $section;
/** @var cm_info the course module instance */
protected $mod;
/** @var array optional display options */
protected $displayoptions;
/** @var string the activity name output class name */
protected $cmnameclass;
/** @var string the activity control menu class name */
protected $controlmenuclass;
/** @var string the activity availability class name */
protected $availabilityclass;
/** @var string the activity completion class name */
protected $completionclass;
/** @var string the activity visibility class name */
protected $visibilityclass;
/** @var string the activity groupmode badge class name */
protected $groupmodeclass;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
* @param array $displayoptions optional extra display options
*/
public function __construct(course_format $format, section_info $section, cm_info $mod, array $displayoptions = []) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
// Add extra display options.
$this->displayoptions = $displayoptions;
$this->load_classes();
// Get the necessary classes.
$this->cmnameclass = $format->get_output_classname('content\\cm\\cmname');
$this->controlmenuclass = $format->get_output_classname('content\\cm\\controlmenu');
$this->availabilityclass = $format->get_output_classname('content\\cm\\availability');
$this->completionclass = $format->get_output_classname('content\\cm\\completion');
$this->visibilityclass = $format->get_output_classname('content\\cm\\visibility');
$this->groupmodeclass = $format->get_output_classname('content\\cm\\groupmode');
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(renderer_base $output): stdClass {
global $PAGE;
$mod = $this->mod;
$displayoptions = $this->displayoptions;
$data = (object)[
'grouping' => $mod->get_grouping_label($displayoptions['textclasses']),
'modname' => get_string('pluginname', 'mod_' . $mod->modname),
'url' => $mod->url,
'activityname' => $mod->get_formatted_name(),
'textclasses' => $displayoptions['textclasses'],
'classlist' => [],
'cmid' => $mod->id,
'editing' => $PAGE->user_is_editing(),
'sectionnum' => $this->section->section,
];
// 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['dates'] = $this->add_dates_data($data, $output);
$haspartials['editor'] = $this->add_editor_data($data, $output);
$haspartials['groupmode'] = $this->add_groupmode_data($data, $output);
$haspartials['visibility'] = $this->add_visibility_data($data, $output);
$this->add_format_data($data, $haspartials, $output);
// Calculated fields.
if (!empty($data->url)) {
$data->hasurl = true;
}
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,
null,
$this->displayoptions
);
$data->cmname = $cmname->export_for_template($output);
$data->hasname = $cmname->has_name();
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;
$activitybadgedata = $this->mod->get_activitybadge($output);
if (!empty($activitybadgedata)) {
$data->activitybadge = $activitybadgedata;
}
return !empty($data->altcontent);
}
/**
* Add activity dates 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_dates_data(stdClass &$data, renderer_base $output): bool {
global $USER;
$course = $this->mod->get_course();
if (!$course->showactivitydates) {
return false;
}
$activitydates = \core\activity_dates::get_dates_for_module($this->mod, $USER->id);
$templatedata = new \core_course\output\activity_dates($activitydates);
$data->dates = $templatedata->export_for_template($output);
return $data->dates->hasdates;
}
/**
* 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 {
$completion = new $this->completionclass($this->format, $this->section, $this->mod);
$templatedata = $completion->export_for_template($output);
if ($templatedata) {
$data->completion = $templatedata;
return true;
}
return false;
}
/**
* 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;
$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;
}
// Special inline activity format.
if (
$this->mod->has_custom_cmlist_item() &&
!$haspartials['availability'] &&
!$haspartials['completion'] &&
!$haspartials['dates'] &&
!$haspartials['groupmode'] &&
!isset($data->modhiddenfromstudents) &&
!isset($data->modstealth) &&
!$this->format->show_editor()
) {
$data->modinline = true;
$result = true;
}
return $result;
}
/**
* 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 {
$course = $this->format->get_course();
$coursecontext = context_course::instance($course->id);
$editcaps = [];
if (has_capability('moodle/course:activityvisibility', $coursecontext)) {
$editcaps = ['moodle/course:activityvisibility'];
}
if (!$this->format->show_editor($editcaps)) {
return false;
}
$returnsection = $this->format->get_sectionnum();
// 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;
}
/**
* Add group mode 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 group mode information
*/
protected function add_groupmode_data(stdClass &$data, renderer_base $output): bool {
$groupmode = new $this->groupmodeclass($this->format, $this->section, $this->mod);
$data->groupmodeinfo = $groupmode->export_for_template($output);
return !empty($data->groupmodeinfo);
}
/**
* Add visibility 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 visibility data
*/
protected function add_visibility_data(stdClass &$data, renderer_base $output): bool {
$visibility = new $this->visibilityclass($this->format, $this->section, $this->mod);
$templatedata = $visibility->export_for_template($output);
if ($templatedata) {
$data->visibility = $templatedata;
return true;
}
return false;
}
/**
* Returns the CSS classes for the activity name/content
*
*/
protected function load_classes() {
$mod = $this->mod;
$linkclasses = '';
$textclasses = '';
if ($mod->uservisible) {
$info = new info_module($mod);
$conditionalhidden = !$info->is_available_for_all();
$accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
has_capability('moodle/course:viewhiddenactivities', $mod->context);
if ($accessiblebutdim && $conditionalhidden) {
$linkclasses .= ' conditionalhidden';
$textclasses .= ' conditionalhidden';
}
}
$this->displayoptions['linkclasses'] = $linkclasses;
$this->displayoptions['textclasses'] = $textclasses;
$this->displayoptions['onclick'] = htmlspecialchars_decode($mod->onclick, ENT_QUOTES);;
}
/**
* Get the activity link classes.
*
* @return string the activity link classes.
*/
public function get_link_classes(): string {
return $this->displayoptions['linkclasses'] ?? '';
}
/**
* Get the activity text/description classes.
*
* @return string the activity text classes.
*/
public function get_text_classes(): string {
return $this->displayoptions['textclasses'] ?? '';
}
/**
* Get the activity onclick code.
*
* @return string the activity onclick.
*/
public function get_onclick_code(): string {
return $this->displayoptions['onclick'];
}
}
@@ -0,0 +1,159 @@
<?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 activity availability information.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use core_courseformat\output\local\content\section\availability as section_avalability;
use cm_info;
use core_courseformat\base as course_format;
use section_info;
use stdClass;
use core_availability\info_module;
use core_availability\info;
/**
* Base class to render a course module availability inside a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class availability extends section_avalability {
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
protected $section;
/** @var cm_info the course module instance */
protected $mod;
/** @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.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
* @param array $displayoptions optional extra display options
*/
public function __construct(course_format $format, section_info $section, cm_info $mod, array $displayoptions = []) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
$this->hasavailabilityname = 'hasmodavailability';
}
/**
* 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 array the availability data.
*/
protected function get_info(\renderer_base $output): array {
if (!$this->mod->is_visible_on_course_page()) {
// Nothing to be displayed to the user.
return [];
}
if (!$this->mod->uservisible) {
return ['info' => $this->user_availability_info($output)];
}
$editurl = new \moodle_url(
'/course/modedit.php',
['update' => $this->mod->id, 'showonly' => 'availabilityconditionsheader']
);
return ['editurl' => $editurl->out(false), 'info' => $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 = [];
$info[] = $this->get_availability_data($output, $this->mod->availableinfo, '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)) {
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';
}
$info[] = $this->get_availability_data($output, $fullinfo, $hidinfoclass);
return $info;
}
}
@@ -0,0 +1,106 @@
<?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 activity icon.
*
* @package core_courseformat
* @copyright 2023 Mikel Martin <mikel@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use cm_info;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use stdClass;
/**
* Base class to render a course module icon.
*
* @package core_courseformat
* @copyright 2023 Mikel Martin <mikel@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cmicon implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var cm_info the course module instance */
protected $mod;
/**
* Constructor.
*
* @param course_format $format the course format
* @param cm_info $mod the course module ionfo
*/
public function __construct(
course_format $format,
cm_info $mod,
) {
$this->format = $format;
$this->mod = $mod;
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): array {
$mod = $this->mod;
if (!$this->is_icon_visible()) {
// Nothing to be displayed to the user.
return [];
}
$iconurl = $mod->get_icon_url();
$iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter';
$isbranded = component_callback('mod_' . $mod->modname, 'is_branded', [], false);
$data = [
'uservisible' => $mod->uservisible,
'url' => $mod->url,
'icon' => $iconurl,
'iconclass' => $iconclass,
'modname' => $mod->modname,
'pluginname' => get_string('pluginname', 'mod_' . $mod->modname),
'showtooltip' => $this->format->show_editor(),
'purpose' => plugin_supports('mod', $mod->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER),
'branded' => $isbranded,
];
return $data;
}
/**
* Return if the activity has a visible icon.
*
* @return bool if the icon should be shown.
*/
public function is_icon_visible(): bool {
return $this->mod->is_visible_on_course_page() && $this->mod->url;
}
}
@@ -0,0 +1,158 @@
<?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 activity name inplace editable.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use cm_info;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a course module inplace editable header.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cmname implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
private $section;
/** @var cm_info the course module instance */
protected $mod;
/** @var array optional display options */
protected $displayoptions;
/** @var string the activity title output class name */
protected $titleclass;
/** @var string the activity icon output class name */
protected $iconclass;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
* @param null $unused This parameter has been deprecated since 4.1 and should not be used anymore.
* @param array $displayoptions optional extra display options
*/
public function __construct(
course_format $format,
section_info $section,
cm_info $mod,
?bool $unused = null,
array $displayoptions = []
) {
if ($unused !== null) {
debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER);
}
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
// Get the necessary classes.
$this->titleclass = $format->get_output_classname('content\\cm\\title');
$this->iconclass = $format->get_output_classname('content\\cm\\cmicon');
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): array {
$mod = $this->mod;
$displayoptions = $this->displayoptions;
if (!$this->has_name()) {
// Nothing to be displayed to the user.
return [];
}
$data = [
'url' => $mod->url,
'modname' => $mod->modname,
'textclasses' => $displayoptions['textclasses'] ?? '',
'activityicon' => $this->get_icon_data($output),
'activityname' => $this->get_title_data($output),
];
return $data;
}
/**
* Get the title data.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
protected function get_title_data(\renderer_base $output): array {
$title = new $this->titleclass(
$this->format,
$this->section,
$this->mod,
$this->displayoptions
);
return (array) $title->export_for_template($output);
}
/**
* Get the icon data.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
protected function get_icon_data(\renderer_base $output): array {
$icon = new $this->iconclass(
$this->format,
$this->mod,
);
return (array) $icon->export_for_template($output);
}
/**
* Return if the activity has a visible name.
*
* @return bool if the title is visible.
*/
public function has_name(): bool {
return $this->mod->is_visible_on_course_page() && $this->mod->url;
}
}
@@ -0,0 +1,122 @@
<?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\cm;
use cm_info;
use core_course\output\activity_completion;
use section_info;
use renderable;
use stdClass;
use core\output\named_templatable;
use core\output\local\dropdown\dialog as dropdown_dialog;
use core_completion\cm_completion_details;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
/**
* Base class to render course module completion.
*
* @package core_courseformat
* @copyright 2023 Mikel Martin <mikel@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class completion implements named_templatable, renderable {
use courseformat_named_templatable;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
*/
public function __construct(
protected course_format $format,
protected section_info $section,
protected cm_info $mod,
) {
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): ?stdClass {
global $USER;
$course = $this->mod->get_course();
$showcompletionconditions = $course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS;
$completiondetails = cm_completion_details::get_instance($this->mod, $USER->id, $showcompletionconditions);
$showcompletioninfo = $completiondetails->has_completion() &&
($showcompletionconditions || $completiondetails->show_manual_completion());
if (!$showcompletioninfo) {
return null;
}
$completion = new activity_completion($this->mod, $completiondetails);
$completiondata = $completion->export_for_template($output);
if ($completiondata->isautomatic || ($completiondata->ismanual && !$completiondata->istrackeduser)) {
$completiondata->completiondialog = $this->get_completion_dialog($output, $completiondata);
}
return $completiondata;
}
/**
* Get the completion dialog.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param stdClass $completioninfo the completion info
* @return array the completion dialog exported for template
*/
private function get_completion_dialog(\renderer_base $output, stdClass $completioninfo): array {
global $PAGE;
$editurl = new \moodle_url(
'/course/modedit.php',
['update' => $this->mod->id, 'showonly' => 'activitycompletionheader']
);
$completioninfo->editurl = $editurl->out(false);
$completioninfo->editing = $PAGE->user_is_editing();
$completioninfo->hasconditions = $completioninfo->ismanual || count($completioninfo->completiondetails) > 0;
$dialogcontent = $output->render_from_template('core_courseformat/local/content/cm/completion_dialog', $completioninfo);
$buttoncontent = get_string('completionmenuitem', 'completion');
$buttonclass = '';
if ($completioninfo->istrackeduser) {
$buttoncontent = get_string('todo', 'completion');
if ($completioninfo->overallcomplete) {
$buttoncontent = $output->pix_icon('i/checked', '') . " " . get_string('completion_manual:done', 'core_course');
$buttonclass = 'btn-success';
}
}
$completiondialog = new dropdown_dialog($buttoncontent, $dialogcontent, [
'classes' => 'completion-dropdown',
'buttonclasses' => 'btn btn-sm dropdown-toggle icon-no-margin ' . $buttonclass,
'dropdownposition' => dropdown_dialog::POSITION['end'],
]);
return $completiondialog->export_for_template($output);
}
}
@@ -0,0 +1,172 @@
<?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 activity control menu.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use action_menu;
use action_menu_link;
use cm_info;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a course module menu inside a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class controlmenu implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
private $section;
/** @var action_menu the activity aciton menu */
protected $menu;
/** @var cm_info the course module instance */
protected $mod;
/** @var array optional display options */
protected $displayoptions;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module info
* @param array $displayoptions optional extra display options
*/
public function __construct(course_format $format, section_info $section, cm_info $mod, array $displayoptions = []) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$mod = $this->mod;
$menu = $this->get_action_menu($output);
if (empty($menu)) {
return new stdClass();
}
$data = (object)[
'menu' => $menu->export_for_template($output),
'hasmenu' => true,
'id' => $mod->id,
];
// After icons.
if (!empty($mod->afterediticons)) {
$data->afterediticons = $mod->afterediticons;
}
return $data;
}
/**
* Generate the action menu element.
*
* This method is public in case some block needs to modify the menu before output it.
* @param \renderer_base $output typically, the renderer that's calling this function
* @return action_menu|null the activity action menu
*/
public function get_action_menu(\renderer_base $output): ?action_menu {
if (!empty($this->menu)) {
return $this->menu;
}
$mod = $this->mod;
$controls = $this->cm_control_items();
if (empty($controls)) {
return null;
}
// Convert control array into an action_menu.
$menu = new action_menu();
$menu->set_kebab_trigger(get_string('edit'));
$menu->attributes['class'] .= ' section-cm-edit-actions commands';
// Prioritise the menu ahead of all other actions.
$menu->prioritise = true;
$ownerselector = $this->displayoptions['ownerselector'] ?? '#module-' . $mod->id;
$menu->set_owner_selector($ownerselector);
foreach ($controls as $control) {
if ($control instanceof action_menu_link) {
$control->add_class('cm-edit-action');
}
$menu->add($control);
}
$this->menu = $menu;
return $menu;
}
/**
* Generate the edit control items of a course module.
*
* This method uses course_get_cm_edit_actions function to get the cm actions.
* However, format plugins can override the method to add or remove elements
* from the menu.
*
* @return array of edit control items
*/
protected function cm_control_items() {
$format = $this->format;
$mod = $this->mod;
$sectionreturn = $format->get_sectionnum();
if (!empty($this->displayoptions['disableindentation']) || !$format->uses_indentation()) {
$indent = -1;
} else {
$indent = $mod->indent;
}
return course_get_cm_edit_actions($mod, $indent, $sectionreturn);
}
}
@@ -0,0 +1,210 @@
<?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\cm;
use cm_info;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use core\output\named_templatable;
use core\output\choicelist;
use core\output\local\dropdown\status;
use pix_icon;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render an activity group mode badge.
*
* @package core_courseformat
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class groupmode implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
private $section;
/** @var cm_info the course module instance */
protected $mod;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
*/
public function __construct(
course_format $format,
section_info $section,
cm_info $mod,
) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
}
/**
* 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 {
if (!$this->format->show_groupmode($this->mod)) {
return null;
}
$usecomponents = $this->format->supports_components();
if ($this->format->show_editor() && $usecomponents && !$this->mod->coursegroupmodeforce) {
return $this->build_editor_data($output);
}
// If the group mode is not editable, the no groups badge is not displayed.
if ($this->mod->effectivegroupmode === NOGROUPS) {
return null;
}
return $this->build_static_data($output);
}
/**
* Build the data for the static badge.
* @param \renderer_base $output
* @return stdClass
*/
protected function build_static_data(\renderer_base $output): stdClass {
switch ($this->mod->effectivegroupmode) {
case SEPARATEGROUPS:
$groupalt = get_string('groupsseparate', 'group');
$groupicon = $this->get_action_icon('cmSeparateGroups', $groupalt);
break;
case VISIBLEGROUPS:
$groupalt = get_string('groupsvisible', 'group');
$groupicon = $this->get_action_icon('cmVisibleGroups', $groupalt);
break;
case NOGROUPS:
default:
$groupalt = get_string('groupsnone', 'group');
$groupicon = $this->get_action_icon('cmNoGroups', $groupalt);
break;
}
$data = (object) [
'groupicon' => $output->render($groupicon),
'groupalt' => $groupalt,
'isInteractive' => false,
];
return $data;
}
/**
* Build the data for the interactive dropdown.
* @param \renderer_base $output
* @return stdClass
*/
protected function build_editor_data(\renderer_base $output): stdClass {
$choice = $this->get_choice_list();
$result = $this->get_dropdown_data($output, $choice);
$result->autohide = ($this->mod->effectivegroupmode === NOGROUPS);
return $result;
}
/**
* Build the data for the interactive dropdown.
* @param \renderer_base $output
* @param choicelist $choice the choice list
* @return stdClass
*/
protected function get_dropdown_data(\renderer_base $output, choicelist $choice): stdClass {
$buttondata = $this->build_static_data($output);
$dropdown = new status(
$buttondata->groupicon,
$choice,
['dialogwidth' => status::WIDTH['big']],
);
$dropdown->set_dialog_width(status::WIDTH['small']);
$dropdown->set_position(status::POSITION['end']);
return (object) [
'isInteractive' => true,
'groupicon' => $buttondata->groupicon,
'groupalt' => $buttondata->groupalt,
'dropwdown' => $dropdown->export_for_template($output),
];
}
/**
* Create a choice list for the dropdown.
* @return choicelist the choice list
*/
public function get_choice_list(): choicelist {
$choice = new choicelist();
$choice->add_option(
NOGROUPS,
get_string('groupsnone', 'group'),
$this->get_option_data(null, 'cmNoGroups', $this->mod->id)
);
$choice->add_option(
SEPARATEGROUPS,
get_string('groupsseparate', 'group'),
$this->get_option_data('groupsseparate', 'cmSeparateGroups', $this->mod->id)
);
$choice->add_option(
VISIBLEGROUPS,
get_string('groupsvisible', 'group'),
$this->get_option_data('groupsvisible', 'cmVisibleGroups', $this->mod->id)
);
$choice->set_selected_value($this->mod->effectivegroupmode);
return $choice;
}
/**
* Get the data for the option.
* @param string|null $name the name of the option
* @param string $action the state action of the option
* @param int $id the id of the module
* @return array
*/
private function get_option_data(?string $name, string $action, int $id): array {
return [
'description' => ($name) ? get_string("groupmode_{$name}_help", 'group') : null,
// The dropdown icons are decorative, so we don't need to provide alt text.
'icon' => $this->get_action_icon($action),
'extras' => [
'data-id' => $id,
'data-action' => $action,
]
];
}
/**
* Get the group mode icon.
* @param string $groupmode the group mode
* @param string $groupalt the alt text
* @return pix_icon
*/
protected function get_action_icon(string $groupmode, string $groupalt = ''): pix_icon {
$icons = [
'cmNoGroups' => 'i/groupn',
'cmSeparateGroups' => 'i/groups',
'cmVisibleGroups' => 'i/groupv',
];
return new pix_icon($icons[$groupmode], $groupalt);
}
}
@@ -0,0 +1,236 @@
<?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 activity title.
*
* This class is usually rendered inside the cmname inplace editable.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use cm_info;
use core\output\inplace_editable;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_text;
use lang_string;
use renderable;
use section_info;
use stdClass;
use core_external\external_api;
use context_module;
/**
* Base class to render a course module title inside a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class title extends inplace_editable implements named_templatable, renderable {
/** @var course_format the course format */
protected $format;
/** @var section_info the section object */
private $section;
/** @var cm_info the course module instance */
protected $mod;
/** @var array optional display options */
protected $displayoptions;
/** @var editable if the title is editable */
protected $editable;
/** @var displaytemplate the default display template */
protected $displaytemplate = 'core_courseformat/local/content/cm/title';
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
* @param array $displayoptions optional extra display options
* @param bool|null $editable force editable value
*/
public function __construct(
course_format $format,
section_info $section,
cm_info $mod,
array $displayoptions = [],
?bool $editable = null
) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
// Usually displayoptions are loaded in the main cm output. However when the user uses the inplace editor
// the cmname output does not calculate the css classes.
$this->displayoptions = $this->load_display_options($displayoptions);
if ($editable === null) {
$editable = $format->show_editor();
}
$this->editable = $editable;
// Setup inplace editable.
parent::__construct(
'core_course',
'activityname',
$mod->id,
$this->editable,
$mod->name,
$mod->name,
new lang_string('edittitle'),
new lang_string('newactivityname', '', $mod->get_formatted_name())
);
}
/**
* Get the name of the template to use for this templatable.
*
* @param \renderer_base $renderer The renderer requesting the template name
* @return string
*/
public function get_template_name(\renderer_base $renderer): string {
return 'core/inplace_editable';
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): array {
// Inplace editable uses pre-rendered elements and does not allow line beaks in the UI value.
$this->displayvalue = str_replace("\n", "", $this->get_title_displayvalue());
if (trim($this->displayvalue) == '') {
$this->editable = false;
}
return parent::export_for_template($output);
}
/**
* Return the title template data to be used inside the inplace editable.
*
*/
protected function get_title_displayvalue(): string {
global $PAGE;
// Inplace editable uses core renderer by default. However, course elements require
// the format specific renderer.
$courseoutput = $this->format->get_renderer($PAGE);
$mod = $this->mod;
$data = (object)[
'url' => $mod->url,
'instancename' => $mod->get_formatted_name(),
'uservisible' => $mod->uservisible,
'linkclasses' => $this->displayoptions['linkclasses'],
];
// File type after name, for alphabetic lists (screen reader).
if (strpos(
core_text::strtolower($data->instancename),
core_text::strtolower($mod->modfullname)
) === false) {
$data->altname = get_accesshide(' ' . $mod->modfullname);
}
// Get on-click attribute value if specified and decode the onclick - it
// has already been encoded for display (puke).
$data->onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES);
return $courseoutput->render_from_template(
$this->displaytemplate,
$data
);
}
/**
* Load the required display options if not present already.
*
* In most cases, display options are provided as a param when creating the
* object. However, inplace_editable and some blocks does not know all of them as it is
* called in a webservice and we need to ensure it is calculated.
*
* @param array $displayoptions the provided dispaly options
* @return array the full display options list
*/
protected function load_display_options(array $displayoptions): array {
$format = $this->format;
$mod = $this->mod;
if (
isset($displayoptions['linkclasses']) &&
isset($displayoptions['textclasses']) &&
isset($displayoptions['onclick'])
) {
return $displayoptions;
}
$cmclass = $format->get_output_classname('content\\cm');
$cmoutput = new $cmclass(
$format,
$this->section,
$mod,
$displayoptions
);
$displayoptions['linkclasses'] = $cmoutput->get_link_classes();
$displayoptions['textclasses'] = $cmoutput->get_text_classes();
$displayoptions['onclick'] = $cmoutput->get_onclick_code();
return $displayoptions;
}
/**
* Updates course module name.
*
* This method is used mainly by inplace_editable webservice.
*
* @param int $itemid course module id
* @param string $newvalue new name
* @return static
*/
public static function update($itemid, $newvalue) {
$context = context_module::instance($itemid);
// Check access.
external_api::validate_context($context);
require_capability('moodle/course:manageactivities', $context);
// Trim module name and Update value.
set_coursemodule_name($itemid, trim($newvalue));
$coursemodulerecord = get_coursemodule_from_id('', $itemid, 0, false, MUST_EXIST);
// Return instance.
$modinfo = get_fast_modinfo($coursemodulerecord->course);
$cm = $modinfo->get_cm($itemid);
$section = $modinfo->get_section_info($cm->sectionnum);
$format = course_get_format($cm->course);
return new static($format, $section, $cm, [], true);
}
}
@@ -0,0 +1,292 @@
<?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 activity availability information.
*
* @package core_courseformat
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\cm;
use action_menu_link_secondary;
use core\output\local\action_menu\subpanel as action_menu_subpanel;
use cm_info;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use core\output\choicelist;
use core\output\local\dropdown\status;
use core\output\named_templatable;
use pix_icon;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a course module availability inside a course format.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@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;
/** @var cm_info the course module instance */
protected $mod;
/**
* Constructor.
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
*/
public function __construct(course_format $format, section_info $section, cm_info $mod) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
}
/**
* 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 {
if (!$this->show_visibility()) {
return null;
}
$format = $this->format;
// In rare legacy cases, the section could be stealth (orphaned) but they are not editable.
if (!$format->show_editor()
|| !has_capability('moodle/course:activityvisibility', $this->mod->context)) {
return $this->build_static_data($output);
} else {
return $this->build_editor_data($output);
}
}
/**
* Check if the visibility is displayed.
* @return bool
*/
protected function show_visibility(): bool {
return !$this->mod->visible || $this->mod->is_stealth();
}
/**
* 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 if ($selected === 'stealth') {
return new pix_icon('t/stealth', '');
} else {
return new pix_icon('t/hide', '');
}
}
/**
* Build the data for the editor.
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass|null data context for a mustache template
*/
public function build_editor_data(\renderer_base $output): ?stdClass {
$choice = $this->get_choice_list();
return $this->get_dropdown_data($output, $choice);
}
/**
* Build the data for the interactive dropdown.
* @param \renderer_base $output
* @param choicelist $choice the choice list
* @return stdClass
*/
protected function get_dropdown_data(
\renderer_base $output,
choicelist $choice,
): stdClass {
$badgetext = $output->sr_text(get_string('availability'));
if (!$this->mod->visible) {
$badgetext .= get_string('hiddenfromstudents');
$icon = $this->get_icon('hide');
} else if ($this->mod->is_stealth()) {
$badgetext .= get_string('hiddenoncoursepage');
$icon = $this->get_icon('stealth');
} else {
$badgetext .= get_string("availability_show", 'core_courseformat');
$icon = $this->get_icon('show');
}
$dropdown = new status(
$output->render($icon) . ' ' . $badgetext,
$choice,
['dialogwidth' => status::WIDTH['big']],
);
return (object) [
'isInteractive' => true,
'dropwdown' => $dropdown->export_for_template($output),
];
}
/**
* Get the availability choice list.
* @return choicelist
*/
public function get_choice_list(): choicelist {
$choice = $this->create_choice_list();
$choice->set_selected_value($this->get_selected_choice_value());
return $choice;
}
/**
* Return the cm availability menu item.
*
* By default, the cm availability is displayed as a menu item subpanel.
* However, it can be simplified when there is only one option and
* it is not stealth (stealth require a subpanel to inform the user).
*
* @return action_menu_link_secondary|action_menu_subpanel|null
*/
public function get_menu_item(): action_menu_link_secondary|action_menu_subpanel|null {
$choice = $this->get_choice_list();
$selectableoptions = $choice->get_selectable_options();
if (count($selectableoptions) === 0) {
return null;
}
// Visible activities in hidden sections are always considered stealth.
if ($this->section->visible && count($selectableoptions) === 1) {
$option = reset($selectableoptions);
$actionlabel = $option->value === 'show' ? 'modshow' : 'modhide';
return new action_menu_link_secondary(
$option->url,
$option->icon,
get_string($actionlabel, 'moodle'),
$choice->get_option_extras($option->value)
);
}
return new action_menu_subpanel(
get_string('availability', 'moodle'),
$choice,
['class' => 'editing_availability'],
new pix_icon('t/hide', '', 'moodle', ['class' => 'iconsmall'])
);
}
/**
* Get the selected choice value depending on the course, section and stealth settings.
* @return string
*/
protected function get_selected_choice_value(): string {
if (!$this->mod->visible) {
return 'hide';
}
if (!$this->mod->is_stealth()) {
return 'show';
}
if (!$this->section->visible) {
// All visible activities in a hidden sections are considered stealth
// but they don't use the stealth attribute for it. It is just implicit.
return 'show';
}
return 'stealth';
}
/**
* Create a choice list for the dropdown.
* @return choicelist the choice list
*/
protected function create_choice_list(): choicelist {
global $CFG;
$choice = new choicelist();
if ($this->section->visible || $this->mod->has_view()) {
$label = $this->section->visible ? 'show' : 'stealth';
$choice->add_option(
'show',
get_string("availability_{$label}", 'core_courseformat'),
$this->get_option_data($label, 'cmShow')
);
}
$choice->add_option(
'hide',
get_string('availability_hide', 'core_courseformat'),
$this->get_option_data('hide', 'cmHide')
);
if ($CFG->allowstealth && $this->format->allow_stealth_module_visibility($this->mod, $this->section)) {
$choice->add_option(
'stealth',
get_string('availability_stealth', 'core_courseformat'),
$this->get_option_data('stealth', 'cmStealth')
);
}
return $choice;
}
/**
* 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 {
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' => $this->format->get_non_ajax_cm_action_url($action, $this->mod),
'extras' => [
'data-id' => $this->mod->id,
'data-action' => $action,
]
];
}
/**
* Build the static badges data.
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass|null data context for a mustache template
*/
public function build_static_data(\renderer_base $output): ?stdClass {
$data = (object) [
'isInteractive' => false,
];
if (!$this->mod->visible) {
$data->modhiddenfromstudents = true;
} else if ($this->mod->is_stealth()) {
$data->modstealth = true;
}
return $data;
}
}
@@ -0,0 +1,102 @@
<?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 frontpage section displayer.
*
* The frontpage has a different wat of rendering the main topic.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use context_course;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use moodle_url;
use renderable;
use section_info;
use stdClass;
/**
* Represents the frontpage section 1.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class frontpagesection implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
protected $section;
/** @var string the section output class name */
protected $sectionclass;
/**
* 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;
// Get the necessary classes.
$this->sectionclass = $format->get_output_classname('content\\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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
global $USER;
$format = $this->format;
$section = $this->section;
$sectionoutput = new $this->sectionclass($format, $section);
$sectionoutput->hide_controls();
if (trim($section->name ?? '') == '') {
$sectionoutput->hide_title();
}
$data = (object)[
'sections' => [$sectionoutput->export_for_template($output)],
];
if ($format->show_editor(['moodle/course:update'])) {
$data->showsettings = true;
$data->settingsurl = new moodle_url('/course/editsection.php', ['id' => $section->id]);
}
return $data;
}
}
@@ -0,0 +1,371 @@
<?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 course format output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use context_course;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use renderer_base;
use section_info;
use stdClass;
/**
* Base class to render a course section.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class section implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format */
protected $format;
/** @var section_info the section info */
protected $section;
/** @var section header output class */
protected $headerclass;
/** @var cm list output class */
protected $cmlistclass;
/** @var section summary output class */
protected $summaryclass;
/** @var activities summary output class */
protected $cmsummaryclass;
/** @var section control menu output class */
protected $controlclass;
/** @var section availability output class */
protected $availabilityclass;
/** @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;
/** @var bool if the title is hidden for some reason */
protected $hidecontrols = false;
/** @var bool if the section is considered stealth */
protected $isstealth = false;
/** @var string control menu class. */
protected $controlmenuclass;
/**
* 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;
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');
$this->cmlistclass = $format->get_output_classname('content\\section\\cmlist');
$this->summaryclass = $format->get_output_classname('content\\section\\summary');
$this->cmsummaryclass = $format->get_output_classname('content\\section\\cmsummary');
$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');
}
/**
* Hide the section title.
*
* This is used on blocks or in the home page where an isolated section is displayed.
*/
public function hide_title(): void {
$this->hidetitle = true;
}
/**
* Hide the section controls.
*
* This is used on blocks or in the home page where an isolated section is displayed.
*/
public function hide_controls(): void {
$this->hidecontrols = true;
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(renderer_base $output): stdClass {
global $USER, $PAGE;
$format = $this->format;
$course = $format->get_course();
$section = $this->section;
$summary = new $this->summaryclass($format, $section);
$data = (object)[
'num' => $section->section ?? '0',
'id' => $section->id,
'sectionreturnid' => $format->get_sectionnum(),
'insertafter' => false,
'summary' => $summary->export_for_template($output),
'highlightedlabel' => $format->get_section_highlighted_name(),
'sitehome' => $course->id == SITEID,
'editing' => $PAGE->user_is_editing(),
'displayonesection' => ($course->id != SITEID && !is_null($format->get_sectionid())),
];
$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;
}
$section = $this->section;
$format = $this->format;
$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.
if ($section->section != 0 && $section->section == $format->get_sectionnum()) {
$data->singleheader = $headerdata;
} else {
$data->header = $headerdata;
}
return true;
}
/**
* 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_sectionnum() &&
$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;
// 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;
$course = $this->format->get_course();
$context = context_course::instance($course->id);
if (has_capability('moodle/course:viewhiddensections', $context, $USER)) {
$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;
}
/**
* 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 {
$course = $this->format->get_course();
$coursecontext = context_course::instance($course->id);
$editcaps = [];
if (has_capability('moodle/course:sectionvisibility', $coursecontext)) {
$editcaps = ['moodle/course:sectionvisibility'];
}
if (!$this->format->show_editor($editcaps)) {
return false;
}
// In a single section page the control menu is located in the page header.
if (empty($this->hidecontrols) && $this->format->get_sectionid() != $this->section->id) {
$controlmenu = new $this->controlmenuclass($this->format, $this->section);
$data->controlmenu = $controlmenu->export_for_template($output);
}
if (!$this->isstealth) {
$data->cmcontrols = $output->course_section_add_cm_control(
$course,
$this->section->section,
$this->format->get_sectionnum()
);
}
return true;
}
/**
* 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 = $this->is_section_collapsed();
if ($format->is_section_current($section)) {
$data->iscurrent = true;
$data->currentlink = get_accesshide(
get_string('currentsection', 'format_' . $format->get_format())
);
}
return true;
}
/**
* Returns true if the current section should be shown collapsed.
*
* @return bool
*/
protected function is_section_collapsed(): bool {
global $PAGE;
$contentcollapsed = false;
$preferences = $this->format->get_sections_preferences();
if (isset($preferences[$this->section->id])) {
$sectionpreferences = $preferences[$this->section->id];
if (!empty($sectionpreferences->contentcollapsed)) {
$contentcollapsed = true;
}
}
// No matter if the user's preference was to collapse the section or not: If the
// 'expandsection' parameter has been specified, it will be shown uncollapsed.
$expandsection = $PAGE->url->get_param('expandsection');
if ($expandsection !== null && $this->section->section == $expandsection) {
$contentcollapsed = false;
}
return $contentcollapsed;
}
}
@@ -0,0 +1,281 @@
<?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 availability output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use context_course;
use core_availability_multiple_messages;
use core\output\named_templatable;
use core_availability\info;
use core_availability\info_section;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render section availability.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class availability implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @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;
/** @var int Availability excerpt text max size treshold */
protected const AVAILABILITY_EXCERPT_MAXSIZE = 100;
/**
* 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;
$this->hasavailabilityname = 'hasavailability';
}
/**
* 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 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) $this->get_info($output);
$attributename = $this->hasavailabilityname;
$data->$attributename = !empty($data->info);
$this->data = $data;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* If section is not visible, display the message about that ('Not available
* until...', that sort of thing). Otherwise, returns blank.
*
* For users with the ability to view hidden sections, it shows the
* information even though you can view the section and also may include
* slightly fuller information (so that teachers can tell when sections
* are going to be unavailable etc). This logic is the same as for
* activities.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdclass data context for a mustache template
*/
protected function get_info(\renderer_base $output): array {
global $CFG, $USER;
$section = $this->section;
$context = context_course::instance($section->course);
$canviewhidden = has_capability('moodle/course:viewhiddensections', $context, $USER);
$editurl = new \moodle_url(
'/course/editsection.php',
['id' => $this->section->id, 'showonly' => 'availabilityconditions']
);
$info = ['editurl' => $editurl->out(false)];
if (!$section->visible) {
return [];
} else if (!$section->uservisible) {
if ($section->availableinfo) {
// Note: We only get to this function if availableinfo is non-empty,
// so there is definitely something to print.
$info['info'] = $this->get_availability_data($output, $section->availableinfo, 'isrestricted');
}
} else if ($canviewhidden && !empty($CFG->enableavailability)) {
// Check if there is an availability restriction.
$ci = new info_section($section);
$fullinfo = $ci->get_full_information();
if ($fullinfo) {
$info['info'] = $this->get_availability_data($output, $fullinfo, 'isrestricted isfullinfo');
}
}
return $info;
}
/**
* Get the basic availability information data.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param string|core_availability_multiple_messages $availabilityinfo the avalability info
* @param string $additionalclasses additional css classes
* @return stdClass the availability information data
*/
protected function get_availability_data($output, $availabilityinfo, $additionalclasses = ''): stdClass {
// At this point, availabilityinfo is either a string or a renderable. We need to handle both cases in a different way.
if (is_string($availabilityinfo)) {
$data = $this->availability_info_from_string($output, $availabilityinfo);
} else {
$data = $this->availability_info_from_output($output, $availabilityinfo);
}
$data->classes = $additionalclasses;
$additionalclasses = array_filter(explode(' ', $additionalclasses));
if (in_array('ishidden', $additionalclasses)) {
$data->ishidden = 1;
} else if (in_array('isstealth', $additionalclasses)) {
$data->isstealth = 1;
} else if (in_array('isrestricted', $additionalclasses)) {
$data->isrestricted = 1;
if (in_array('isfullinfo', $additionalclasses)) {
$data->isfullinfo = 1;
}
}
return $data;
}
/**
* Generate the basic availability information data from a string.
* Just shorten availability text to generate the excerpt text.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param string $availabilityinfo the avalability info
* @return stdClass the availability information data
*/
protected function availability_info_from_string(\renderer_base $output, string $availabilityinfo): stdClass {
$course = $this->format->get_course();
$text = info::format_info($availabilityinfo, $course);
$data = ['text' => $text];
if (strlen(html_to_text($text, 0, false)) > self::AVAILABILITY_EXCERPT_MAXSIZE) {
$data['excerpt'] = shorten_text($text, self::AVAILABILITY_EXCERPT_MAXSIZE);
}
return (object) $data;
}
/**
* Generate the basic availability information data from a renderable.
* Use the header and the first item to generate the excerpt text.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param core_availability_multiple_messages $availabilityinfo the avalability info
* @return stdClass the availability information data
*/
protected function availability_info_from_output(
\renderer_base $output,
core_availability_multiple_messages $availabilityinfo
): stdClass {
$course = $this->format->get_course();
$renderable = new \core_availability\output\availability_info($availabilityinfo);
// We need to export_for_template() instead of directly render, to reuse the info for both 'text' and 'excerpt'.
$info = $renderable->export_for_template($output);
$text = $output->render_from_template('core_availability/availability_info', $info);
$data = ['text' => info::format_info($text, $course)];
if (!empty($info->items)) {
$excerpttext = $info->header . ' ' . $info->items[0]->header;
$data['excerpt'] = info::format_info($excerpttext, $course);
}
return (object) $data;
}
/**
* Generate the basic availability information data.
*
* @deprecated since Moodle 4.3 MDL-78204. Please use {@see self::get_availability_data} instead.
* @todo MDL-78489 This will be deleted in Moodle 4.7.
* @param string $text the formatted avalability text
* @param string $additionalclasses additional css classes
* @return stdClass the availability information data
*/
protected function availability_info($text, $additionalclasses = ''): stdClass {
debugging('Use of ' . __FUNCTION__ . '() have been deprecated, ' .
'please use core_courseformat\output\local\content\section\availability::get_availability_data()', DEBUG_DEVELOPER);
$data = (object)[
'text' => $text,
'classes' => $additionalclasses
];
$additionalclasses = array_filter(explode(' ', $additionalclasses));
if (in_array('ishidden', $additionalclasses)) {
$data->ishidden = 1;
} else if (in_array('isstealth', $additionalclasses)) {
$data->isstealth = 1;
} else if (in_array('isrestricted', $additionalclasses)) {
$data->isrestricted = 1;
if (in_array('isfullinfo', $additionalclasses)) {
$data->isfullinfo = 1;
}
}
return $data;
}
}
@@ -0,0 +1,113 @@
<?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 activity item from a section.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use cm_info;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use renderer_base;
use section_info;
use stdClass;
/**
* Base class to render a section activity in the activities list.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cmitem implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
protected $section;
/** @var cm_info the course module to display */
protected $mod;
/** @var array optional display options */
protected $displayoptions;
/** @var string the cm output class name */
protected $cmclass;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $mod the course module ionfo
* @param array $displayoptions optional extra display options
*/
public function __construct(course_format $format, section_info $section, cm_info $mod, array $displayoptions = []) {
$this->format = $format;
$this->section = $section;
$this->mod = $mod;
$this->displayoptions = $displayoptions;
// Get the necessary classes.
$this->cmclass = $format->get_output_classname('content\\cm');
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$format = $this->format;
$course = $format->get_course();
$mod = $this->mod;
$data = new stdClass();
$data->cms = [];
$completionenabled = $course->enablecompletion == COMPLETION_ENABLED;
$showactivityconditions = $completionenabled && $course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS;
$showactivitydates = !empty($course->showactivitydates);
// This will apply styles to the course homepage when the activity information output component is displayed.
$hasinfo = $showactivityconditions || $showactivitydates;
$item = new $this->cmclass($format, $this->section, $mod, $this->displayoptions);
return (object)[
'id' => $mod->id,
'anchor' => "module-{$mod->id}",
'module' => $mod->modname,
'extraclasses' => $mod->extraclasses,
'cmformat' => $item->export_for_template($output),
'hasinfo' => $hasinfo,
'indent' => ($format->uses_indentation()) ? $mod->indent : 0,
'groupmode' => $mod->groupmode,
];
}
}
@@ -0,0 +1,134 @@
<?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 activity list from a section.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use moodle_url;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a section activity list.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cmlist implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
protected $section;
/** @var array optional display options */
protected $displayoptions;
/** @var string the item output class name */
protected $itemclass;
/** @var optional move here output class */
protected $movehereclass;
/**
* Constructor.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param array $displayoptions optional extra display options
*/
public function __construct(course_format $format, section_info $section, array $displayoptions = []) {
$this->format = $format;
$this->section = $section;
$this->displayoptions = $displayoptions;
// Get the necessary classes.
$this->itemclass = $format->get_output_classname('content\\section\\cmitem');
}
/**
* 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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
global $USER;
$format = $this->format;
$section = $this->section;
$course = $format->get_course();
$modinfo = $format->get_modinfo();
$user = $USER;
$data = new stdClass();
$data->cms = [];
// By default, non-ajax controls are disabled but in some places like the frontpage
// it is necessary to display them. This is a temporal solution while JS is still
// optional for course editing.
$showmovehere = ismoving($course->id);
if ($showmovehere) {
$data->hascms = true;
$data->showmovehere = true;
$data->strmovefull = strip_tags(get_string("movefull", "", "'$user->activitycopyname'"));
$data->movetosectionurl = new moodle_url('/course/mod.php', ['movetosection' => $section->id, 'sesskey' => sesskey()]);
$data->movingstr = strip_tags(get_string('activityclipboard', '', $user->activitycopyname));
$data->cancelcopyurl = new moodle_url('/course/mod.php', ['cancelcopy' => 'true', 'sesskey' => sesskey()]);
}
if (empty($modinfo->sections[$section->section])) {
return $data;
}
foreach ($modinfo->sections[$section->section] as $modnumber) {
$mod = $modinfo->cms[$modnumber];
// If the old non-ajax move is necessary, we do not print the selected cm.
if ($showmovehere && $USER->activitycopy == $mod->id) {
continue;
}
if ($mod->is_visible_on_course_page()) {
$item = new $this->itemclass($format, $section, $mod, $this->displayoptions);
$data->cms[] = (object)[
'cmitem' => $item->export_for_template($output),
'moveurl' => new moodle_url('/course/mod.php', array('moveto' => $modnumber, 'sesskey' => sesskey())),
];
}
}
if (!empty($data->cms)) {
$data->hascms = true;
}
return $data;
}
}
@@ -0,0 +1,132 @@
<?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 activities summary (used for singlesection format).
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use completion_info;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a course section summary.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cmsummary implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
list($mods, $complete, $total, $showcompletion) = $this->calculate_section_stats();
$totalactivities = array_reduce($mods, fn($carry, $item) => $carry + ($item["count"] ?? 0), 0);
$data = (object)[
'showcompletion' => $showcompletion,
'total' => $total,
'complete' => $complete,
'mods' => array_values($mods),
'totalactivities' => $totalactivities,
];
$data->modprogress = get_string('progresstotal', 'completion', $data);
return $data;
}
/**
* Calculate the activities count of the current section.
*
* @return array with [[count by activity type], completed activities, total of activitites]
*/
private function calculate_section_stats(): array {
$format = $this->format;
$course = $format->get_course();
$section = $this->section;
$modinfo = $format->get_modinfo();
$completioninfo = new completion_info($course);
$mods = [];
$total = 0;
$complete = 0;
$cmids = $modinfo->sections[$section->section] ?? [];
$cancomplete = isloggedin() && !isguestuser();
$showcompletion = false;
foreach ($cmids as $cmid) {
$thismod = $modinfo->cms[$cmid];
if ($thismod->uservisible) {
if (isset($mods[$thismod->modname])) {
$mods[$thismod->modname]['name'] = $thismod->modplural;
$mods[$thismod->modname]['count']++;
} else {
$mods[$thismod->modname]['name'] = $thismod->modfullname;
$mods[$thismod->modname]['count'] = 1;
}
if ($cancomplete && $completioninfo->is_enabled($thismod) != COMPLETION_TRACKING_NONE) {
$showcompletion = true;
$total++;
$completiondata = $completioninfo->get_data($thismod, true);
if ($completiondata->completionstate == COMPLETION_COMPLETE ||
$completiondata->completionstate == COMPLETION_COMPLETE_PASS) {
$complete++;
}
}
}
}
return [$mods, $complete, $total, $showcompletion];
}
}
@@ -0,0 +1,365 @@
<?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 core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use action_menu;
use action_menu_link_secondary;
use context_course;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use moodle_url;
use pix_icon;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render section controls.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class controlmenu implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$menu = $this->get_action_menu($output);
if (empty($menu)) {
return new stdClass();
}
$data = (object)[
'menu' => $output->render($menu),
'hasmenu' => true,
'id' => $this->section->id,
];
return $data;
}
/**
* Generate the action menu element depending on the section.
*
* Sections controlled by a plugin will delegate the control menu to the plugin.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return action_menu|null the activity action menu or null if no action menu is available
*/
public function get_action_menu(\renderer_base $output): ?action_menu {
$sectiondelegate = $this->section->get_component_instance();
if ($sectiondelegate) {
return $sectiondelegate->get_section_action_menu($this->format, $this, $output);
}
return $this->get_default_action_menu($output);
}
/**
* Generate the default section action menu.
*
* This method is public in case some block needs to modify the menu before output it.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return action_menu|null the activity action menu
*/
public function get_default_action_menu(\renderer_base $output): ?action_menu {
$controls = $this->section_control_items();
if (empty($controls)) {
return null;
}
// Convert control array into an action_menu.
$menu = new action_menu();
$menu->set_kebab_trigger(get_string('edit'));
$menu->attributes['class'] .= ' section-actions';
$menu->attributes['data-sectionid'] = $this->section->id;
foreach ($controls as $value) {
$url = empty($value['url']) ? '' : $value['url'];
$icon = empty($value['icon']) ? '' : $value['icon'];
$name = empty($value['name']) ? '' : $value['name'];
$attr = empty($value['attr']) ? [] : $value['attr'];
$class = empty($value['pixattr']['class']) ? '' : $value['pixattr']['class'];
$al = new action_menu_link_secondary(
new moodle_url($url),
new pix_icon($icon, '', null, ['class' => "smallicon " . $class]),
$name,
$attr
);
$menu->add($al);
}
return $menu;
}
/**
* Generate the edit control items of a section.
*
* It is not clear this kind of controls are still available in 4.0 so, for now, this
* method is almost a clone of the previous section_control_items from the course/renderer.php.
*
* 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() {
global $USER, $PAGE;
$format = $this->format;
$section = $this->section;
$course = $format->get_course();
$sectionreturn = !is_null($format->get_sectionid()) ? $format->get_sectionnum() : null;
$user = $USER;
$usecomponents = $format->supports_components();
$coursecontext = context_course::instance($course->id);
$numsections = $format->get_last_section_number();
$isstealth = $section->section > $numsections;
$baseurl = course_get_url($course, $sectionreturn);
$baseurl->param('sesskey', sesskey());
$controls = [];
// Only show the view link if we are not already in the section view page.
if ($PAGE->pagetype !== 'course-view-section-' . $course->format) {
$controls['view'] = [
'url' => new moodle_url('/course/section.php', ['id' => $section->id]),
'icon' => 'i/viewsection',
'name' => get_string('view'),
'pixattr' => ['class' => ''],
'attr' => ['class' => 'icon view'],
];
}
if (!$isstealth && has_capability('moodle/course:update', $coursecontext, $user)) {
$params = ['id' => $section->id];
$params['sr'] = $section->section;
if (get_string_manager()->string_exists('editsection', 'format_'.$format->get_format())) {
$streditsection = get_string('editsection', 'format_'.$format->get_format());
} else {
$streditsection = get_string('editsection');
}
$controls['edit'] = [
'url' => new moodle_url('/course/editsection.php', $params),
'icon' => 'i/settings',
'name' => $streditsection,
'pixattr' => ['class' => ''],
'attr' => ['class' => 'icon edit'],
];
if ($section->section) {
$duplicatesectionurl = clone($baseurl);
$duplicatesectionurl->param('sectionid', $section->id);
$duplicatesectionurl->param('duplicatesection', 1);
if (!is_null($sectionreturn)) {
$duplicatesectionurl->param('sr', $sectionreturn);
}
$controls['duplicate'] = [
'url' => $duplicatesectionurl,
'icon' => 't/copy',
'name' => get_string('duplicate'),
'pixattr' => ['class' => ''],
'attr' => ['class' => 'icon duplicate'],
];
}
}
if ($section->section) {
$url = clone($baseurl);
if (!is_null($sectionreturn)) {
$url->param('sectionid', $format->get_sectionid());
}
if (!$isstealth) {
if (has_capability('moodle/course:sectionvisibility', $coursecontext, $user)) {
$strhidefromothers = get_string('hidefromothers', 'format_' . $course->format);
$strshowfromothers = get_string('showfromothers', 'format_' . $course->format);
if ($section->visible) { // Show the hide/show eye.
$url->param('hide', $section->section);
$controls['visibility'] = [
'url' => $url,
'icon' => 'i/show',
'name' => $strhidefromothers,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'icon editing_showhide',
'data-sectionreturn' => $sectionreturn,
'data-action' => ($usecomponents) ? 'sectionHide' : 'hide',
'data-id' => $section->id,
'data-icon' => 'i/show',
'data-swapname' => $strshowfromothers,
'data-swapicon' => 'i/hide',
],
];
} else {
$url->param('show', $section->section);
$controls['visibility'] = [
'url' => $url,
'icon' => 'i/hide',
'name' => $strshowfromothers,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'icon editing_showhide',
'data-sectionreturn' => $sectionreturn,
'data-action' => ($usecomponents) ? 'sectionShow' : 'show',
'data-id' => $section->id,
'data-icon' => 'i/hide',
'data-swapname' => $strhidefromothers,
'data-swapicon' => 'i/show',
],
];
}
}
if (!$sectionreturn && has_capability('moodle/course:movesections', $coursecontext, $user)) {
if ($usecomponents) {
// This tool will appear only when the state is ready.
$url = clone ($baseurl);
$url->param('movesection', $section->section);
$url->param('section', $section->section);
$controls['movesection'] = [
'url' => $url,
'icon' => 'i/dragdrop',
'name' => get_string('move', 'moodle'),
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'icon move waitstate',
'data-action' => 'moveSection',
'data-id' => $section->id,
],
];
}
// Legacy move up and down links for non component-based formats.
$url = clone($baseurl);
if ($section->section > 1) { // Add a arrow to move section up.
$url->param('section', $section->section);
$url->param('move', -1);
$strmoveup = get_string('moveup');
$controls['moveup'] = [
'url' => $url,
'icon' => 'i/up',
'name' => $strmoveup,
'pixattr' => ['class' => ''],
'attr' => ['class' => 'icon moveup whilenostate'],
];
}
$url = clone($baseurl);
if ($section->section < $numsections) { // Add a arrow to move section down.
$url->param('section', $section->section);
$url->param('move', 1);
$strmovedown = get_string('movedown');
$controls['movedown'] = [
'url' => $url,
'icon' => 'i/down',
'name' => $strmovedown,
'pixattr' => ['class' => ''],
'attr' => ['class' => 'icon movedown whilenostate'],
];
}
}
}
if (course_can_delete_section($course, $section)) {
if (get_string_manager()->string_exists('deletesection', 'format_'.$course->format)) {
$strdelete = get_string('deletesection', 'format_'.$course->format);
} else {
$strdelete = get_string('deletesection');
}
$params = [
'id' => $section->id,
'delete' => 1,
'sesskey' => sesskey(),
];
if (!is_null($sectionreturn)) {
$params['sr'] = $sectionreturn;
}
$url = new moodle_url(
'/course/editsection.php',
$params,
);
$controls['delete'] = [
'url' => $url,
'icon' => 'i/delete',
'name' => $strdelete,
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'icon editing_delete text-danger',
'data-action' => 'deleteSection',
'data-id' => $section->id,
],
];
}
}
if (
has_any_capability([
'moodle/course:movesections',
'moodle/course:update',
'moodle/course:sectionvisibility',
], $coursecontext)
) {
$sectionlink = new moodle_url(
'/course/section.php',
['id' => $section->id]
);
$controls['permalink'] = [
'url' => $sectionlink,
'icon' => 'i/link',
'name' => get_string('sectionlink', 'course'),
'pixattr' => ['class' => ''],
'attr' => [
'class' => 'icon',
'data-action' => 'permalink',
],
];
}
return $controls;
}
}
@@ -0,0 +1,123 @@
<?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 header format output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a section header.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class header implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$format = $this->format;
$section = $this->section;
$course = $format->get_course();
$data = (object)[
'num' => $section->section,
'id' => $section->id,
];
$data->editing = $format->show_editor();
if ($course->id == SITEID) {
$data->title = $output->section_title_without_link($section, $course);
$data->sitehome = true;
} else {
if (is_null($format->get_sectionid())) {
// All sections are displayed.
if (!$data->editing) {
$data->title = $output->section_title($section, $course);
} else {
$data->title = $output->section_title_without_link($section, $course);
}
} else {
// Only one section is displayed.
$data->displayonesection = true;
$data->title = $output->section_title_without_link($section, $course);
}
}
$coursedisplay = $format->get_course_display();
$data->headerdisplaymultipage = ($coursedisplay == COURSE_DISPLAY_MULTIPAGE);
if ($section->section > $format->get_last_section_number()) {
// Stealth sections (orphaned) has special title.
$data->title = get_string('orphanedactivitiesinsectionno', '', $section->section);
}
if (!$section->visible) {
$data->ishidden = true;
}
if (!$data->editing && $section->uservisible) {
$data->url = course_get_url($course, $section->section, ['navigation' => true]);
}
$data->name = get_section_name($course, $section);
$data->selecttext = $format->get_format_string('selectsection', $data->name);
if (!$format->get_sectionnum()) {
$data->sectionbulk = true;
}
return $data;
}
}
@@ -0,0 +1,97 @@
<?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 summary (used for multipage format).
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content\section;
use context_course;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use section_info;
use stdClass;
/**
* Base class to render a course section summary.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class summary implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
private $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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$section = $this->section;
$data = new stdClass();
if ($section->uservisible || $section->visible) {
$data->summarytext = $this->format_summary_text();
}
return $data;
}
/**
* Generate html for a section summary text
*
* @return string HTML to output.
*/
public function format_summary_text(): string {
$section = $this->section;
$context = context_course::instance($section->course);
$summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php',
$context->id, 'course', 'section', $section->id);
$options = new stdClass();
$options->noclean = true;
$options->overflowdiv = true;
return format_text($summarytext, $section->summaryformat, $options);
}
}
@@ -0,0 +1,141 @@
<?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($name, $this->section->section);
// The section page is not yet fully reactive and it needs to use the old non-ajax links.
$pagesectionid = $this->format->get_sectionid();
if ($this->section->id == $pagesectionid) {
$baseurl->param('sectionid', $pagesectionid);
$action = '';
}
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', '');
}
}
}
@@ -0,0 +1,126 @@
<?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 navigation output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use context_course;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use stdClass;
/**
* Base class to render a course add section navigation.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sectionnavigation implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var int the course displayed section */
protected $sectionno;
/** @var stdClass the calculated data to prevent calculations when rendered several times */
private $data = null;
/**
* Constructor.
*
* @param course_format $format the course format
* @param int $sectionno the section number
*/
public function __construct(course_format $format, int $sectionno) {
$this->format = $format;
$this->sectionno = $sectionno;
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
global $USER;
if ($this->data !== null) {
return $this->data;
}
$format = $this->format;
$course = $format->get_course();
$context = context_course::instance($course->id);
$modinfo = $this->format->get_modinfo();
$sections = $modinfo->get_section_info_all();
// FIXME: This is really evil and should by using the navigation API.
$canviewhidden = has_capability('moodle/course:viewhiddensections', $context, $USER);
$data = (object)[
'previousurl' => '',
'nexturl' => '',
'larrow' => $output->larrow(),
'rarrow' => $output->rarrow(),
'currentsection' => $this->sectionno,
];
$back = $this->sectionno - 1;
while ($back >= 0 && empty($data->previousurl)) {
if ($canviewhidden || $sections[$back]->uservisible) {
if (!$sections[$back]->visible) {
$data->previoushidden = true;
}
$data->previousname = get_section_name($course, $sections[$back]);
$data->previousurl = course_get_url($course, $back, ['navigation' => true]);
$data->hasprevious = true;
}
$back--;
}
$forward = $this->sectionno + 1;
$numsections = course_get_format($course)->get_last_section_number();
while ($forward <= $numsections and empty($data->nexturl)) {
if ($canviewhidden || $sections[$forward]->uservisible) {
if (!$sections[$forward]->visible) {
$data->nexthidden = true;
}
$data->nextname = get_section_name($course, $sections[$forward]);
$data->nexturl = course_get_url($course, $forward, ['navigation' => true]);
$data->hasnext = true;
}
$forward++;
}
$this->data = $data;
return $data;
}
}
@@ -0,0 +1,102 @@
<?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 selector.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output\local\content;
use core\output\named_templatable;
use core_courseformat\base as course_format;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use stdClass;
use url_select;
/**
* Represents the section selector.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sectionselector implements named_templatable, renderable {
use courseformat_named_templatable;
/** @var course_format the course format class */
protected $format;
/** @var sectionnavigation the main section navigation class */
protected $navigation;
/**
* Constructor.
*
* In the current imeplementaiton the seciton selector is almost a variation of the section navigator
* but in the 4.0 this selector will be a kind of dropdown menu. When this happens the construct params
* will change.
*
* @param course_format $format the course format
* @param sectionnavigation $navigation the current section navigation
*/
public function __construct(course_format $format, sectionnavigation $navigation) {
$this->format = $format;
$this->navigation = $navigation;
}
/**
* 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 data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$format = $this->format;
$course = $format->get_course();
$modinfo = $this->format->get_modinfo();
$data = $this->navigation->export_for_template($output);
// Add the section selector.
$sectionmenu = [];
$sectionmenu[course_get_url($course)->out(false)] = get_string('maincoursepage');
$section = 1;
$numsections = $format->get_last_section_number();
while ($section <= $numsections) {
$thissection = $modinfo->get_section_info($section);
$url = course_get_url($course, $section, ['navigation' => true]);
if ($thissection->uservisible && $url && $section != $data->currentsection) {
$sectionmenu[$url->out(false)] = get_section_name($course, $section);
}
$section++;
}
$select = new url_select($sectionmenu, '', ['' => get_string('jumpto')]);
$select->class = 'jumpmenu';
$select->formid = 'sectionmenu';
$data->selector = $output->render($select);
return $data;
}
}
@@ -0,0 +1,46 @@
<?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;
/**
* Base templatable class for coursformat templateables which are typically overridden by course formats.
*
* @package core_courseformat
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
trait courseformat_named_templatable {
/**
* Get the name of the template to use for this templatable.
*
* @param \renderer_base $renderer The renderer requesting the template name
* @return string
*/
public function get_template_name(\renderer_base $renderer): string {
$fullpath = str_replace('\\', '/', get_class($this));
$specialrenderers = '@^.*/output/(local|courseformat)/(?<template>.+)$@';
$matches = null;
if (preg_match($specialrenderers, $fullpath, $matches)) {
return "core_courseformat/local/{$matches['template']}";
}
throw new \coding_exception("Unable to determine template name for class " . get_class($this));
}
}
@@ -0,0 +1,147 @@
<?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\state;
use core_courseformat\base as course_format;
use completion_info;
use core_courseformat\sectiondelegate;
use renderer_base;
use section_info;
use cm_info;
use renderable;
use stdClass;
use core_availability\info_module;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/completionlib.php');
/**
* Contains the ajax update course module structure.
*
* @package core_courseformat
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cm implements renderable {
/**
* Constructor.
*/
public function __construct(
/** @var course_format $format The course format. */
protected course_format $format,
/** @var section_info $section The section data. */
protected section_info $section,
/** @var cm_info $cm The course module data. */
protected cm_info $cm,
/** @var bool $exportcontent False if pre-rendered cmitem HTML content must be exported. */
protected bool $exportcontent = false,
/** @var ?bool $istrackeduser If is_tracked_user is pre-computed for this CM's course, it can be provided here. */
protected ?bool $istrackeduser = null,
) {
}
/**
* Export this data so it can be used as state object in the course editor.
*
* @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 $CFG, $USER;
$format = $this->format;
$section = $this->section;
$cm = $this->cm;
$course = $format->get_course();
$data = (object)[
'id' => $cm->id,
'anchor' => "module-{$cm->id}",
'name' => \core_external\util::format_string($cm->name, $cm->context, true),
'visible' => !empty($cm->visible),
'stealth' => $cm->is_stealth(),
'sectionid' => $section->id,
'sectionnumber' => $section->section,
'uservisible' => $cm->uservisible,
'hascmrestrictions' => $this->get_has_restrictions(),
'modname' => get_string('pluginname', 'mod_' . $cm->modname),
'indent' => ($format->uses_indentation()) ? $cm->indent : 0,
'groupmode' => $cm->groupmode,
'module' => $cm->modname,
'plugin' => 'mod_' . $cm->modname,
// Activities with delegate section has some restriction to prevent structure loops.
'delegatesection' => sectiondelegate::has_delegate_class('mod_'.$cm->modname),
];
// Check the user access type to this cm.
$info = new info_module($cm);
$data->accessvisible = ($data->visible && $info->is_available_for_all());
// Add url if the activity is compatible.
$url = $cm->url;
if ($url) {
$data->url = $url->out();
}
if ($this->exportcontent) {
$data->content = $output->course_section_updated_cm_item($format, $section, $cm);
}
// Completion status.
$completioninfo = new completion_info($course);
$data->istrackeduser = $this->istrackeduser ?? $completioninfo->is_tracked_user($USER->id);
if ($data->istrackeduser && $completioninfo->is_enabled($cm)) {
$completiondata = new \core_completion\cm_completion_details($completioninfo, $cm, $USER->id, false);
$data->completionstate = $completiondata->get_overall_completion();
$data->isoverallcomplete = $completiondata->is_overall_complete();
}
$data->allowstealth = !empty($CFG->allowstealth) && $format->allow_stealth_module_visibility($cm, $section);
return $data;
}
/**
* Return if the activity has a restrictions icon displayed or not.
*
* @return bool if the activity has visible restrictions for the user.
*/
protected function get_has_restrictions(): bool {
global $CFG;
$cm = $this->cm;
if (empty($cm->visible) || empty($CFG->enableavailability)) {
return false;
}
// Nothing to be displayed to the user.
if (!$cm->is_visible_on_course_page()) {
return false;
}
// Not allowed to see the module but might be allowed to see some availability.
if (!$cm->uservisible) {
return !empty($cm->availableinfo);
}
// Content editors can see all restrictions if the activity is visible.
if (has_capability('moodle/course:viewhiddenactivities', $cm->context)) {
$ci = new info_module($cm);
return !empty($ci->get_full_information());
}
// Regular users can only see restrictions if apply to them.
return false;
}
}
@@ -0,0 +1,87 @@
<?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\state;
use core_courseformat\base as course_format;
use course_modinfo;
use moodle_url;
use renderable;
use stdClass;
/**
* Contains the ajax update course structure.
*
* @package core_course
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course implements renderable {
/** @var course_format the course format class */
protected $format;
/**
* Constructor.
*
* @param course_format $format the course format
*/
public function __construct(course_format $format) {
$this->format = $format;
}
/**
* Export this data so it can be used as state object in the course editor.
*
* @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 $CFG;
$format = $this->format;
$course = $format->get_course();
$context = $format->get_context();
// State must represent always the most updated version of the course.
$modinfo = course_modinfo::instance($course);
$url = new moodle_url('/course/view.php', ['id' => $course->id]);
$maxbytes = get_user_max_upload_file_size($context, $CFG->maxbytes, $course->maxbytes);
$data = (object)[
'id' => $course->id,
'numsections' => $format->get_last_section_number(),
'sectionlist' => [],
'editmode' => $format->show_editor(),
'highlighted' => $format->get_section_highlighted_name(),
'maxsections' => $format->get_max_sections(),
'baseurl' => $url->out(),
'statekey' => course_format::session_cache($course),
'maxbytes' => $maxbytes,
'maxbytestext' => display_size($maxbytes),
];
$sections = $modinfo->get_section_info_all();
foreach ($sections as $section) {
if ($format->is_section_visible($section)) {
$data->sectionlist[] = $section->id;
}
}
return $data;
}
}
@@ -0,0 +1,147 @@
<?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\state;
use core_availability\info_section;
use core_courseformat\base as course_format;
use section_info;
use renderable;
use stdClass;
use context_course;
/**
* Contains the ajax update section structure.
*
* @package core_course
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class section implements renderable {
/** @var course_format the course format class */
protected $format;
/** @var section_info the course section class */
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 state object in the course editor.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$format = $this->format;
$course = $format->get_course();
$section = $this->section;
$modinfo = $format->get_modinfo();
$indexcollapsed = false;
$contentcollapsed = false;
$preferences = $format->get_sections_preferences();
if (isset($preferences[$section->id])) {
$sectionpreferences = $preferences[$section->id];
if (!empty($sectionpreferences->contentcollapsed)) {
$contentcollapsed = true;
}
if (!empty($sectionpreferences->indexcollapsed)) {
$indexcollapsed = true;
}
}
$data = (object)[
'id' => $section->id,
'section' => $section->section,
'number' => $section->section,
'title' => $format->get_section_name($section),
'hassummary' => !empty($section->summary),
'rawtitle' => $section->name,
'cmlist' => [],
'visible' => !empty($section->visible),
'sectionurl' => course_get_url($course, $section->section, ['navigation' => true])->out(),
'current' => $format->is_section_current($section),
'indexcollapsed' => $indexcollapsed,
'contentcollapsed' => $contentcollapsed,
'hasrestrictions' => $this->get_has_restrictions(),
'bulkeditable' => $this->is_bulk_editable(),
'component' => $section->component,
'itemid' => $section->itemid,
];
if (empty($modinfo->sections[$section->section])) {
return $data;
}
foreach ($modinfo->sections[$section->section] as $modnumber) {
$mod = $modinfo->cms[$modnumber];
if ($section->uservisible && $mod->is_visible_on_course_page()) {
$data->cmlist[] = $mod->id;
}
}
return $data;
}
/**
* Return if the section can be selected for bulk editing.
* @return bool if the section can be edited in bulk
*/
protected function is_bulk_editable(): bool {
$section = $this->section;
return ($section->section != 0);
}
/**
* Return if the section has a restrictions icon displayed or not.
*
* @return bool if the section has visible restrictions for the user.
*/
protected function get_has_restrictions(): bool {
global $CFG;
$section = $this->section;
$course = $this->format->get_course();
$context = context_course::instance($course->id);
// Hidden sections have no restriction indicator displayed.
if (empty($section->visible) || empty($CFG->enableavailability)) {
return false;
}
// The activity is not visible to the user but it may have some availability information.
if (!$section->uservisible) {
return !empty($section->availableinfo);
}
// Course editors can see all restrictions if the section is visible.
if (has_capability('moodle/course:viewhiddensections', $context)) {
$ci = new info_section($section);
return !empty($ci->get_full_information());
}
// Regular users can only see restrictions if apply to them.
return false;
}
}
@@ -0,0 +1,444 @@
<?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;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/renderer.php');
use cm_info;
use coding_exception;
use core_course_renderer;
use core_courseformat\base as course_format;
use html_writer;
use moodle_page;
use renderable;
use section_info;
use stdClass;
/**
* Contains the default section course format output class.
*
* @package core_courseformat
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class section_renderer extends core_course_renderer {
/**
* Constructor method, calls the parent constructor.
*
* @param moodle_page $page
* @param string $target one of rendering target constants
*/
public function __construct(moodle_page $page, $target) {
parent::__construct($page, $target);
// Ensure capabilities for section editing controls match those defined in course/view.php to ensure that they work
// when called via an AJAX request.
if (course_get_format($page->course)->uses_sections()) {
$page->set_other_editing_capability('moodle/course:sectionvisibility');
$page->set_other_editing_capability('moodle/course:movesections');
}
}
/**
* Renders the provided widget and returns the HTML to display it.
*
* Course format templates uses a similar subfolder structure to the renderable classes.
* This method find out the specific template for a course widget. That's the reason why
* this render method is different from the normal plugin renderer one.
*
* course format templatables can be rendered using the core_course/local/* templates.
* Format plugins are free to override the default template location using render_xxx methods as usual.
*
* @param renderable $widget instance with renderable interface
* @return string the widget HTML
*/
public function render(renderable $widget) {
global $CFG;
$fullpath = str_replace('\\', '/', get_class($widget));
$classparts = explode('/', $fullpath);
// Strip namespaces.
$classname = array_pop($classparts);
// Remove _renderable suffixes.
$classname = preg_replace('/_renderable$/', '', $classname);
$rendermethod = 'render_' . $classname;
if (method_exists($this, $rendermethod)) {
return $this->$rendermethod($widget);
}
// If nothing works, let the parent class decide.
return parent::render($widget);
}
/**
* Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
* @return string HTML to output.
*/
public function section_title($section, $course) {
$title = get_section_name($course, $section);
$url = course_get_url($course, $section->section, array('navigation' => true));
if ($url) {
$title = html_writer::link($url, $title);
}
return $title;
}
/**
* Generate the section title to be displayed on the section page, without a link
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
* @return string HTML to output.
*/
public function section_title_without_link($section, $course) {
return get_section_name($course, $section);
}
/**
* Get the updated rendered version of a cm list item.
*
* This method is used when an activity is duplicated or copied in on the client side without refreshing the page.
* Note that the previous method is used every time an activity is rendered, independent of it is the initial page
* loading or an Ajax update. In this case, course_section_updated_cm_item will only be used when the course editor
* requires to get an updated cm item HTML to perform partial page refresh. It will be used for suporting the course
* editor webservices.
*
* By default, the template used for update a cm_item is the same as when it renders initially, but format plugins are
* free to override this methos to provide extra affects or so.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @param cm_info $cm the course module ionfo
* @param array $displayoptions optional extra display options
* @return string the rendered element
*/
public function course_section_updated_cm_item(
course_format $format,
section_info $section,
cm_info $cm,
array $displayoptions = []
) {
$cmitemclass = $format->get_output_classname('content\\section\\cmitem');
$cmitem = new $cmitemclass($format, $section, $cm, $displayoptions);
return $this->render($cmitem);
}
/**
* Get the updated rendered version of a section.
*
* This method will only be used when the course editor requires to get an updated cm item HTML
* to perform partial page refresh. It will be used for supporting the course editor webservices.
*
* By default, the template used for update a section is the same as when it renders initially,
* but format plugins are free to override this method to provide extra effects or so.
*
* @param course_format $format the course format
* @param section_info $section the section info
* @return string the rendered element
*/
public function course_section_updated(
course_format $format,
section_info $section
): string {
$sectionclass = $format->get_output_classname('content\\section');
$output = new $sectionclass($format, $section);
return $this->render($output);
}
/**
* Get the course index drawer with placeholder.
*
* The default course index is loaded after the page is ready. Format plugins can override
* this method to provide an alternative course index.
*
* If the format is not compatible with the course index, this method will return an empty string.
*
* @param course_format $format the course format
* @return String the course index HTML.
*/
public function course_index_drawer(course_format $format): ?String {
if ($format->uses_course_index()) {
include_course_editor($format);
return $this->render_from_template('core_courseformat/local/courseindex/drawer', []);
}
return '';
}
/**
* Render the enable bulk editing button.
* @param course_format $format the course format
* @return string|null the enable bulk button HTML (or null if no bulk available).
*/
public function bulk_editing_button(course_format $format): ?string {
if (!$format->show_editor() || !$format->supports_components()) {
return null;
}
$widgetclass = $format->get_output_classname('content\\bulkedittoggler');
$widget = new $widgetclass($format);
return $this->render($widget);
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_edit_control_menu($controls, $course, $section) {
throw new coding_exception('section_edit_control_menu() can not be used anymore. Please use ' .
'core_courseformat\\output\\local\\content\\section to render a section. In case you need to modify those controls ' .
'override core_courseformat\\output\\local\\content\\section\\controlmenu in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_right_content($section, $course, $onsectionpage) {
throw new coding_exception('section_right_content() can not be used anymore. Please use ' .
'core_courseformat\\output\\local\\content\\section to render a section.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_left_content($section, $course, $onsectionpage) {
throw new coding_exception('section_left_content() can not be used anymore. Please use ' .
'core_courseformat\\output\\local\\content\\section to render a section.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_header($section, $course, $onsectionpage, $sectionreturn = null) {
throw new coding_exception('section_header() can not be used any more. Please use ' .
'core_courseformat\\output\\local\\content\\section to render a section ' .
'or core_courseformat\output\\local\\content\\section\\header ' .
'to print only the header.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_footer() {
throw new coding_exception('section_footer() can not be used any more. Please use ' .
'core_courseformat\\output\\local\\content\\section to render individual sections or .' .
'core_courseformat\\output\\local\\content to render the full course');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function start_section_list() {
throw new coding_exception('start_section_list() can not be used any more. Please use ' .
'core_courseformat\\output\\local\\content\\section to render individual sections or .' .
'core_courseformat\\output\\local\\content to render the full course');
}
/**
* @deprecated since 4.0 - use core_course output components instead.y
*/
protected function end_section_list() {
throw new coding_exception('end_section_list() can not be used any more. Please use ' .
'core_courseformat\\output\\local\\content\\section to render individual sections or .' .
'core_courseformat\\output\\local\\content to render the full course');
}
/**
* Old method to print section edit controls. Do not use it!
*
* @deprecated since Moodle 3.0 MDL-48947 - Use core_courseformat\output\section_renderer::section_edit_control_items() instead
*/
protected function section_edit_controls() {
throw new coding_exception('section_edit_controls() can not be used anymore. Please use ' .
'section_edit_control_items() instead.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_edit_control_items($course, $section, $onsectionpage = false) {
throw new coding_exception('section_edit_control_items() can not be used any more. Please use or extend' .
'core_courseformat\output\\local\\content\\section\\controlmenu instead (like topics format does).');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_summary($section, $course, $mods) {
throw new coding_exception('section_summary() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections. If you need to modify those summary, extend ' .
'core_courseformat\output\\local\\content\\section\\summary in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_activity_summary($section, $course, $mods) {
throw new coding_exception('section_activity_summary() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections. ' .
'If you need to modify those information, extend ' .
'core_courseformat\output\\local\\content\\section\\cmsummary in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_availability_message($section, $canviewhidden) {
throw new coding_exception('section_availability_message() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections. If you need to modify this element, extend ' .
'core_courseformat\output\\local\\content\\section\\availability in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
public function section_availability($section) {
throw new coding_exception('section_availability() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections. If you need to modify this element, extend ' .
'core_courseformat\output\\local\\content\\section\\availability in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function course_activity_clipboard($course, $sectionno = null) {
throw new coding_exception('Non ajax course edition using course_activity_clipboard is not supported anymore.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function get_nav_links($course, $sections, $sectionno) {
throw new coding_exception('get_nav_links() can not be used any more. Please use ' .
'core_courseformat\\output\\local\\content to render a course. If you need to modify this element, extend ' .
'core_courseformat\\output\\local\\content\\sectionnavigation in your format plugin.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function stealth_section_header($sectionno) {
throw new coding_exception('stealth_section_header() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections.');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function stealth_section_footer() {
throw new coding_exception('stealth_section_footer() can not be used any more. Please use ' .
'core_courseformat\output\\local\\content\\section to render sections.');
}
/**
* Generate the html for a hidden section
*
* @param int $sectionno The section number in the course which is being displayed
* @param int|stdClass $courseorid The course to get the section name for (object or just course id)
* @return string HTML to output.
*/
protected function section_hidden($sectionno, $courseorid = null) {
if ($courseorid) {
$sectionname = get_section_name($courseorid, $sectionno);
$strnotavailable = get_string('notavailablecourse', '', $sectionname);
} else {
$strnotavailable = get_string('notavailable');
}
$o = '';
$o .= html_writer::start_tag('li', [
'id' => 'section-' . $sectionno,
'class' => 'section main clearfix hidden',
'data-sectionid' => $sectionno
]);
$o .= html_writer::tag('div', '', array('class' => 'left side'));
$o .= html_writer::tag('div', '', array('class' => 'right side'));
$o .= html_writer::start_tag('div', array('class' => 'content'));
$o .= html_writer::tag('div', $strnotavailable);
$o .= html_writer::end_tag('div');
$o .= html_writer::end_tag('li');
return $o;
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function section_nav_selection($course, $sections, $displaysection) {
throw new coding_exception('section_nav_selection() can not be used anymore. Please use ' .
'core_courseformat\\output\\local\\content to render a course. If you need to modify this element, extend ' .
'core_courseformat\\output\\local\\content\\sectionnavigation or ' .
'core_courseformat\\output\\local\\content\\sectionselector in your format plugin.');
}
/**
* @deprecated since 4.0
*/
public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) {
throw new coding_exception('Method print_single_section_page can not be used anymore. Please use' .
'core_courseformat\\output\\local\\content instead ' .
'or override render_content method to use a different template');
}
/**
* @deprecated since 4.0
*/
public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) {
throw new coding_exception('Method print_multiple_section_page can not be used anymore. Please use' .
'core_courseformat\\output\\local\\content instead ' .
'or override render_content method to use a diferent template');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function change_number_sections($course, $sectionreturn = null) {
throw new coding_exception('Method change_number_sections can not be used anymore. Please use' .
'core_courseformat\\output\\local\\content\\addsection instead');
}
/**
* @deprecated since 4.0 - use core_course output components instead.
*/
protected function format_summary_text($section) {
throw new coding_exception('Method format_summary_text can not be used anymore. Please use' .
'core_courseformat\output\\local\\content\\section\\summary::format_summary_text instead');
}
}
@@ -0,0 +1,30 @@
<?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/>.
/**
* Site topics renderer.
*
* Site course is not a real course format, but it requires a format renderer to use the output
* components.
*
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_courseformat\output;
class site_renderer extends section_renderer {
}