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
+225
View File
@@ -0,0 +1,225 @@
<?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\output;
use moodle_page;
use renderer_base;
use url_select;
/**
* Data structure representing standard components displayed on the activity header.
*
* Consists of title, header, description. In addition, additional_items can be provided which is a url_select
*
* @copyright 2021 Peter
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.0
* @package core
* @category output
*/
class activity_header implements \renderable, \templatable {
/** @var moodle_page $page The current page we are looking at */
protected $page;
/** @var string $title The title to be displayed in the header. Defaults to activityrecord name. */
protected $title;
/** @var string $description The description to be displayed. Defaults to activityrecord intro. */
protected $description;
/** @var \stdClass $user The user we are dealing with */
protected $user;
/** @var url_select $additionalnavitems Any additional custom navigation elements to be injected into template. */
protected $additionalnavitems;
/** @var bool $hidecompletion Whether to show completion criteria, if available, or not */
protected $hidecompletion;
/** @var bool $hideoverflow Whether to show the overflow data or not */
protected $hideoverflow;
/** @var bool $hideheader Whether or not to show the header */
protected $hideheader;
/**
* Constructor for activity_header
*
* @param moodle_page $page
* @param \stdClass $user
*/
public function __construct(moodle_page $page, \stdClass $user) {
$this->page = $page;
$this->user = $user;
$pageoptions = $this->page->theme->activityheaderconfig ?? [];
$layoutoptions = $this->page->layout_options['activityheader'] ?? [];
// Do a basic setup for the header based on theme/page options.
if ($page->activityrecord) {
if (empty($pageoptions['notitle']) && empty($layoutoptions['notitle'])) {
$this->title = format_string($page->activityrecord->name);
}
if (empty($layoutoptions['nodescription']) && !empty($page->activityrecord->intro) &&
trim($page->activityrecord->intro)) {
$this->description = format_module_intro($this->page->activityname, $page->activityrecord, $page->cm->id);
}
}
$this->hidecompletion = !empty($layoutoptions['nocompletion']);
$this->hideoverflow = false;
$this->hideheader = false;
}
/**
* Checks if the theme has specified titles to be displayed.
*
* @return bool
*/
public function is_title_allowed(): bool {
return empty($this->page->theme->activityheaderconfig['notitle']);
}
/**
* Bulk set class member variables. Only updates variables which have corresponding setters
*
* @param mixed[] $config Array of variables to set, with keys being their name. Valid names/types as follows:
* 'hidecompletion' => bool
* 'additionalnavitems' => url_select
* 'hideoverflow' => bool
* 'title' => string
* 'description' => string
*/
public function set_attrs(array $config): void {
foreach ($config as $key => $value) {
if (method_exists($this, "set_$key")) {
$this->{"set_$key"}($value);
} else {
debugging("Invalid class member variable: {$key}", DEBUG_DEVELOPER);
}
}
}
/**
* Sets the hidecompletion class member variable
*
* @param bool $value
*/
public function set_hidecompletion(bool $value): void {
$this->hidecompletion = $value;
}
/**
* Sets the additionalnavitems class member variable
*
* @param url_select $value
*/
public function set_additionalnavitems(url_select $value): void {
$this->additionalnavitems = $value;
}
/**
* Sets the hideoverflow class member variable
*
* @param bool $value
*/
public function set_hideoverflow(bool $value): void {
$this->hideoverflow = $value;
}
/**
* Sets the title class member variable.
*
* @param string $value
*/
public function set_title(string $value): void {
$this->title = preg_replace('/<h2[^>]*>([.\s\S]*)<\/h2>/', '$1', $value);
}
/**
* Sets the description class member variable
*
* @param string $value
*/
public function set_description(string $value): void {
$this->description = $value;
}
/**
* Disable the activity header completely. Use this if the page has some custom content, headings to be displayed.
*/
public function disable(): void {
$this->hideheader = true;
}
/**
* Export items to be rendered with a template.
*
* @param renderer_base $output
* @return array
*/
public function export_for_template(renderer_base $output): array {
// Don't need to show anything if not displaying within an activity context.
if (!$this->page->activityrecord) {
return [];
}
// If within an activity context but requesting to hide the header,
// then just trigger the render for maincontent div.
if ($this->hideheader) {
return ['title' => ''];
}
$activityinfo = null;
if (!$this->hidecompletion) {
$completiondetails = \core_completion\cm_completion_details::get_instance($this->page->cm, $this->user->id);
$activitydates = \core\activity_dates::get_dates_for_module($this->page->cm, $this->user->id);
$activitycompletion = new \core_course\output\activity_completion($this->page->cm, $completiondetails);
$activitycompletiondata = (array) $activitycompletion->export_for_template($output);
$activitydates = new \core_course\output\activity_dates($activitydates);
$activitydatesdata = (array) $activitydates->export_for_template($output);
$data = array_merge($activitycompletiondata, $activitydatesdata);
$activityinfo = $output->render_from_template('core_course/activity_info', $data);
}
$format = course_get_format($this->page->course);
if ($format->supports_components()) {
$this->page->requires->js_call_amd(
'core_courseformat/local/content/activity_header',
'init'
);
}
return [
'title' => $this->title,
'description' => $this->description,
'completion' => $activityinfo,
'additional_items' => $this->hideoverflow ? '' : $this->additionalnavitems,
];
}
/**
* Get the heading level for a given heading depending on whether the theme's activity header displays a heading
* (usually the activity name).
*
* @param int $defaultlevel The default heading level when the activity header does not display a heading.
* @return int
*/
public function get_heading_level(int $defaultlevel = 2): int {
// The heading level depends on whether the theme's activity header displays a heading (usually the activity name).
$headinglevel = $defaultlevel;
if ($this->is_title_allowed() && !empty(trim($this->title))) {
// A heading for the activity name is displayed on this page with a heading level 2.
// Increment the default level for this heading by 1.
$headinglevel++;
}
return $headinglevel;
}
}
+125
View File
@@ -0,0 +1,125 @@
<?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/>.
/**
* The renderable for core/checkbox-toggleall.
*
* @package core
* @copyright 2019 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
/**
* The checkbox-toggleall renderable class.
*
* @package core
* @copyright 2019 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class checkbox_toggleall implements renderable, templatable {
/** @var string The name of the group of checkboxes to be toggled. */
protected $togglegroup;
/** @var bool $ismaster Whether we're rendering for a master checkbox or a slave checkbox. */
protected $ismaster;
/** @var array $options The options for the checkbox. */
protected $options;
/** @var bool $isbutton Whether to render this as a button. Applies to master checkboxes only. */
protected $isbutton;
/**
* Constructor.
*
* @param string $togglegroup The name of the group of checkboxes to be toggled.
* @param bool $ismaster Whether we're rendering for a master checkbox or a slave checkbox.
* @param array $options The options for the checkbox. Valid options are:
* <ul>
* <li><b>id </b> string - The element ID.</li>
* <li><b>name </b> string - The element name.</li>
* <li><b>classes </b> string - CSS classes that you want to add for your checkbox or toggle controls.
* For button type master toggle controls, this could be any Bootstrap 4 btn classes
* that you might want to add. Defaults to "btn-secondary".</li>
* <li><b>value </b> string|int - The element's value.</li>
* <li><b>checked </b> boolean - Whether to render this initially as checked.</li>
* <li><b>label </b> string - The label for the checkbox element.</li>
* <li><b>labelclasses</b> string - CSS classes that you want to add for your label.</li>
* <li><b>selectall </b> string - Master only. The language string that will be used to indicate that clicking on
* the master will select all of the slave checkboxes. Defaults to "Select all".</li>
* <li><b>deselectall </b> string - Master only. The language string that will be used to indicate that clicking on
* the master will select all of the slave checkboxes. Defaults to "Deselect all".</li>
* </ul>
* @param bool $isbutton Whether to render this as a button. Applies to master only.
*/
public function __construct(string $togglegroup, bool $ismaster, $options = [], $isbutton = false) {
$this->togglegroup = $togglegroup;
$this->ismaster = $ismaster;
$this->options = $options;
$this->isbutton = $ismaster && $isbutton;
}
/**
* Export for template.
*
* @param renderer_base $output The renderer.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = (object)[
'togglegroup' => $this->togglegroup,
'id' => $this->options['id'] ?? null,
'name' => $this->options['name'] ?? null,
'value' => $this->options['value'] ?? null,
'classes' => $this->options['classes'] ?? null,
'label' => $this->options['label'] ?? null,
'labelclasses' => $this->options['labelclasses'] ?? null,
'checked' => $this->options['checked'] ?? false,
];
if ($this->ismaster) {
$data->selectall = $this->options['selectall'] ?? get_string('selectall');
$data->deselectall = $this->options['deselectall'] ?? get_string('deselectall');
}
return $data;
}
/**
* Fetches the appropriate template for the checkbox toggle all element.
*
* @return string
*/
public function get_template() {
if ($this->ismaster) {
if ($this->isbutton) {
return 'core/checkbox-toggleall-master-button';
} else {
return 'core/checkbox-toggleall-master';
}
}
return 'core/checkbox-toggleall-slave';
}
}
+308
View File
@@ -0,0 +1,308 @@
<?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\output;
use renderable;
use renderer_base;
use core\output\named_templatable;
/**
* A generic user choice output class.
*
* This class can be used as a generic user choice data structure for any dropdown, modal, or any
* other component that offers choices to the user.
*
* @package core
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class choicelist implements renderable, named_templatable {
/** @var object[] The user choices. */
protected $options = [];
/** @var string the selected option. */
protected $selected = null;
/** @var string the choice description. */
protected $description = null;
/** @var bool if the selected value can be empty. */
protected $allowempty = null;
/**
* Constructor.
*
* @param string $description the choice description.
*/
public function __construct(?string $description = null) {
$this->description = $description;
}
/**
* Add option to the user choice.
*
* The definition object could contain the following keys:
* - string description: the description of the option.
* - \moodle_url url: the URL to link to.
* - \pix_icon icon: the icon to display.
* - bool disabled: whether the option is disabled.
* - bool selected: whether the option is selected.
* - array extras: an array of HTML attributes to add to the option (attribute => value).
*
* @param string $value
* @param string $name
* @param array $definition an optional array of definition for the option.
*/
public function add_option(string $value, string $name, array $definition = []) {
$option = [
'value' => $value,
'name' => $name,
'description' => $definition['description'] ?? null,
'url' => $definition['url'] ?? null,
'icon' => $definition['icon'] ?? null,
'disabled' => (!empty($definition['disabled'])) ? true : false,
];
if (!empty($definition['selected'])) {
$this->selected = $value;
}
$this->options[$value] = $option;
if (isset($definition['extras'])) {
$this->set_option_extras($value, $definition['extras']);
}
}
/**
* Get the number of options added to the choice list.
* @return int
*/
public function count_options(): int {
return count($this->options);
}
/**
* Get the selectable options.
*
* This method returns an array of options that are selectable, excluding the selected option and any disabled options.
*
* @return \stdClass[]
*/
public function get_selectable_options(): array {
$selectableOptions = [];
foreach ($this->options as $option) {
if ($option['value'] !== $this->selected && !$option['disabled']) {
$selectableOptions[] = (object) $option;
}
}
return $selectableOptions;
}
/**
* Set the selected option.
*
* @param string $value The value of the selected option.
*/
public function set_selected_value(string $value) {
$this->selected = $value;
}
/**
* Get the selected option.
*
* @return string|null The value of the selected option.
*/
public function get_selected_value(): ?string {
if (empty($this->selected) && !$this->allowempty && !empty($this->options)) {
return array_key_first($this->options);
}
return $this->selected;
}
/**
* Set the allow empty option.
* @param bool $allowempty Whether the selected value can be empty.
*/
public function set_allow_empty(bool $allowempty) {
$this->allowempty = $allowempty;
}
/**
* Get the allow empty option.
* @return bool Whether the selected value can be empty.
*/
public function get_allow_empty(): bool {
return $this->allowempty;
}
/**
* Check if the value is in the options.
* @param string $value The value to check.
* @return bool
*/
public function has_value(string $value): bool {
return isset($this->options[$value]);
}
/**
* Set the general choice description option.
*
* @param string $value the new description.
*/
public function set_description(string $value) {
$this->description = $value;
}
/**
* Get the choice description option.
*
* @return string|null the current description.
*/
public function get_description(): ?string {
return $this->description;
}
/**
* Set the option disabled.
*
* @param string $value The value of the option.
* @param bool $disabled Whether the option is disabled.
*/
public function set_option_disabled(string $value, bool $disabled) {
if (isset($this->options[$value])) {
$this->options[$value]['disabled'] = $disabled;
}
}
/**
* Sets the HTML attributes to the option.
*
* This method will remove any previous extra attributes.
*
* @param string $value The value of the option.
* @param array $extras an array to add HTML attributes to the option (attribute => value).
*/
public function set_option_extras(string $value, array $extras) {
if (!isset($this->options[$value])) {
return;
}
$this->options[$value]['extras'] = [];
$this->add_option_extras($value, $extras);
}
/**
* Add HTML attributes to the option.
* @param string $value The value of the option.
* @param array $extras an array to add HTML attributes to the option (attribute => value).
*/
public function add_option_extras(string $value, array $extras) {
if (!isset($this->options[$value])) {
return;
}
if (!isset($this->options[$value]['extras'])) {
$this->options[$value]['extras'] = [];
}
foreach ($extras as $attribute => $attributevalue) {
$this->options[$value]['extras'][] = [
'attribute' => $attribute,
'value' => $attributevalue,
];
}
}
/**
* Retrieves the HTML attributes for a given value from the options array.
* @param string $value The value for which to retrieve the extras.
* @return array an array of HTML attributes of the option (attribute => value).
*/
public function get_option_extras(string $value): array {
if (!isset($this->options[$value]) || !isset($this->options[$value]['extras'])) {
return [];
}
$result = [];
foreach ($this->options[$value]['extras'] as $extra) {
$result[$extra['attribute']] = $extra['value'];
}
return $result;
}
/**
* Get the selected option HTML.
*
* This method is used to display the selected option and the option icon.
*
* @param renderer_base $output The renderer.
* @return string
*/
public function get_selected_content(renderer_base $output): string {
if (empty($this->selected)) {
return '';
}
$option = $this->options[$this->selected];
$icon = '';
if (!empty($option['icon'])) {
$icon = $output->render($option['icon']);
}
return $icon . $option['name'];
}
/**
* Export for template.
*
* @param renderer_base $output The renderer.
* @return array
*/
public function export_for_template(renderer_base $output): array {
$options = [];
foreach ($this->options as $option) {
if (!empty($option['icon'])) {
$option['icon'] = $option['icon']->export_for_pix($output);
}
$option['hasicon'] = !empty($option['icon']);
if (!empty($option['url'])) {
$option['url'] = $option['url']->out(true);
}
$option['hasurl'] = !empty($option['url']);
if ($option['value'] == $this->get_selected_value()) {
$option['selected'] = true;
}
$option['optionnumber'] = count($options) + 1;
$option['first'] = count($options) === 0;
$option['optionuniqid'] = \html_writer::random_id('choice_option_');
$options[] = $option;
}
return [
'description' => $this->description,
'options' => $options,
'hasoptions' => !empty($options),
];
}
/**
* 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/local/choicelist';
}
}
+134
View File
@@ -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/>.
/**
* The chooser renderable.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use lang_string;
use moodle_url;
use renderer_base;
use renderable;
use stdClass;
use templatable;
/**
* The chooser renderable class.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class chooser implements renderable, templatable {
/** @var moodle_url The form action URL. */
public $actionurl;
/** @var lang_string The instructions to display. */
public $instructions;
/** @var string The form method. */
public $method = 'post';
/** @var string The name of the parameter for the items value. */
public $paramname;
/** @var array The list of hidden parameters. See {@link self::add_param}. */
public $params = [];
/** @var chooser_section[] The sections */
public $sections;
/** @var lang_string The chooser title. */
public $title;
/**
* Constructor.
*
* @param moodle_url $actionurl The form action URL.
* @param lang_string $title The title of the chooser.
* @param chooser_section[] $sections The sections.
* @param string $paramname The name of the parameter for the items value.
*/
public function __construct(moodle_url $actionurl, lang_string $title, array $sections, $paramname) {
$this->actionurl = $actionurl;
$this->title = $title;
$this->sections = $sections;
$this->paramname = $paramname;
}
/**
* Add a parameter to submit with the form.
*
* @param string $name The parameter name.
* @param string $value The parameter value.
* @param string $id The parameter ID.
*/
public function add_param($name, $value, $id = null) {
if (!$id) {
$id = $name;
}
$this->params[] = [
'name' => $name,
'value' => $value,
'id' => $id
];
}
/**
* Set the chooser instructions.
*
* @param lang_string $value The instructions.
*/
public function set_instructions(lang_string $value) {
$this->instructions = $value;
}
/**
* Set the form method.
*
* @param string $value The method.
*/
public function set_method($value) {
$this->method = $value;
}
/**
* Export for template.
*
* @param renderer_base The renderer.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->actionurl = $this->actionurl->out(false);
$data->instructions = (string) $this->instructions;
$data->method = $this->method;
$data->paramname = $this->paramname;
$data->params = $this->params;
$data->sesskey = sesskey();
$data->title = (string) $this->title;
$data->sections = array_map(function($section) use ($output) {
return $section->export_for_template($output);
}, $this->sections);
return $data;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?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/>.
/**
* The chooser_item renderable.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use coding_exception;
use context;
use pix_icon;
use renderer_base;
use renderable;
use stdClass;
use templatable;
/**
* The chooser_item renderable class.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class chooser_item implements renderable, templatable {
/** @var string An identifier for the item. */
public $id;
/** @var string The label of this item. */
public $label;
/** @var string The value this item represents. */
public $value;
/** @var pix_icon The icon for this item. */
public $icon;
/** @var string The item description. */
public $description;
/** @var context The relevant context. */
public $context;
/**
* Constructor.
*/
public function __construct($id, $label, $value, pix_icon $icon, $description = null, context $context = null) {
$this->id = $id;
$this->label = $label;
$this->value = $value;
$this->icon = $icon;
$this->description = $description;
if (!empty($description) && empty($context)) {
throw new coding_exception('The context must be passed when there is a description.');
}
$this->context = $context;
}
/**
* Export for template.
*
* @param renderer_base The renderer.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->id = $this->id;
$data->label = $this->label;
$data->value = $this->value;
$data->icon = $this->icon->export_for_template($output);
$options = new stdClass();
$options->trusted = false;
$options->noclean = false;
$options->filter = false;
$options->para = true;
$options->newlines = false;
$options->overflowdiv = false;
$data->description = '';
if (!empty($this->description)) {
list($data->description) = \core_external\util::format_text((string) $this->description, FORMAT_MARKDOWN,
$this->context->id, null, null, null, $options);
}
return $data;
}
}
+79
View File
@@ -0,0 +1,79 @@
<?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/>.
/**
* The chooser_section renderable.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use lang_string;
use renderer_base;
use renderable;
use stdClass;
use templatable;
/**
* The chooser_section renderable class.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class chooser_section implements renderable, templatable {
/** @var string $id An identifier for the section. */
public $id;
/** @var lang_string $label The label of the section. */
public $label;
/** @var chooser_item[] $items The items in this section. */
public $items;
/**
* Constructor.
*
* @param string $id An identifier for the section.
* @param lang_string $label The label of the section.
* @param chooser_item[] $items The items in this section.
*/
public function __construct($id, lang_string $label, array $items) {
$this->id = $id;
$this->label = $label;
$this->items = $items;
}
/**
* Export for template.
*
* @param renderer_base The renderer.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->id = $this->id;
$data->label = (string) $this->label;
$data->items = array_map(function($item) use ($output) {
return $item->export_for_template($output);
}, array_values($this->items));
return $data;
}
}
+165
View File
@@ -0,0 +1,165 @@
<?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\output;
use moodle_exception;
use renderable;
use renderer_base;
use templatable;
/**
* Renderable class for the comboboxsearch.
*
* @package core_output
* @copyright 2022 Mathew May <Mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comboboxsearch implements renderable, templatable {
/** @var bool $renderlater Should the dropdown render straightaway? We sometimes need to output the component without all of the
* data and leave the rendering of any defaults and actual data to the caller. We will give you a basic placeholder that can
* then be easily replaced.*/
protected $renderlater;
/** @var string $buttoncontent What is the content of the "Button" that users will always see. */
protected $buttoncontent;
/** @var null|string $dropdowncontent The content that can be passed in to render immediately. */
protected $dropdowncontent;
/** @var null|string $parentclasses Any special classes to put on the HTMLElement that contains the BS events. */
protected $parentclasses;
/** @var null|string $buttonclasses Any special classes to put on the HTMLElement that triggers the dropdown. */
protected $buttonclasses;
/** @var null|string $dropdownclasses Any special classes to put on the HTMLElement that contains the actual dropdown. */
protected $dropdownclasses;
/** @var null|string $buttonheader If the button item in the tertiary nav needs an extra top header for context. */
protected $buttonheader;
/** @var boolean $usesbutton Whether to provide a A11y button. */
protected $usesbutton;
/** @var null|string $label The label of the combobox. */
protected $label;
/** @var null|string $name The name of the input element representing the combobox. */
protected $name;
/** @var null|string $value The value of the input element representing the combobox. */
protected $value;
/**
* The class constructor.
*
* @param bool $renderlater How we figure out if we should render the template instantly.
* @param string $buttoncontent What gets placed in the button.
* @param ?string $dropdowncontent What will be placed in the dropdown if we are rendering now.
* @param ?string $parentclasses The classes that can be added that the bootstrap events are attached to.
* @param ?string $buttonclasses Any special classes that may be needed.
* @param ?string $dropdownclasses Any special classes that may be needed.
* @param ?string $buttonheader Sometimes we want extra context for a button before it is shown, basically a pseudo header.
* @param ?bool $usebutton If we want the mustache to add the button roles for us or do we have another aria role node?
* @param ?string $label The label of the combobox.
* @param ?string $name The name of the input element representing the combobox.
* @param ?string $value The value of the input element representing the combobox.
* @throws moodle_exception If the implementor incorrectly calls this module.
*/
public function __construct(
bool $renderlater,
string $buttoncontent,
?string $dropdowncontent = null,
?string $parentclasses = null,
?string $buttonclasses = null,
?string $dropdownclasses = null,
?string $buttonheader = null,
?bool $usebutton = true,
?string $label = null,
?string $name = null,
?string $value = null
) {
// Ensure implementors cant request to render the content now and not provide us any to show.
if (!$renderlater && empty($dropdowncontent)) {
throw new moodle_exception(
'incorrectdropdownvars',
'core',
'', null,
'Dropdown content must be set to render later.'
);
}
if ($usebutton && !$label) {
debugging(
'You have requested to use the button but have not provided a label for the combobox.',
DEBUG_DEVELOPER
);
}
if ($usebutton && !$name) {
debugging(
'You have requested to use the button but have not provided a name for the input element.',
DEBUG_DEVELOPER
);
}
$this->renderlater = $renderlater;
$this->buttoncontent = $buttoncontent;
$this->dropdowncontent = $dropdowncontent;
$this->parentclasses = $parentclasses;
$this->buttonclasses = $buttonclasses;
$this->dropdownclasses = $dropdownclasses;
$this->buttonheader = $buttonheader;
$this->usesbutton = $usebutton;
$this->label = $label;
$this->name = $name;
$this->value = $value;
}
/**
* Export the data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(renderer_base $output): array {
return [
'renderlater' => $this->renderlater,
'buttoncontent' => $this->buttoncontent ,
'dropdowncontent' => $this->dropdowncontent,
'parentclasses' => $this->parentclasses,
'buttonclasses' => $this->buttonclasses,
'dropdownclasses' => $this->dropdownclasses,
'buttonheader' => $this->buttonheader,
'usebutton' => $this->usesbutton,
'instance' => rand(), // Template uniqid is per render out so sometimes these conflict.
'label' => $this->label,
'name' => $this->name,
'value' => $this->value,
];
}
/**
* Returns the standard template for the dropdown.
*
* @return string
*/
public function get_template(): string {
return 'core/comboboxsearch';
}
}
+116
View File
@@ -0,0 +1,116 @@
<?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\output;
use context;
use renderable;
use stdClass;
use templatable;
/**
* The filter renderable class.
*
* @package core
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class datafilter implements renderable, templatable {
/** @var int None of the following match */
public const JOINTYPE_NONE = 0;
/** @var int Any of the following match */
public const JOINTYPE_ANY = 1;
/** @var int All of the following match */
public const JOINTYPE_ALL = 2;
/** @var context $context The context where the filters are being rendered. */
protected $context;
/** @var string $tableregionid Container of the table to be updated by this filter, is used to retrieve the table */
protected $tableregionid;
/** @var stdClass $course The course shown */
protected $course;
/**
* Filter constructor.
*
* @param context $context The context where the filters are being rendered
* @param string|null $tableregionid Container of the table which will be updated by this filter
*/
public function __construct(context $context, ?string $tableregionid = null) {
$this->context = $context;
$this->tableregionid = $tableregionid;
if ($context instanceof \context_course) {
$this->course = get_course($context->instanceid);
}
}
/**
* Get data for all filter types.
*
* @return array
*/
abstract protected function get_filtertypes(): array;
/**
* Get a standardised filter object.
*
* @param string $name
* @param string $title
* @param bool $custom
* @param bool $multiple
* @param string|null $filterclass
* @param array $values
* @param bool $allowempty
* @return stdClass|null
*/
protected function get_filter_object(
string $name,
string $title,
bool $custom,
bool $multiple,
?string $filterclass,
array $values,
bool $allowempty = false,
?stdClass $filteroptions = null,
bool $required = false,
array $joinlist = [self::JOINTYPE_NONE, self::JOINTYPE_ANY, self::JOINTYPE_ALL]
): ?stdClass {
if (!$allowempty && empty($values)) {
// Do not show empty filters.
return null;
}
return (object) [
'name' => $name,
'title' => $title,
'allowcustom' => $custom,
'allowmultiple' => $multiple,
'filtertypeclass' => $filterclass,
'values' => $values,
'filteroptions' => $filteroptions,
'required' => $required,
'joinlist' => json_encode($joinlist)
];
}
}
+87
View File
@@ -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/>.
declare(strict_types=1);
namespace core\output;
use core\output\dynamic_tabs\base;
use renderer_base;
use templatable;
/**
* Class dynamic tabs
*
* @package core
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dynamic_tabs implements templatable {
/** @var base[] */
protected $tabs = [];
/**
* tabs constructor.
*
* @param base[] $tabs array of tab
*/
public function __construct(array $tabs = []) {
foreach ($tabs as $tab) {
$this->add_tab($tab);
}
}
/**
* Add a tab
*
* @param base $tab
*/
public function add_tab(base $tab): void {
$this->tabs[] = $tab;
}
/**
* Implementation of exporter from templatable interface
*
* @param renderer_base $output
* @return array
*/
public function export_for_template(renderer_base $output): array {
$data = [
'tabs' => []
];
foreach ($this->tabs as $tab) {
$dataattributes = [];
foreach ($tab->get_data() as $name => $value) {
$dataattributes[] = ['name' => $name, 'value' => $value];
}
$data['tabs'][] = [
'shortname' => $tab->get_tab_id(),
'displayname' => $tab->get_tab_label(),
'enabled' => $tab->is_available(),
'tabclass' => get_class($tab),
'dataattributes' => $dataattributes,
];
}
$data['showtabsnavigation'] = (count($data['tabs']) > 1) ? 1 : 0;
return $data;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?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/>.
declare(strict_types=1);
namespace core\output\dynamic_tabs;
use moodle_exception;
use templatable;
/**
* Class tab_base
*
* @package core
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class base implements templatable {
/** @var array */
protected $data;
/**
* tab constructor.
*
* @param array $data
*/
final public function __construct(array $data) {
$this->data = $data;
}
/**
* HTML "id" attribute that should be used for this tab, by default the last part of class name
*
* @return string
*/
public function get_tab_id(): string {
$parts = preg_split('/\\\\/', static::class);
return array_pop($parts);
}
/**
* The label to be displayed on the tab
*
* @return string
*/
abstract public function get_tab_label(): string;
/**
* Check permission of the current user to access this tab
*
* @return bool
*/
abstract public function is_available(): bool;
/**
* Check that tab is accessible, throw exception otherwise - used from WS requesting tab contents
*
* @throws moodle_exception
*/
final public function require_access() {
if (!$this->is_available()) {
throw new moodle_exception('nopermissiontoaccesspage', 'error');
}
}
/**
* Template to use to display tab contents
*
* @return string
*/
abstract public function get_template(): string;
/**
* Return tab data attributes
*
* @return array
*/
public function get_data(): array {
return $this->data;
}
/**
* Add custom data to the tab data attributes
*
* @param array $data
*/
public function add_data(array $data): void {
$this->data = array_merge($this->data, $data);
}
}
+223
View File
@@ -0,0 +1,223 @@
<?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\output;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use context_system;
use core\external\output\icon_system\load_fontawesome_map;
/**
* This class contains a list of webservice functions related to output.
*
* @package core
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class external extends external_api {
/**
* Returns description of load_template() parameters.
*
* @return external_function_parameters
*/
public static function load_template_parameters() {
return new external_function_parameters(
array('component' => new external_value(PARAM_COMPONENT, 'component containing the template'),
'template' => new external_value(PARAM_SAFEPATH, 'name of the template'),
'themename' => new external_value(PARAM_ALPHANUMEXT, 'The current theme.'),
'includecomments' => new external_value(PARAM_BOOL, 'Include comments or not', VALUE_DEFAULT, false)
)
);
}
/**
* Return a mustache template, and all the strings it requires.
*
* @param string $component The component that holds the template.
* @param string $templatename The name of the template.
* @param string $themename The name of the current theme.
* @return string the template
*/
public static function load_template($component, $template, $themename, $includecomments = false) {
global $DB, $CFG, $PAGE;
$PAGE->set_context(context_system::instance());
$params = self::validate_parameters(self::load_template_parameters(),
array('component' => $component,
'template' => $template,
'themename' => $themename,
'includecomments' => $includecomments));
$loader = new mustache_template_source_loader();
// Will throw exceptions if the template does not exist.
return $loader->load(
$params['component'],
$params['template'],
$params['themename'],
$params['includecomments']
);
}
/**
* Returns description of load_template() result value.
*
* @return \core_external\external_description
*/
public static function load_template_returns() {
return new external_value(PARAM_RAW, 'template');
}
/**
* Returns description of load_template_with_dependencies() parameters.
*
* @return external_function_parameters
*/
public static function load_template_with_dependencies_parameters() {
return new external_function_parameters([
'component' => new external_value(PARAM_COMPONENT, 'component containing the template'),
'template' => new external_value(PARAM_SAFEPATH, 'name of the template'),
'themename' => new external_value(PARAM_ALPHANUMEXT, 'The current theme.'),
'includecomments' => new external_value(PARAM_BOOL, 'Include comments or not', VALUE_DEFAULT, false),
'lang' => new external_value(PARAM_LANG, 'lang', VALUE_DEFAULT, null),
]);
}
/**
* Return a mustache template, and all the child templates and strings it requires.
*
* @param string $component The component that holds the template.
* @param string $template The name of the template.
* @param string $themename The name of the current theme.
* @param bool $includecomments Whether to strip comments from the template source.
* @param string $lang moodle translation language, null means use current.
* @return string the template
*/
public static function load_template_with_dependencies(
string $component,
string $template,
string $themename,
bool $includecomments = false,
string $lang = null
) {
global $DB, $CFG, $PAGE;
$params = self::validate_parameters(
self::load_template_with_dependencies_parameters(),
[
'component' => $component,
'template' => $template,
'themename' => $themename,
'includecomments' => $includecomments,
'lang' => $lang
]
);
$loader = new mustache_template_source_loader();
// Will throw exceptions if the template does not exist.
$dependencies = $loader->load_with_dependencies(
$params['component'],
$params['template'],
$params['themename'],
$params['includecomments'],
[],
[],
$params['lang']
);
$formatdependencies = function($dependency) {
$results = [];
foreach ($dependency as $dependencycomponent => $dependencyvalues) {
foreach ($dependencyvalues as $dependencyname => $dependencyvalue) {
array_push($results, [
'component' => $dependencycomponent,
'name' => $dependencyname,
'value' => $dependencyvalue
]);
}
}
return $results;
};
// Now we have to unpack the dependencies into a format that can be returned
// by external functions (because they don't support dynamic keys).
return [
'templates' => $formatdependencies($dependencies['templates']),
'strings' => $formatdependencies($dependencies['strings'])
];
}
/**
* Returns description of load_template_with_dependencies() result value.
*
* @return \core_external\external_description
*/
public static function load_template_with_dependencies_returns() {
$resourcestructure = new external_single_structure([
'component' => new external_value(PARAM_COMPONENT, 'component containing the resource'),
'name' => new external_value(PARAM_TEXT, 'name of the resource'),
'value' => new external_value(PARAM_RAW, 'resource value')
]);
return new external_single_structure([
'templates' => new external_multiple_structure($resourcestructure),
'strings' => new external_multiple_structure($resourcestructure)
]);
}
/**
* Returns description of load_icon_map() parameters.
*
* @return external_function_parameters
*/
public static function load_fontawesome_icon_map_parameters() {
return new external_function_parameters([]);
}
/**
* Return a mapping of icon names to icons.
*
* @deprecated since Moodle 3.10
* @return array the mapping
*/
public static function load_fontawesome_icon_map() {
global $PAGE;
return load_fontawesome_map::execute($PAGE->theme->name);
}
/**
* Returns description of load_icon_map() result value.
*
* @return \core_external\external_description
*/
public static function load_fontawesome_icon_map_returns() {
return load_fontawesome_map::execute_returns();
}
/**
* The `load_fontawesome_icon_map` function has been replaced with
* @see load_fontawesome_map::execute()
*
* @return bool
*/
public static function load_fontawesome_icon_map_is_deprecated() {
return true;
}
}
+164
View File
@@ -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 class \core\output\icon_system
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use renderer_base;
use pix_icon;
defined('MOODLE_INTERNAL') || die();
/**
* Class allowing different systems for mapping and rendering icons.
*
* Possible icon styles are:
* 1. standard - image tags are generated which point to pix icons stored in a plugin pix folder.
* 2. fontawesome - font awesome markup is generated with the name of the icon mapped from the moodle icon name.
* 3. inline - inline tags are used for svg and png so no separate page requests are made (at the expense of page size).
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class icon_system {
/**
* @var string Default icon system.
*/
const STANDARD = '\\core\\output\\icon_system_standard';
/**
* @var string Default icon system.
*/
const FONTAWESOME = '\\core\\output\\icon_system_fontawesome';
/**
* @var \core\output\icon_system $instance The cached default instance
*/
private static $instance = null;
/**
* @var array $map A cached mapping of moodle icons to other icons
*/
private $map = null;
/**
* Constructor
*/
private function __construct() {
}
/**
* Factory method
*
* @param string $type Either a specific type, or null to get the default type.
* @return \core\output\icon_system
*/
final public static function instance($type = null) {
global $PAGE;
if (empty(self::$instance)) {
$iconsystem = $PAGE->theme->get_icon_system();
self::$instance = new $iconsystem();
}
if ($type === null) {
// No type specified. Return the icon system for the current theme.
return self::$instance;
}
if (!static::is_valid_system($type)) {
throw new \coding_exception("Invalid icon system requested '{$type}'");
}
if (is_a(self::$instance, $type) && is_a($type, get_class(self::$instance), true)) {
// The requested type is an exact match for the current icon system.
return self::$instance;
} else {
// Return the requested icon system.
return new $type();
}
}
/**
* Validate the theme config setting.
*
* @param string $system
* @return boolean
*/
final public static function is_valid_system($system) {
return class_exists($system) && is_a($system, static::class, true);
}
/**
* The name of an AMD module extending core/icon_system
*
* @return string
*/
abstract public function get_amd_name();
/**
* Render the pix icon according to the icon system.
*
* @param renderer_base $output
* @param pix_icon $icon
* @return string
*/
abstract public function render_pix_icon(renderer_base $output, pix_icon $icon);
/**
* Overridable function to get a mapping of all icons.
* Default is to do no mapping.
*/
public function get_icon_name_map() {
return [];
}
/**
* Overridable function to map the icon name to something else.
* Default is to do no mapping. Map is cached in the singleton.
*/
final public function remap_icon_name($iconname, $component) {
if ($this->map === null) {
$this->map = $this->get_icon_name_map();
}
if ($component == null || $component == 'moodle') {
$component = 'core';
} else if ($component != 'theme') {
$component = \core_component::normalize_componentname($component);
}
if (isset($this->map[$component . ':' . $iconname])) {
return $this->map[$component . ':' . $iconname];
}
return false;
}
/**
* Clears the instance cache, for use in unit tests
*/
public static function reset_caches() {
self::$instance = null;
}
}
+46
View File
@@ -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/>.
/**
* Contains class \core\output\icon_system
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
/**
* Class allowing different systems for mapping and rendering icons.
*
* Possible icon styles are:
* 1. standard - image tags are generated which point to pix icons stored in a plugin pix folder.
* 2. fontawesome - font awesome markup is generated with the name of the icon mapped from the moodle icon name.
* 3. inline - inline tags are used for svg and png so no separate page requests are made (at the expense of page size).
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class icon_system_font extends icon_system {
}
@@ -0,0 +1,522 @@
<?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 class \core\output\icon_system
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use renderer_base;
use pix_icon;
defined('MOODLE_INTERNAL') || die();
/**
* Class allowing different systems for mapping and rendering icons.
*
* Possible icon styles are:
* 1. standard - image tags are generated which point to pix icons stored in a plugin pix folder.
* 2. fontawesome - font awesome markup is generated with the name of the icon mapped from the moodle icon name.
* 3. inline - inline tags are used for svg and png so no separate page requests are made (at the expense of page size).
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class icon_system_fontawesome extends icon_system_font {
/**
* @var array $map Cached map of moodle icon names to font awesome icon names.
*/
private $map = [];
public function get_core_icon_map() {
return [
'core:docs' => 'fa-info-circle',
'core:book' => 'fa-book',
'core:help' => 'fa-question-circle text-info',
'core:req' => 'fa-exclamation-circle text-danger',
'core:a/add_file' => 'fa-file-o',
'core:a/create_folder' => 'fa-folder-o',
'core:a/download_all' => 'fa-download',
'core:a/help' => 'fa-question-circle text-info',
'core:a/logout' => 'fa-sign-out',
'core:a/refresh' => 'fa-refresh',
'core:a/search' => 'fa-search',
'core:a/setting' => 'fa-cog',
'core:a/view_icon_active' => 'fa-th',
'core:a/view_list_active' => 'fa-list',
'core:a/view_tree_active' => 'fa-folder',
'core:b/bookmark-new' => 'fa-bookmark',
'core:b/document-edit' => 'fa-pencil',
'core:b/document-new' => 'fa-file-o',
'core:b/document-properties' => 'fa-info',
'core:b/edit-copy' => 'fa-files-o',
'core:b/edit-delete' => 'fa-trash',
'core:e/abbr' => 'fa-comment',
'core:e/absolute' => 'fa-crosshairs',
'core:e/accessibility_checker' => 'fa-universal-access',
'core:e/acronym' => 'fa-comment',
'core:e/advance_hr' => 'fa-arrows-h',
'core:e/align_center' => 'fa-align-center',
'core:e/align_left' => 'fa-align-left',
'core:e/align_right' => 'fa-align-right',
'core:e/anchor' => 'fa-chain',
'core:e/backward' => 'fa-undo',
'core:e/bold' => 'fa-bold',
'core:e/bullet_list' => 'fa-list-ul',
'core:e/cancel' => 'fa-times',
'core:e/cancel_solid_circle' => 'fas fa-times-circle',
'core:e/cell_props' => 'fa-info',
'core:e/cite' => 'fa-quote-right',
'core:e/cleanup_messy_code' => 'fa-eraser',
'core:e/clear_formatting' => 'fa-i-cursor',
'core:e/copy' => 'fa-clone',
'core:e/cut' => 'fa-scissors',
'core:e/decrease_indent' => 'fa-outdent',
'core:e/delete_col' => 'fa-minus',
'core:e/delete_row' => 'fa-minus',
'core:e/delete' => 'fa-minus',
'core:e/delete_table' => 'fa-minus',
'core:e/document_properties' => 'fa-info',
'core:e/emoticons' => 'fa-smile-o',
'core:e/find_replace' => 'fa-search-plus',
'core:e/file-text' => 'fa-file-text',
'core:e/forward' => 'fa-arrow-right',
'core:e/fullpage' => 'fa-arrows-alt',
'core:e/fullscreen' => 'fa-arrows-alt',
'core:e/help' => 'fa-question-circle',
'core:e/increase_indent' => 'fa-indent',
'core:e/insert_col_after' => 'fa-columns',
'core:e/insert_col_before' => 'fa-columns',
'core:e/insert_date' => 'fa-calendar',
'core:e/insert_edit_image' => 'fa-picture-o',
'core:e/insert_edit_link' => 'fa-link',
'core:e/insert_edit_video' => 'fa-file-video-o',
'core:e/insert_file' => 'fa-file',
'core:e/insert_horizontal_ruler' => 'fa-arrows-h',
'core:e/insert_nonbreaking_space' => 'fa-square-o',
'core:e/insert_page_break' => 'fa-level-down',
'core:e/insert_row_after' => 'fa-plus',
'core:e/insert_row_before' => 'fa-plus',
'core:e/insert' => 'fa-plus',
'core:e/insert_time' => 'fa-clock-o',
'core:e/italic' => 'fa-italic',
'core:e/justify' => 'fa-align-justify',
'core:e/layers_over' => 'fa-level-up',
'core:e/layers' => 'fa-window-restore',
'core:e/layers_under' => 'fa-level-down',
'core:e/left_to_right' => 'fa-chevron-right',
'core:e/manage_files' => 'fa-files-o',
'core:e/math' => 'fa-calculator',
'core:e/merge_cells' => 'fa-compress',
'core:e/new_document' => 'fa-file-o',
'core:e/numbered_list' => 'fa-list-ol',
'core:e/page_break' => 'fa-level-down',
'core:e/paste' => 'fa-clipboard',
'core:e/paste_text' => 'fa-clipboard',
'core:e/paste_word' => 'fa-clipboard',
'core:e/prevent_autolink' => 'fa-exclamation',
'core:e/preview' => 'fa-search-plus',
'core:e/print' => 'fa-print',
'core:e/question' => 'fa-question',
'core:e/redo' => 'fa-repeat',
'core:e/remove_link' => 'fa-chain-broken',
'core:e/remove_page_break' => 'fa-remove',
'core:e/resize' => 'fa-expand',
'core:e/restore_draft' => 'fa-undo',
'core:e/restore_last_draft' => 'fa-undo',
'core:e/right_to_left' => 'fa-chevron-left',
'core:e/row_props' => 'fa-info',
'core:e/save' => 'fa-floppy-o',
'core:e/screenreader_helper' => 'fa-braille',
'core:e/search' => 'fa-search',
'core:e/select_all' => 'fa-arrows-h',
'core:e/show_invisible_characters' => 'fa-eye-slash',
'core:e/source_code' => 'fa-code',
'core:e/special_character' => 'fa-pencil-square-o',
'core:e/spellcheck' => 'fa-check',
'core:e/split_cells' => 'fa-columns',
'core:e/strikethrough' => 'fa-strikethrough',
'core:e/styleparagraph' => 'fa-font',
'core:e/subscript' => 'fa-subscript',
'core:e/superscript' => 'fa-superscript',
'core:e/table_props' => 'fa-table',
'core:e/table' => 'fa-table',
'core:e/template' => 'fa-sticky-note',
'core:e/text_color_picker' => 'fa-paint-brush',
'core:e/text_color' => 'fa-paint-brush',
'core:e/text_highlight_picker' => 'fa-lightbulb-o',
'core:e/text_highlight' => 'fa-lightbulb-o',
'core:e/tick' => 'fa-check',
'core:e/toggle_blockquote' => 'fa-quote-left',
'core:e/underline' => 'fa-underline',
'core:e/undo' => 'fa-undo',
'core:e/visual_aid' => 'fa-universal-access',
'core:e/visual_blocks' => 'fa-audio-description',
'theme:fp/add_file' => 'fa-file-o',
'theme:fp/alias' => 'fa-share',
'theme:fp/alias_sm' => 'fa-share',
'theme:fp/check' => 'fa-check',
'theme:fp/create_folder' => 'fa-folder-o',
'theme:fp/cross' => 'fa-remove',
'theme:fp/download_all' => 'fa-download',
'theme:fp/help' => 'fa-question-circle',
'theme:fp/link' => 'fa-link',
'theme:fp/link_sm' => 'fa-link',
'theme:fp/logout' => 'fa-sign-out',
'theme:fp/path_folder' => 'fa-folder',
'theme:fp/path_folder_rtl' => 'fa-folder',
'theme:fp/refresh' => 'fa-refresh',
'theme:fp/search' => 'fa-search',
'theme:fp/setting' => 'fa-cog',
'theme:fp/view_icon_active' => 'fa-th',
'theme:fp/view_list_active' => 'fa-list',
'theme:fp/view_tree_active' => 'fa-folder',
'core:i/activities' => 'fa-file-pen',
'core:i/addblock' => 'fa-plus-square',
'core:i/assignroles' => 'fa-user-plus',
'core:i/asterisk' => 'fa-asterisk',
'core:i/backup' => 'fa-file-zip-o',
'core:i/badge' => 'fa-shield',
'core:i/breadcrumbdivider' => 'fa-angle-right',
'core:i/bullhorn' => 'fa-bullhorn',
'core:i/calc' => 'fa-calculator',
'core:i/calendar' => 'fa-calendar',
'core:i/calendareventdescription' => 'fa-align-left',
'core:i/calendareventtime' => 'fa-clock-o',
'core:i/caution' => 'fa-exclamation text-warning',
'core:i/checked' => 'fa-check',
'core:i/checkedcircle' => 'fa-check-circle',
'core:i/checkpermissions' => 'fa-unlock-alt',
'core:i/cohort' => 'fa-users',
'core:i/competencies' => 'fa-check-square-o',
'core:i/completion_self' => 'fa-user-o',
'core:i/contentbank' => 'fa-paint-brush',
'core:i/dashboard' => 'fa-tachometer',
'core:i/categoryevent' => 'fa-cubes',
'core:i/chartbar' => 'fa-chart-bar',
'core:i/course' => 'fa-graduation-cap',
'core:i/courseevent' => 'fa-graduation-cap',
'core:i/cloudupload' => 'fa-cloud-upload',
'core:i/customfield' => 'fa-hand-o-right',
'core:i/db' => 'fa-database',
'core:i/delete' => 'fa-trash',
'core:i/down' => 'fa-arrow-down',
'core:i/dragdrop' => 'fa-arrows',
'core:i/duration' => 'fa-clock-o',
'core:i/emojicategoryactivities' => 'fa-futbol-o',
'core:i/emojicategoryanimalsnature' => 'fa-leaf',
'core:i/emojicategoryflags' => 'fa-flag',
'core:i/emojicategoryfooddrink' => 'fa-cutlery',
'core:i/emojicategoryobjects' => 'fa-lightbulb-o',
'core:i/emojicategorypeoplebody' => 'fa-male',
'core:i/emojicategoryrecent' => 'fa-clock-o',
'core:i/emojicategorysmileysemotion' => 'fa-smile-o',
'core:i/emojicategorysymbols' => 'fa-heart',
'core:i/emojicategorytravelplaces' => 'fa-plane',
'core:i/edit' => 'fa-pencil',
'core:i/email' => 'fa-envelope',
'core:i/empty' => 'fa-fw',
'core:i/enrolmentsuspended' => 'fa-pause',
'core:i/enrolusers' => 'fa-user-plus',
'core:i/excluded' => 'fa-minus-circle',
'core:i/expired' => 'fa-exclamation text-warning',
'core:i/export' => 'fa-download',
'core:i/link' => 'fa-link',
'core:i/externallink' => 'fa-external-link',
'core:i/files' => 'fa-file',
'core:i/filter' => 'fa-filter',
'core:i/flagged' => 'fa-flag',
'core:i/folder' => 'fa-folder',
'core:i/grade_correct' => 'fa-check text-success',
'core:i/grade_incorrect' => 'fa-remove text-danger',
'core:i/grade_partiallycorrect' => 'fa-check-square',
'core:i/grades' => 'fa-table',
'core:i/grading' => 'fa-magic',
'core:i/gradingnotifications' => 'fa-bell-o',
'core:i/groupevent' => 'fa-group',
'core:i/group' => 'fa-users',
'core:i/home' => 'fa-home',
'core:i/hide' => 'fa-eye',
'core:i/hierarchylock' => 'fa-lock',
'core:i/import' => 'fa-level-up',
'core:i/incorrect' => 'fa-exclamation',
'core:i/info' => 'fa-info',
'core:i/invalid' => 'fa-times text-danger',
'core:i/item' => 'fa-circle',
'core:i/loading' => 'fa-circle-o-notch fa-spin fa-sm',
'core:i/loading_small' => 'fa-circle-o-notch fa-spin fa-sm',
'core:i/location' => 'fa-map-marker',
'core:i/lock' => 'fa-lock',
'core:i/log' => 'fa-list-alt',
'core:i/mahara_host' => 'fa-id-badge',
'core:i/manual_item' => 'fa-pencil-square-o',
'core:i/marked' => 'fa-circle',
'core:i/marker' => 'fa-circle-o',
'core:i/mean' => 'fa-calculator',
'core:i/menu' => 'fa-ellipsis-v',
'core:i/menubars' => 'fa-bars',
'core:i/messagecontentaudio' => 'fa-headphones',
'core:i/messagecontentimage' => 'fa-image',
'core:i/messagecontentvideo' => 'fa-film',
'core:i/messagecontentmultimediageneral' => 'fa-file-video-o',
'core:i/mnethost' => 'fa-external-link',
'core:i/moodle_host' => 'fa-graduation-cap',
'core:i/moremenu' => 'fa-ellipsis-h',
'core:i/move_2d' => 'fa-arrows',
'core:i/muted' => 'fa-microphone-slash',
'core:i/navigationitem' => 'fa-fw',
'core:i/ne_red_mark' => 'fa-remove',
'core:i/new' => 'fa-bolt',
'core:i/news' => 'fa-newspaper-o',
'core:i/next' => 'fa-chevron-right',
'core:i/nosubcat' => 'fa-plus-square-o',
'core:i/notifications' => 'fa-bell-o',
'core:i/open' => 'fa-folder-open',
'core:i/otherevent' => 'fa-calendar',
'core:i/outcomes' => 'fa-tasks',
'core:i/overriden_grade' => 'fa-edit',
'core:i/payment' => 'fa-money',
'core:i/permissionlock' => 'fa-lock',
'core:i/permissions' => 'fa-pencil-square-o',
'core:i/persona_sign_in_black' => 'fa-male',
'core:i/portfolio' => 'fa-id-badge',
'core:i/preview' => 'fa-search-plus',
'core:i/previous' => 'fa-chevron-left',
'core:i/privatefiles' => 'fa-file-o',
'core:i/progressbar' => 'fa-spinner fa-spin',
'core:i/publish' => 'fa-share',
'core:i/questions' => 'fa-question',
'core:i/reload' => 'fa-refresh',
'core:i/report' => 'fa-area-chart',
'core:i/repository' => 'fa-hdd-o',
'core:i/restore' => 'fa-level-up',
'core:i/return' => 'fa-arrow-left',
'core:i/risk_config' => 'fa-exclamation text-muted',
'core:i/risk_managetrust' => 'fa-exclamation-triangle text-warning',
'core:i/risk_personal' => 'fa-exclamation-circle text-info',
'core:i/risk_spam' => 'fa-exclamation text-primary',
'core:i/risk_xss' => 'fa-exclamation-triangle text-danger',
'core:i/role' => 'fa-user-md',
'core:i/rss' => 'fa-rss',
'core:i/rsssitelogo' => 'fa-graduation-cap',
'core:i/scales' => 'fa-balance-scale',
'core:i/scheduled' => 'fa-calendar-check-o',
'core:i/search' => 'fa-search',
'core:i/section' => 'fa-folder-o',
'core:i/sendmessage' => 'fa-paper-plane',
'core:i/settings' => 'fa-cog',
'core:i/share' => 'fa-share-square-o',
'core:i/show' => 'fa-eye-slash',
'core:i/siteevent' => 'fa-globe',
'core:i/star' => 'fa-star',
'core:i/star-o' => 'fa-star-o',
'core:i/star-rating' => 'fa-star',
'core:i/stats' => 'fa-line-chart',
'core:i/switch' => 'fa-exchange',
'core:i/switchrole' => 'fa-user-secret',
'core:i/trash' => 'fa-trash',
'core:i/twoway' => 'fa-arrows-h',
'core:i/unchecked' => 'fa-square-o',
'core:i/uncheckedcircle' => 'fa-circle-o',
'core:i/unflagged' => 'fa-flag-o',
'core:i/unlock' => 'fa-unlock',
'core:i/up' => 'fa-arrow-up',
'core:i/upload' => 'fa-upload',
'core:i/userevent' => 'fa-user',
'core:i/user' => 'fa-user',
'core:i/users' => 'fa-users',
'core:i/valid' => 'fa-check text-success',
'core:i/viewsection' => 'fa-pager',
'core:i/warning' => 'fa-exclamation text-warning',
'core:i/window_close' => 'fa-window-close',
'core:i/withsubcat' => 'fa-plus-square',
'core:i/language' => 'fa-language',
'core:m/USD' => 'fa-usd',
'core:t/addcontact' => 'fa-address-card',
'core:t/add' => 'fa-plus',
'core:t/angles-down' => 'fa-angles-down',
'core:t/angles-left' => 'fa-angles-left',
'core:t/angles-right' => 'fa-angles-right',
'core:t/angles-up' => 'fa-angles-up',
'core:t/approve' => 'fa-thumbs-up',
'core:t/assignroles' => 'fa-user-circle',
'core:t/award' => 'fa-trophy',
'core:t/backpack' => 'fa-shopping-bag',
'core:t/backup' => 'fa-arrow-circle-down',
'core:t/block' => 'fa-ban',
'core:t/block_to_dock_rtl' => 'fa-chevron-right',
'core:t/block_to_dock' => 'fa-chevron-left',
'core:t/blocks_drawer' => 'fa-chevron-left',
'core:t/blocks_drawer_rtl' => 'fa-chevron-right',
'core:t/calc_off' => 'fa-calculator', // TODO: Change to better icon once we have stacked icon support or more icons.
'core:t/calc' => 'fa-calculator',
'core:t/check' => 'fa-check',
'core:t/clipboard' => 'fa-clipboard',
'core:t/cohort' => 'fa-users',
'core:t/collapsed_empty_rtl' => 'fa-caret-square-o-left',
'core:t/collapsed_empty' => 'fa-caret-square-o-right',
'core:t/collapsed_rtl' => 'fa-caret-left',
'core:t/collapsed' => 'fa-caret-right',
'core:t/collapsedcaret' => 'fa-caret-right',
'core:t/collapsedchevron' => 'fa-chevron-right',
'core:t/collapsedchevron_rtl' => 'fa-chevron-left',
'core:t/collapsedchevron_up' => 'fa-chevron-up',
'core:t/completion_complete' => 'fa-circle',
'core:t/completion_fail' => 'fa-times',
'core:t/completion_incomplete' => 'fa-circle-thin',
'core:t/contextmenu' => 'fa-cog',
'core:t/copy' => 'fa-copy',
'core:t/delete' => 'fa-trash',
'core:t/dockclose' => 'fa-window-close',
'core:t/dock_to_block_rtl' => 'fa-chevron-right',
'core:t/dock_to_block' => 'fa-chevron-left',
'core:t/download' => 'fa-download',
'core:t/down' => 'fa-arrow-down',
'core:t/downlong' => 'fa-long-arrow-down',
'core:t/dropdown' => 'fa-cog',
'core:t/editinline' => 'fa-pencil',
'core:t/edit_menu' => 'fa-cog',
'core:t/editstring' => 'fa-pencil',
'core:t/edit' => 'fa-cog',
'core:t/emailno' => 'fa-ban',
'core:t/email' => 'fa-envelope-o',
'core:t/emptystar' => 'fa-star-o',
'core:t/enrolusers' => 'fa-user-plus',
'core:t/expanded' => 'fa-caret-down',
'core:t/expandedchevron' => 'fa-chevron-down',
'core:t/go' => 'fa-play',
'core:t/grades' => 'fa-table',
'core:t/groupn' => 'fa-user',
'core:t/groups' => 'fa-user-circle',
'core:t/groupv' => 'fa-user-group',
'core:t/hide' => 'fa-eye',
'core:t/index_drawer' => 'fa-list',
'core:t/left' => 'fa-arrow-left',
'core:t/less' => 'fa-caret-up',
'core:t/life-ring' => 'fa-life-ring',
'core:t/locked' => 'fa-lock',
'core:t/lock' => 'fa-unlock',
'core:t/locktime' => 'fa-lock',
'core:t/markasread' => 'fa-check',
'core:t/messages' => 'fa-comments',
'core:t/messages-o' => 'fa-comments-o',
'core:t/message' => 'fa-comment-o',
'core:t/more' => 'fa-caret-down',
'core:t/move' => 'fa-arrows-v',
'core:t/online' => 'fa-circle',
'core:t/passwordunmask-edit' => 'fa-pencil',
'core:t/passwordunmask-reveal' => 'fa-eye',
'core:t/play' => 'fa-play',
'core:t/portfolioadd' => 'fa-plus',
'core:t/preferences' => 'fa-wrench',
'core:t/preview' => 'fa-search-plus',
'core:t/print' => 'fa-print',
'core:t/removecontact' => 'fa-user-times',
'core:t/reload' => 'fa-refresh',
'core:t/reset' => 'fa-repeat',
'core:t/restore' => 'fa-arrow-circle-up',
'core:t/right' => 'fa-arrow-right',
'core:t/sendmessage' => 'fa-paper-plane',
'core:t/show' => 'fa-eye-slash',
'core:t/sort_by' => 'fa-sort-amount-asc',
'core:t/sort_asc' => 'fa-sort-asc',
'core:t/sort_desc' => 'fa-sort-desc',
'core:t/sort' => 'fa-sort',
'core:t/stealth' => 'fa-low-vision',
'core:t/stop' => 'fa-stop',
'core:t/switch_minus' => 'fa-minus',
'core:t/switch_plus' => 'fa-plus',
'core:t/switch_whole' => 'fa-square-o',
'core:t/tags' => 'fa-tags',
'core:t/unblock' => 'fa-commenting',
'core:t/unlocked' => 'fa-unlock-alt',
'core:t/unlock' => 'fa-lock',
'core:t/up' => 'fa-arrow-up',
'core:t/uplong' => 'fa-long-arrow-up',
'core:t/user' => 'fa-user',
'core:t/viewdetails' => 'fa-list',
];
}
/**
* Overridable function to get a mapping of all icons.
* Default is to do no mapping.
*/
public function get_icon_name_map() {
if ($this->map === []) {
$cache = \cache::make('core', 'fontawesomeiconmapping');
// Create different mapping keys for different icon system classes, there may be several different
// themes on the same site.
$mapkey = 'mapping_'.preg_replace('/[^a-zA-Z0-9_]/', '_', get_class($this));
$this->map = $cache->get($mapkey);
if (empty($this->map)) {
$this->map = $this->get_core_icon_map();
$callback = 'get_fontawesome_icon_map';
if ($pluginsfunction = get_plugins_with_function($callback)) {
foreach ($pluginsfunction as $plugintype => $plugins) {
foreach ($plugins as $pluginfunction) {
$pluginmap = $pluginfunction();
$this->map += $pluginmap;
}
}
}
$cache->set($mapkey, $this->map);
}
}
return $this->map;
}
public function get_amd_name() {
return 'core/icon_system_fontawesome';
}
public function render_pix_icon(renderer_base $output, pix_icon $icon) {
$subtype = 'pix_icon_fontawesome';
$subpix = new $subtype($icon);
$data = $subpix->export_for_template($output);
if (!$subpix->is_mapped()) {
$data['unmappedIcon'] = $icon->export_for_template($output);
}
if (isset($icon->attributes['aria-hidden'])) {
$data['aria-hidden'] = $icon->attributes['aria-hidden'];
}
// Flip question mark icon orientation when the `questioniconfollowlangdirection` lang config string is set to `yes`.
$isquestionicon = strpos($data['key'], 'fa-question') !== false;
if ($isquestionicon && right_to_left() && get_string('questioniconfollowlangdirection', 'langconfig') === 'yes') {
$data['extraclasses'] = "fa-flip-horizontal";
}
return $output->render_from_template('core/pix_icon_fontawesome', $data);
}
}
@@ -0,0 +1,53 @@
<?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 class \core\output\icon_system_standard
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use renderer_base;
use pix_icon;
defined('MOODLE_INTERNAL') || die();
/**
* Standard icon rendering. No mapping - img tags used.
*
* @package core
* @category output
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class icon_system_standard extends icon_system {
public function render_pix_icon(renderer_base $output, pix_icon $icon) {
$data = $icon->export_for_template($output);
return $output->render_from_template('core/pix_icon', $data);
}
public function get_amd_name() {
return 'core/icon_system_standard';
}
}
+284
View File
@@ -0,0 +1,284 @@
<?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 class \core\output\inplace_editable
*
* @package core
* @category output
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use templatable;
use renderable;
use lang_string;
use pix_icon;
/**
* Class allowing to quick edit a title inline
*
* This class is used for displaying an element that can be in-place edited by the user. To display call:
* echo $OUTPUT->render($element);
* or
* echo $OUTPUT->render_from_template('core/inplace_editable', $element->export_for_template($OUTPUT));
*
* Template core/inplace_editable will automatically load javascript module with the same name
* core/inplace_editable. Javascript module registers a click-listener on edit link and
* then replaces the displayed value with an input field. On "Enter" it sends a request
* to web service core_update_inplace_editable, which invokes the callback from the component.
* Any exception thrown by the web service (or callback) is displayed as an error popup.
*
* Callback {$component}_inplace_editable($itemtype, $itemid, $newvalue) must be present in the lib.php file of
* the component or plugin. It must return instance of this class.
*
* @package core
* @category output
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class inplace_editable implements templatable, renderable {
/**
* @var string component responsible for diplsying/updating
*/
protected $component = null;
/**
* @var string itemtype inside the component
*/
protected $itemtype = null;
/**
* @var int identifier of the editable element (usually database id)
*/
protected $itemid = null;
/**
* @var string value of the editable element as it is present in the database
*/
protected $value = null;
/**
* @var string value of the editable element as it should be displayed,
* must be formatted and may contain links or other html tags
*/
protected $displayvalue = null;
/**
* @var string label for the input element (for screenreaders)
*/
protected $editlabel = null;
/**
* @var string hint for the input element (for screenreaders)
*/
protected $edithint = null;
/**
* @var pix_icon icon to use to toggle editing
*/
protected $editicon = null;
/**
* @var bool indicates if the current user is allowed to edit this element - set in constructor after permissions are checked
*/
protected $editable = false;
/**
* @var string type of the element - text, toggle or select
*/
protected $type = 'text';
/**
* @var string options for the element, for example new value for the toggle or json-encoded list of options for select
*/
protected $options = '';
/**
* Constructor.
*
* @param string $component name of the component or plugin responsible for the updating of the value (must declare callback)
* @param string $itemtype type of the item inside the component - each component/plugin may implement multiple inplace-editable elements
* @param int $itemid identifier of the item that can be edited in-place
* @param bool $editable whether this value is editable (check capabilities and editing mode), if false, only "displayvalue"
* will be displayed without anything else
* @param string $displayvalue what needs to be displayed to the user, it must be cleaned, with applied filters (call
* {@link format_string()}). It may be wrapped in an html link, contain icons or other decorations
* @param string $value what needs to be edited - usually raw value from the database, it may contain multilang tags
* @param lang_string|string $edithint hint (title) that will be displayed under the edit link
* @param lang_string|string $editlabel label for the input element in the editing mode (for screenreaders)
* @param pix_icon|null $editicon icon to use to toggle editing
*/
public function __construct($component, $itemtype, $itemid, $editable,
$displayvalue, $value = null, $edithint = null, $editlabel = null, ?pix_icon $editicon = null) {
$this->component = $component;
$this->itemtype = $itemtype;
$this->itemid = $itemid;
$this->editable = $editable;
$this->displayvalue = $displayvalue;
$this->value = $value;
$this->edithint = $edithint;
$this->editlabel = $editlabel;
$this->editicon = $editicon;
}
/**
* Sets the element type to be a toggle
*
* For toggle element $editlabel is not used.
* $displayvalue must be specified, it can have text or icons but can not contain html links.
*
* Toggle element can have two or more options.
*
* @param array $options toggle options as simple, non-associative array; defaults to array(0,1)
* @return self
*/
public function set_type_toggle($options = null) {
if ($options === null) {
$options = array(0, 1);
}
$options = array_values($options);
$idx = array_search($this->value, $options, true);
if ($idx === false) {
throw new \coding_exception('Specified value must be one of the toggle options');
}
$nextvalue = ($idx < count($options) - 1) ? $idx + 1 : 0;
$this->type = 'toggle';
$this->options = (string)$nextvalue;
return $this;
}
/**
* Sets the element type to be a dropdown
*
* For select element specifying $displayvalue is optional, if null it will
* be assumed that $displayvalue = $options[$value].
* However displayvalue can still be specified if it needs icons and/or
* html links.
*
* If only one option specified, the element will not be editable.
*
* @param array $options associative array with dropdown options
* @return self
*/
public function set_type_select($options) {
if (!array_key_exists($this->value, $options)) {
throw new \coding_exception('Options for select element must contain an option for the specified value');
}
if (count($options) < 2) {
$this->editable = false;
}
$this->type = 'select';
$pairedoptions = [];
foreach ($options as $key => $value) {
$pairedoptions[] = [
'key' => $key,
'value' => $value,
];
}
$this->options = json_encode($pairedoptions);
if ($this->displayvalue === null) {
$this->displayvalue = $options[$this->value];
}
if ($this->editicon === null) {
$this->editicon = new pix_icon('t/expanded', (string) $this->edithint);
}
return $this;
}
/**
* Sets the element type to be an autocomplete field
*
* @param array $options associative array with dropdown options
* @param array $attributes associative array with attributes for autoselect field. See AMD module core/form-autocomplete.
* @return self
*/
public function set_type_autocomplete($options, $attributes) {
$this->type = 'autocomplete';
$pairedoptions = [];
foreach ($options as $key => $value) {
$pairedoptions[] = [
'key' => $key,
'value' => $value,
];
}
$this->options = json_encode(['options' => $pairedoptions, 'attributes' => $attributes]);
return $this;
}
/**
* Whether the link should contain all of the content or not.
*/
protected function get_linkeverything() {
if ($this->type === 'toggle') {
return true;
}
if (preg_match('#<a .*>.*</a>#', $this->displayvalue) === 1) {
return false;
}
return true;
}
/**
* 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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output) {
if (!$this->editable) {
return array(
'displayvalue' => (string)$this->displayvalue
);
}
if ($this->editicon === null) {
$this->editicon = new pix_icon('t/editstring', (string) $this->edithint);
}
return array(
'component' => $this->component,
'itemtype' => $this->itemtype,
'itemid' => $this->itemid,
'displayvalue' => (string)$this->displayvalue,
'value' => (string)$this->value,
'edithint' => (string)$this->edithint,
'editlabel' => (string)$this->editlabel,
'editicon' => $this->editicon->export_for_pix(),
'type' => $this->type,
'options' => $this->options,
'linkeverything' => $this->get_linkeverything() ? 1 : 0,
);
}
/**
* Renders this element
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return string
*/
public function render(\renderer_base $output) {
return $output->render_from_template('core/inplace_editable', $this->export_for_template($output));
}
}
+179
View File
@@ -0,0 +1,179 @@
<?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 class \core\output\language_menu
*
* @package core
* @category output
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Class for creating the language menu
*
* @package core
* @category output
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class language_menu implements \renderable, \templatable {
/** @var \moodle_page $page the moodle page that the languague menu belongs to */
protected $page;
/** @var string current language code */
protected $currentlang;
/** @var array localised list of installed translations */
protected $langs;
/**
* Language menu constructor.
*
* @param \moodle_page $page the moodle page that the languague menu belongs to.
*/
public function __construct($page) {
$this->page = $page;
$this->currentlang = \current_language();
$this->langs = \get_string_manager()->get_list_of_translations();
}
/**
* Determine if the language menu should be shown.
*
* @return bool true if the language menu should be shown.
*/
protected function show_language_menu(): bool {
global $CFG;
if (empty($CFG->langmenu)) {
return false;
}
if ($this->page->course != SITEID and !empty($this->page->course->lang)) {
// Do not show lang menu if language forced.
return false;
}
if (count($this->langs) < 2) {
return false;
}
return true;
}
/**
* Export the data.
*
* @param \renderer_base $output
* @return array with the title for the menu and an array of items.
*/
public function export_for_template(\renderer_base $output): array {
// Early return if a lang menu does not exists.
if (!$this->show_language_menu()) {
return [];
}
$nodes = [];
$activelanguage = '';
// Add the lang picker if needed.
foreach ($this->langs as $langtype => $langname) {
$isactive = $langtype == $this->currentlang;
$attributes = [];
if (!$isactive) {
// Set the lang attribute for languages different from the page's current language.
$attributes[] = [
'key' => 'lang',
'value' => get_html_lang_attribute_value($langtype),
];
}
$node = [
'title' => $langname,
'text' => $langname,
'link' => true,
'isactive' => $isactive,
'url' => $isactive ? new \moodle_url('#') : new \moodle_url($this->page->url, ['lang' => $langtype]),
];
if (!empty($attributes)) {
$node['attributes'] = $attributes;
}
$nodes[] = $node;
if ($isactive) {
$activelanguage = $langname;
}
}
return [
'title' => $activelanguage,
'items' => $nodes,
];
}
/**
* Export the data providing a structure for the core/action_menu template.
*
* @param \renderer_base $output
* @return \stdClass action_menu data export.
*/
public function export_for_action_menu(\renderer_base $output): ?\stdClass {
$languagedata = $this->export_for_template($output);
if (empty($languagedata)) {
return null;
}
$langmenu = new \action_menu();
$menuname = \get_string('language');
if (!empty($languagedata['title'])) {
$menuname = $languagedata['title'];
}
$langmenu->set_menu_trigger($menuname);
foreach ($languagedata['items'] as $node) {
$langparam = $node['url']->get_param('lang');
$attributes = [];
if ($langparam) {
$attributes = [
'data-lang' => $langparam,
'lang' => get_html_lang_attribute_value($langparam),
];
}
$lang = new \action_menu_link_secondary($node['url'], null, $node['title'], $attributes);
$langmenu->add($lang);
}
return $langmenu->export_for_template($output);
}
/**
* Export the data providing a structure for the core/single_select template.
*
* @param \renderer_base $output
* @return \stdClass single_select data export.
*/
public function export_for_single_select(\renderer_base $output): ?\stdClass {
if (!$this->show_language_menu()) {
return null;
}
$singleselect = new \single_select($this->page->url, 'lang', $this->langs, $this->currentlang, null);
$singleselect->label = get_accesshide(\get_string('language'));
$singleselect->class = 'langmenu';
return $singleselect->export_for_template($output);
}
}
@@ -0,0 +1,82 @@
<?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\output\local\action_menu;
use action_link;
use pix_icon;
use renderable;
use stdClass;
/**
* Interface to a subpanel implementation.
*
* @package core_admin
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class subpanel extends action_link implements renderable {
/**
* The subpanel content.
* @var renderable
*/
protected $subpanel;
/**
* The number of instances of this action menu link (and its subclasses).
* @var int
*/
protected static $instance = 1;
/**
* Constructor.
* @param string $text the text to display
* @param renderable $subpanel the subpanel content
* @param array|null $attributes an optional array of attributes
* @param pix_icon|null $icon an optional icon
*/
public function __construct(
$text,
renderable $subpanel,
array $attributes = null,
pix_icon $icon = null
) {
$this->text = $text;
$this->subpanel = $subpanel;
if (empty($attributes['id'])) {
$attributes['id'] = \html_writer::random_id('action_menu_submenu');
}
$this->attributes = (array) $attributes;
$this->icon = $icon;
}
/**
* Export this object for template rendering.
* @param \renderer_base $output the output renderer
* @return stdClass
*/
public function export_for_template(\renderer_base $output): stdClass {
$data = parent::export_for_template($output);
$data->instance = self::$instance++;
$data->subpanelcontent = $output->render($this->subpanel);
// The menu trigger icon collides with the subpanel item icon. Unlike regular menu items,
// subpanel items usually does not use icons. To prevent the collision, subpanels use a diferent
// context variable for item icon.
$data->itemicon = $data->icon;
unset($data->icon);
return $data;
}
}
@@ -0,0 +1,260 @@
<?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\output\local\dropdown;
use core\output\named_templatable;
use renderable;
/**
* Class to render a dropdown dialog element.
*
* A dropdown dialog allows to render any arbitrary HTML into a dropdown elements triggered
* by a button.
*
* @package core
* @category output
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dialog implements named_templatable, renderable {
/** Dropdown dialog positions. */
public const POSITION = [
'start' => 'dropdown-menu-left',
'end' => 'dropdown-menu-right',
];
/** Dropdown dialog positions. */
public const WIDTH = [
'default' => '',
'big' => 'dialog-big',
'small' => 'dialog-small',
];
/**
* @var string content of dialog.
*/
protected $dialogcontent = '';
/**
* @var bool if the footer should auto enable or not.
*/
protected $buttoncontent = true;
/**
* @var string trigger button CSS classes.
*/
protected $buttonclasses = '';
/**
* @var string component CSS classes.
*/
protected $classes = '';
/**
* @var string the dropdown position.
*/
protected $dropdownposition = self::POSITION['start'];
/**
* @var string dropdown preferred width.
*/
protected $dropdownwidth = self::WIDTH['default'];
/**
* @var array extra HTML attributes (attribute => value).
*/
protected $extras = [];
/**
* @var bool if the element is disabled.
*/
protected $disabled = false;
/**
* Constructor.
*
* The definition object could contain the following keys:
* - classes: component CSS classes.
* - buttonclasses: the button CSS classes.
* - dialogwidth: the dropdown width.
* - dropdownposition: the dropdown position.
* - extras: extra HTML attributes (attribute => value).
*
* @param string $buttoncontent the button content
* @param string $dialogcontent the footer content
* @param array $definition an optional array of the element definition
*/
public function __construct(string $buttoncontent, string $dialogcontent, array $definition = []) {
$this->buttoncontent = $buttoncontent;
$this->dialogcontent = $dialogcontent;
if (isset($definition['classes'])) {
$this->classes = $definition['classes'];
}
if (isset($definition['buttonclasses'])) {
$this->buttonclasses = $definition['buttonclasses'];
}
if (isset($definition['extras'])) {
$this->extras = $definition['extras'];
}
if (isset($definition['dialogwidth'])) {
$this->dropdownwidth = $definition['dialogwidth'];
}
if (isset($definition['dropdownposition'])) {
$this->dropdownposition = $definition['dropdownposition'];
}
}
/**
* Set the dialog contents.
*
* @param string $dialogcontent
*/
public function set_content(string $dialogcontent) {
$this->dialogcontent = $dialogcontent;
}
/**
* Set the button contents.
*
* @param string $buttoncontent
* @param string|null $buttonclasses the button classes
*/
public function set_button(string $buttoncontent, ?string $buttonclasses = null) {
$this->buttoncontent = $buttoncontent;
if ($buttonclasses !== null) {
$this->buttonclasses = $buttonclasses;
}
}
/**
* Set the dialog width.
*
* @param string $width
*/
public function set_dialog_width(string $width) {
$this->dropdownwidth = $width;
}
/**
* Add extra classes to trigger butotn.
*
* @param string $buttonclasses the extra classes
*/
public function set_button_classes(string $buttonclasses) {
$this->buttonclasses = $buttonclasses;
}
/**
* Add extra classes to the component.
*
* @param string $classes the extra classes
*/
public function set_classes(string $classes) {
$this->classes = $classes;
}
/**
* Add extra extras to the sticky footer element.
*
* @param string $attribute the extra attribute
* @param string $value the value
*/
public function add_extra(string $attribute, string $value) {
$this->extras[$attribute] = $value;
}
/**
* Set the button element id.
*
* @param string $value the value
*/
public function add_button_id(string $value) {
$this->extras['buttonid'] = $value;
}
/**
* Set the dropdown position.
* @param string $position the position
*/
public function set_position(string $position) {
$this->dropdownposition = $position;
}
/**
* Set the dropdown disabled attribute.
* @param bool $disabled the disabled value
*/
public function set_disabled(bool $disabled) {
$this->disabled = $disabled;
}
/**
* 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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): array {
$extras = [];
// Id is required to add JS controls to the dropdown.
$dropdownid = $this->extras['id'] ?? \html_writer::random_id('dropdownDialog_');
if (isset($this->extras['id'])) {
unset($this->extras['id']);
}
foreach ($this->extras as $attribute => $value) {
$extras[] = [
'attribute' => $attribute,
'value' => $value,
];
}
$data = [
// Id is required for the correct HTML labelling.
'dropdownid' => $dropdownid,
'buttonid' => $this->extras['buttonid'] ?? \html_writer::random_id('dropwdownbutton_'),
'buttoncontent' => (string) $this->buttoncontent,
'dialogcontent' => (string) $this->dialogcontent,
'classes' => $this->classes,
'buttonclasses' => $this->buttonclasses,
'dialogclasses' => $this->dropdownwidth,
'extras' => $extras,
];
if ($this->disabled) {
$data['disabledbutton'] = true;
}
// Bootstrap 4 dropdown position still uses left and right literals.
$data["position"] = $this->dropdownposition;
if (right_to_left()) {
$rltposition = [
self::POSITION['start'] => self::POSITION['end'],
self::POSITION['end'] => self::POSITION['end'],
];
$data["position"] = $rltposition[$this->dropdownposition];
}
return $data;
}
/**
* Get the name of the template to use for this templatable.
*
* @param \renderer_base $renderer The renderer requesting the template name
* @return string the template name
*/
public function get_template_name(\renderer_base $renderer): string {
return 'core/local/dropdown/dialog';
}
}
@@ -0,0 +1,108 @@
<?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\output\local\dropdown;
use core\output\choicelist;
/**
* Class to render a dropdown dialog element.
*
* A dropdown dialog allows to render any arbitrary HTML into a dropdown elements triggered
* by a button.
*
* @package core
* @category output
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class status extends dialog {
/**
* @var choicelist content of dialog.
*/
protected $choices = null;
/**
* Constructor.
*
* The definition object could contain the following keys:
* - classes: component CSS classes.
* - buttonclasses: the button CSS classes.
* - dialogwidth: the dropdown width.
* - extras: extra HTML attributes (attribute => value).
* - buttonsync: if the button should be synced with the selected value.
* - updatestatus: if component must update the status and trigger a change event when clicked.
*
* @param string $buttoncontent the button content
* @param choicelist $choices the choice object
* @param array $definition an optional array of the element definition
*/
public function __construct(string $buttoncontent, choicelist $choices, array $definition = []) {
parent::__construct($buttoncontent, '', $definition);
$this->set_choice($choices);
if ($definition['buttonsync'] ?? false) {
$this->extras['data-button-sync'] = 'true';
}
if ($definition['updatestatus'] ?? false) {
$this->extras['data-update-status'] = 'true';
}
}
/**
* Set the dialog contents.
*
* @param choicelist $choices
*/
public function set_choice(choicelist $choices) {
$this->choices = $choices;
$description = $choices->get_description();
if (!empty($description)) {
$this->set_content($description);
}
}
/**
* 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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output): array {
$data = parent::export_for_template($output);
if ($this->choices !== null) {
$data['choices'] = $this->choices->export_for_template($output);
}
$selectedvalue = $this->choices->get_selected_value();
if ($selectedvalue !== null) {
$data['extras'][] = (object)[
'attribute' => 'data-value',
'value' => $selectedvalue,
];
}
return $data;
}
/**
* Get the name of the template to use for this templatable.
*
* @param \renderer_base $renderer The renderer requesting the template name
* @return string the template name
*/
public function get_template_name(\renderer_base $renderer): string {
return 'core/local/dropdown/status';
}
}
@@ -0,0 +1,68 @@
<?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/>.
/**
* Mustache helper to load strings from string_manager and perform HTML escaping on them.
*
* @package core
* @category output
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use Mustache_LambdaHelper;
/**
* This class will load language strings in a template.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*/
class mustache_clean_string_helper {
/** @var mustache_string_helper A string helper instance that is being used internally for fetching strings */
private $stringhelper;
/**
* Create new instance of mustache clean string helper.
*/
public function __construct() {
$this->stringhelper = new \core\output\mustache_string_helper();
}
/**
* Read a lang string from a template and get it from get_string.
*
* Some examples for calling this from a template are:
*
* {{#cleanstr}}activity{{/cleanstr}}
* {{#cleanstr}}actionchoice, core, {{#str}}delete{{/str}}{{/cleanstr}} (Together with the str helper)
* {{#cleanstr}}uploadrenamedchars, core, {"oldname":"Old", "newname":"New"}{{/cleanstr}} (Complex $a)
*
* The args are comma separated and only the first is required.
* The last is a $a argument for get string. For complex data here, use JSON.
*
* @param string $text The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function cleanstr($text, Mustache_LambdaHelper $helper) {
return s($this->stringhelper->str($text, $helper));
}
}
+83
View File
@@ -0,0 +1,83 @@
<?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/>.
/**
* Custom Moodle engine for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Custom Moodle engine for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_engine extends \Mustache_Engine {
/**
* @var mustache_helper_collection
*/
private $helpers;
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $disallowednestedhelpers = [];
/**
* Mustache engine constructor.
*
* This provides an additional option to the parent \Mustache_Engine implementation:
* $options = [
* // A list of helpers (by name) to prevent from executing within the rendering
* // of other helpers.
* 'disallowednestedhelpers' => ['js']
* ];
* @param array $options [description]
*/
public function __construct(array $options = []) {
if (isset($options['blacklistednestedhelpers'])) {
debugging('blacklistednestedhelpers option is deprecated. Use disallowednestedhelpers instead.', DEBUG_DEVELOPER);
$this->disallowednestedhelpers = $options['blacklistednestedhelpers'];
}
if (isset($options['disallowednestedhelpers'])) {
$this->disallowednestedhelpers = $options['disallowednestedhelpers'];
}
parent::__construct($options);
}
/**
* Get the current set of Mustache helpers.
*
* @see Mustache_Engine::setHelpers
*
* @return \Mustache_HelperCollection
*/
public function getHelpers()
{
if (!isset($this->helpers)) {
$this->helpers = new mustache_helper_collection(null, $this->disallowednestedhelpers);
}
return $this->helpers;
}
}
@@ -0,0 +1,83 @@
<?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/>.
/**
* Perform some custom name mapping for template file names (strip leading component/).
*
* @package core
* @category output
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use coding_exception;
/**
* Perform some custom name mapping for template file names.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_filesystem_loader extends \Mustache_Loader_FilesystemLoader {
/**
* Provide a default no-args constructor (we don't really need anything).
*/
public function __construct() {
}
/**
* Helper function for getting a Mustache template file name.
* Uses the leading component to restrict us specific directories.
*
* @param string $name
* @return string Template file name
*/
protected function getFileName($name) {
// Call the Moodle template finder.
return mustache_template_finder::get_template_filepath($name);
}
/**
* Only check if baseDir is a directory and requested templates are files if
* baseDir is using the filesystem stream wrapper.
*
* Always check path for mustache_filesystem_loader.
*
* @return bool Whether to check `is_dir` and `file_exists`
*/
protected function shouldCheckPath() {
return true;
}
/**
* Load a Template by name.
*
* @param string $name the template name
* @return string Mustache Template source
*/
public function load($name) {
global $CFG;
if (!empty($CFG->debugtemplateinfo)) {
// We use many templates per page. We don't want to allocate more memory than necessary.
return "<!-- template(PHP): $name -->" . parent::load($name) . "<!-- /template(PHP): $name -->";
}
return parent::load($name);
}
}
@@ -0,0 +1,183 @@
<?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/>.
/**
* Custom Moodle helper collection for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Custom Moodle helper collection for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_helper_collection extends \Mustache_HelperCollection {
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $disallowednestedhelpers = [];
/**
* Helper Collection constructor.
*
* Optionally accepts an array (or Traversable) of `$name => $helper` pairs.
*
* @throws \Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
*
* @param array|\Traversable $helpers (default: null)
* @param string[] $disallowednestedhelpers Names of helpers that aren't allowed to be called within other helpers.
*/
public function __construct($helpers = null, array $disallowednestedhelpers = []) {
$this->disallowednestedhelpers = $disallowednestedhelpers;
parent::__construct($helpers);
}
/**
* Add a helper to this collection.
*
* This function has overridden the parent implementation to provide disallowing
* functionality for certain helpers to prevent them being called from within
* other helpers. This is because the JavaScript helper can be used in a
* security exploit if it can be nested.
*
* The function will wrap callable helpers in an anonymous function that strips
* out the disallowed helpers from the source string before giving it to the
* helper function. This prevents the disallowed helper functions from being
* called by nested render functions from within other helpers.
*
* @see \Mustache_HelperCollection::add()
* @param string $name
* @param mixed $helper
*/
public function add($name, $helper) {
$disallowedlist = $this->disallowednestedhelpers;
if (is_callable($helper) && !empty($disallowedlist)) {
$helper = function($source, \Mustache_LambdaHelper $lambdahelper) use ($helper, $disallowedlist) {
// Temporarily override the disallowed helpers to return nothing
// so that they can't be executed from within other helpers.
$disabledhelpers = $this->disable_helpers($disallowedlist);
// Call the original function with the modified sources.
$result = call_user_func($helper, $source, $lambdahelper);
// Restore the original disallowed helper implementations now
// that this helper has finished executing so that the rest of
// the rendering process continues to work correctly.
$this->restore_helpers($disabledhelpers);
// Lastly parse the returned string to strip out any unwanted helper
// tags that were added through variable substitution (or other means).
// This is done because a secondary render is called on the result
// of a helper function if it still includes mustache tags. See
// the section function of Mustache_Compiler for details.
return $this->strip_disallowed_helpers($disallowedlist, $result);
};
}
parent::add($name, $helper);
}
/**
* Disable a list of helpers (by name) by changing their implementation to
* simply return an empty string.
*
* @param string[] $names List of helper names to disable
* @return \Closure[] The original helper functions indexed by name
*/
private function disable_helpers($names) {
$disabledhelpers = [];
foreach ($names as $name) {
if ($this->has($name)) {
$function = $this->get($name);
// Null out the helper. Must call parent::add here to avoid
// a recursion problem.
parent::add($name, function() {
return '';
});
$disabledhelpers[$name] = $function;
}
}
return $disabledhelpers;
}
/**
* Restore the original helper implementations. Typically used after disabling
* a helper.
*
* @param \Closure[] $helpers The helper functions indexed by name
*/
private function restore_helpers($helpers) {
foreach ($helpers as $name => $function) {
// Restore the helper functions. Must call parent::add here to avoid
// a recursion problem.
parent::add($name, $function);
}
}
/**
* Parse the given string and remove any reference to disallowed helpers.
*
* E.g.
* $disallowedlist = ['js'];
* $string = "core, move, {{#js}} some nasty JS hack {{/js}}"
* result: "core, move, {{}}"
*
* @param string[] $disallowedlist List of helper names to strip
* @param string $string String to parse
* @return string Parsed string
*/
public function strip_disallowed_helpers($disallowedlist, $string) {
$starttoken = \Mustache_Tokenizer::T_SECTION;
$endtoken = \Mustache_Tokenizer::T_END_SECTION;
if ($endtoken == '/') {
$endtoken = '\/';
}
$regexes = array_map(function($name) use ($starttoken, $endtoken) {
// We only strip out the name of the helper (excluding delimiters)
// the user is able to change the delimeters on a per template
// basis so they may not be curly braces.
return '/\s*' . $starttoken . '\s*'. $name . '\W+.*' . $endtoken . '\s*' . $name . '\s*/';
}, $disallowedlist);
// This will strip out unwanted helpers from the $source string
// before providing it to the original helper function.
// E.g.
// Before:
// "core, move, {{#js}} some nasty JS hack {{/js}}"
// After:
// "core, move, {{}}".
return preg_replace_callback($regexes, function() {
return '';
}, $string);
}
/**
* @deprecated Deprecated since Moodle 3.10 (MDL-69050) - use {@see strip_disallowed_helpers}
*/
public function strip_blacklisted_helpers() {
throw new \coding_exception('\core\output\mustache_helper_collection::strip_blacklisted_helpers() has been removed.');
}
}
@@ -0,0 +1,54 @@
<?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\output;
/**
* Store a list of JS calls to insert at the end of the page.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_javascript_helper {
/** @var \moodle_page $page - Page used to get requirement manager */
private $page = null;
/**
* Create new instance of mustache javascript helper.
*
* @param \moodle_page $page Page.
*/
public function __construct($page) {
$this->page = $page;
}
/**
* Add the block of text to the page requires so it is appended in the footer. The
* content of the block can contain further mustache tags which will be resolved.
*
* This function will always return an empty string because the JS is added to the page via the requirements manager.
*
* @param string $text The script content of the section.
* @param \Mustache_LambdaHelper $helper Used to render the content of this block.
* @return string The text of the block
*/
public function help($text, \Mustache_LambdaHelper $helper) {
$this->page->requires->js_amd_inline($helper->render($text));
return '';
}
}
@@ -0,0 +1,83 @@
<?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/>.
/**
* Mustache helper render pix icons.
*
* @package core
* @category output
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use Mustache_LambdaHelper;
use renderer_base;
/**
* This class will call pix_icon with the section content.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_pix_helper {
/** @var renderer_base $renderer A reference to the renderer in use */
private $renderer;
/**
* Save a reference to the renderer.
* @param renderer_base $renderer
*/
public function __construct(renderer_base $renderer) {
$this->renderer = $renderer;
}
/**
* Read a pix icon name from a template and get it from pix_icon.
*
* {{#pix}}t/edit,component,Anything else is alt text{{/pix}}
*
* The args are comma separated and only the first is required.
*
* @param string $text The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function pix($text, Mustache_LambdaHelper $helper) {
// Split the text into an array of variables.
$key = strtok($text, ",");
$key = trim($helper->render($key));
$component = strtok(",");
$component = trim($helper->render($component));
if (!$component) {
$component = '';
}
$text = strtok("");
// Allow mustache tags in the last argument.
$text = trim($helper->render($text));
// The $text has come from a template, so HTML special
// chars have been escaped. However, render_pix_icon
// assumes the alt arrives with no escaping. So we need
// ot un-escape here.
$text = htmlspecialchars_decode($text, ENT_COMPAT);
return trim($this->renderer->pix_icon($key, $text, $component));
}
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* Wrap content in quotes, and escape all special JSON characters used.
*
* @package core
* @category output
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Wrap content in quotes, and escape all special JSON characters used.
*
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_quote_helper {
/**
* Wrap content in quotes, and escape all special JSON characters used.
*
* Note: This helper is only compatible with the standard {{ }} delimeters.
*
* @param string $text The text to parse for arguments.
* @param \Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function quote($text, \Mustache_LambdaHelper $helper) {
$content = trim($text);
$content = $helper->render($content);
// Escape the {{ and JSON encode.
$content = json_encode($content);
$content = preg_replace('([{}]{2,3})', '{{=<% %>=}}${0}<%={{ }}=%>', $content);
return $content;
}
}
@@ -0,0 +1,65 @@
<?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/>.
/**
* Mustache helper shorten text.
*
* @package core
* @category output
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use Mustache_LambdaHelper;
use renderer_base;
/**
* This class will call shorten_text with the section content.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_shorten_text_helper {
/**
* Read a length and text component from the string.
*
* {{#shortentext}}50,Some test to shorten{{/shortentext}}
*
* Both args are required. The length must come first.
*
* @param string $args The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function shorten($args, Mustache_LambdaHelper $helper) {
// Split the text into an array of variables.
list($length, $text) = explode(',', $args, 2);
$length = trim($length);
$text = trim($text);
// Allow mustache tags in the length and text.
$text = $helper->render($text);
$length = $helper->render($length);
return shorten_text($text, $length);
}
}
@@ -0,0 +1,78 @@
<?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/>.
/**
* Mustache helper to load strings from string_manager.
*
* @package core
* @category output
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use Mustache_LambdaHelper;
use stdClass;
/**
* This class will load language strings in a template.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_string_helper {
/**
* Read a lang string from a template and get it from get_string.
*
* Some examples for calling this from a template are:
*
* {{#str}}activity{{/str}}
* {{#str}}actionchoice, core, {{#str}}delete{{/str}}{{/str}} (Nested)
* {{#str}}addinganewto, core, {"what":"This", "to":"That"}{{/str}} (Complex $a)
*
* The args are comma separated and only the first is required.
* The last is a $a argument for get string. For complex data here, use JSON.
*
* @param string $text The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function str($text, Mustache_LambdaHelper $helper) {
// Split the text into an array of variables.
$key = strtok($text, ",");
$key = trim($key);
$component = strtok(",");
$component = trim($component);
if (!$component) {
$component = '';
}
$a = new stdClass();
$next = strtok('');
$next = trim($next);
if ((strpos($next, '{') === 0) && (strpos($next, '{{') !== 0)) {
$rawjson = $helper->render($next);
$a = json_decode($rawjson);
} else {
$a = $helper->render($next);
}
return get_string($key, $component, $a);
}
}
@@ -0,0 +1,125 @@
<?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/>.
/**
* List the valid locations to search for a template with a given name.
*
* @package core
* @category output
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use coding_exception;
use moodle_exception;
use core_component;
use theme_config;
/**
* Get information about valid locations for mustache templates.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_template_finder {
/**
* Helper function for getting a list of valid template directories for a specific component.
*
* @param string $component The component to search
* @param string $themename The current theme name
* @return string[] List of valid directories for templates for this compoonent. Directories are not checked for existence.
*/
public static function get_template_directories_for_component($component, $themename = '') {
global $CFG, $PAGE;
// Default the param.
if ($themename == '') {
$themename = $PAGE->theme->name;
}
// Clean params for safety.
$component = clean_param($component, PARAM_COMPONENT);
$themename = clean_param($themename, PARAM_COMPONENT);
// Validate the component.
$dirs = array();
$compdirectory = core_component::get_component_directory($component);
if (!$compdirectory) {
throw new coding_exception("Component was not valid: " . s($component));
}
// Find the parent themes.
$parents = array();
if ($themename === $PAGE->theme->name) {
$parents = $PAGE->theme->parents;
} else {
$themeconfig = theme_config::load($themename);
$parents = $themeconfig->parents;
}
// First check the theme.
$dirs[] = $CFG->dirroot . '/theme/' . $themename . '/templates/' . $component . '/';
if (isset($CFG->themedir)) {
$dirs[] = $CFG->themedir . '/' . $themename . '/templates/' . $component . '/';
}
// Now check the parent themes.
// Search each of the parent themes second.
foreach ($parents as $parent) {
$dirs[] = $CFG->dirroot . '/theme/' . $parent . '/templates/' . $component . '/';
if (isset($CFG->themedir)) {
$dirs[] = $CFG->themedir . '/' . $parent . '/templates/' . $component . '/';
}
}
$dirs[] = $compdirectory . '/templates/';
return $dirs;
}
/**
* Helper function for getting a filename for a template from the template name.
*
* @param string $name - This is the componentname/templatename combined.
* @param string $themename - This is the current theme name.
* @return string
*/
public static function get_template_filepath($name, $themename = '') {
global $CFG, $PAGE;
if (strpos($name, '/') === false) {
throw new coding_exception('Templates names must be specified as "componentname/templatename"' .
' (' . s($name) . ' requested) ');
}
list($component, $templatename) = explode('/', $name, 2);
$component = clean_param($component, PARAM_COMPONENT);
$dirs = self::get_template_directories_for_component($component, $themename);
foreach ($dirs as $dir) {
$candidate = $dir . $templatename . '.mustache';
if (file_exists($candidate)) {
return $candidate;
}
}
throw new moodle_exception('filenotfound', 'error', '', null, $name);
}
}
@@ -0,0 +1,387 @@
<?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/>.
/**
* Load template source strings.
*
* @package core
* @category output
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use \Mustache_Tokenizer;
/**
* Load template source strings.
*
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_template_source_loader {
/** @var $gettemplatesource Callback function to load the template source from full name */
private $gettemplatesource = null;
/**
* Constructor that takes a callback to allow the calling code to specify how to retrieve
* the source for a template name.
*
* If no callback is provided then default to the load from disk implementation.
*
* @param callable|null $gettemplatesource Callback to load template source by template name
*/
public function __construct(callable $gettemplatesource = null) {
if ($gettemplatesource) {
// The calling code has specified a function for retrieving the template source
// code by name and theme.
$this->gettemplatesource = $gettemplatesource;
} else {
// By default we will pull the template from disk.
$this->gettemplatesource = function($component, $name, $themename) {
$fulltemplatename = $component . '/' . $name;
$filename = mustache_template_finder::get_template_filepath($fulltemplatename, $themename);
return file_get_contents($filename);
};
}
}
/**
* Remove comments from mustache template.
*
* @param string $templatestr
* @return string
*/
protected function strip_template_comments($templatestr): string {
return preg_replace('/(?={{!)(.*)(}})/sU', '', $templatestr);
}
/**
* Load the template source from the component and template name.
*
* @param string $component The moodle component (e.g. core_message)
* @param string $name The template name (e.g. message_drawer)
* @param string $themename The theme to load the template for (e.g. boost)
* @param bool $includecomments If the comments should be stripped from the source before returning
* @return string The template source
*/
public function load(
string $component,
string $name,
string $themename,
bool $includecomments = false
): string {
global $CFG;
// Get the template source from the callback.
$source = ($this->gettemplatesource)($component, $name, $themename);
// Remove comments from template.
if (!$includecomments) {
$source = $this->strip_template_comments($source);
}
if (!empty($CFG->debugtemplateinfo)) {
return "<!-- template(JS): $name -->" . $source . "<!-- /template(JS): $name -->";
}
return $source;
}
/**
* Load a template and some of the dependencies that will be needed in order to render
* the template.
*
* The current implementation will return all of the templates and all of the strings in
* each of those templates (excluding string substitutions).
*
* The return format is an array indexed with the dependency type (e.g. templates / strings) then
* the component (e.g. core_message), and then the id (e.g. message_drawer).
*
* For example:
* * We have 3 templates in core named foo, bar, and baz.
* * foo includes bar and bar includes baz.
* * foo uses the string 'home' from core
* * baz uses the string 'help' from core
*
* If we load the template foo this function would return:
* [
* 'templates' => [
* 'core' => [
* 'foo' => '... template source ...',
* 'bar' => '... template source ...',
* 'baz' => '... template source ...',
* ]
* ],
* 'strings' => [
* 'core' => [
* 'home' => 'Home',
* 'help' => 'Help'
* ]
* ]
* ]
*
* @param string $templatecomponent The moodle component (e.g. core_message)
* @param string $templatename The template name (e.g. message_drawer)
* @param string $themename The theme to load the template for (e.g. boost)
* @param bool $includecomments If the comments should be stripped from the source before returning
* @param array $seentemplates List of templates already processed / to be skipped.
* @param array $seenstrings List of strings already processed / to be skipped.
* @param string|null $lang moodle translation language, null means use current.
* @return array
*/
public function load_with_dependencies(
string $templatecomponent,
string $templatename,
string $themename,
bool $includecomments = false,
array $seentemplates = [],
array $seenstrings = [],
string $lang = null
): array {
// Initialise the return values.
$templates = [];
$strings = [];
$templatecomponent = trim($templatecomponent);
$templatename = trim($templatename);
// Get the requested template source.
$templatesource = $this->load($templatecomponent, $templatename, $themename, $includecomments);
// This is a helper function to save a value in one of the result arrays (either $templates or $strings).
$save = function(array $results, array $seenlist, string $component, string $id, $value) use ($lang) {
if (!isset($results[$component])) {
// If the results list doesn't already contain this component then initialise it.
$results[$component] = [];
}
// Save the value.
$results[$component][$id] = $value;
// Record that this item has been processed.
array_push($seenlist, "$component/$id");
// Return the updated results and seen list.
return [$results, $seenlist];
};
// This is a helper function for processing a dependency. Does stuff like ignore duplicate processing,
// common result formatting etc.
$handler = function(array $dependency, array $ignorelist, callable $processcallback) use ($lang) {
foreach ($dependency as $component => $ids) {
foreach ($ids as $id) {
$dependencyid = "$component/$id";
if (array_search($dependencyid, $ignorelist) === false) {
$processcallback($component, $id);
// Add this to our ignore list now that we've processed it so that we don't
// process it again.
array_push($ignorelist, $dependencyid);
}
}
}
return $ignorelist;
};
// Save this template as the first result in the $templates result array.
list($templates, $seentemplates) = $save($templates, $seentemplates, $templatecomponent, $templatename, $templatesource);
// Check the template for any dependencies that need to be loaded.
$dependencies = $this->scan_template_source_for_dependencies($templatesource);
// Load all of the lang strings that this template requires and add them to the
// returned values.
$seenstrings = $handler(
$dependencies['strings'],
$seenstrings,
// Include $strings and $seenstrings by reference so that their values can be updated
// outside of this anonymous function.
function($component, $id) use ($save, &$strings, &$seenstrings, $lang) {
$string = get_string_manager()->get_string($id, $component, null, $lang);
// Save the string in the $strings results array.
list($strings, $seenstrings) = $save($strings, $seenstrings, $component, $id, $string);
}
);
// Load any child templates that we've found in this template and add them to
// the return list of dependencies.
$seentemplates = $handler(
$dependencies['templates'],
$seentemplates,
// Include $strings, $seenstrings, $templates, and $seentemplates by reference so that their values can be updated
// outside of this anonymous function.
function($component, $id) use (
$themename,
$includecomments,
&$seentemplates,
&$seenstrings,
&$templates,
&$strings,
$save,
$lang
) {
// We haven't seen this template yet so load it and it's dependencies.
$subdependencies = $this->load_with_dependencies(
$component,
$id,
$themename,
$includecomments,
$seentemplates,
$seenstrings,
$lang
);
foreach ($subdependencies['templates'] as $component => $ids) {
foreach ($ids as $id => $value) {
// Include the child themes in our results.
list($templates, $seentemplates) = $save($templates, $seentemplates, $component, $id, $value);
}
};
foreach ($subdependencies['strings'] as $component => $ids) {
foreach ($ids as $id => $value) {
// Include any strings that the child templates need in our results.
list($strings, $seenstrings) = $save($strings, $seenstrings, $component, $id, $value);
}
}
}
);
return [
'templates' => $templates,
'strings' => $strings
];
}
/**
* Scan over a template source string and return a list of dependencies it requires.
* At the moment the list will only include other templates and strings.
*
* The return format is an array indexed with the dependency type (e.g. templates / strings) then
* the component (e.g. core_message) with it's value being an array of the items required
* in that component.
*
* For example:
* If we have a template foo that includes 2 templates, bar and baz, and also 2 strings
* 'home' and 'help' from the core component then the return value would look like:
*
* [
* 'templates' => [
* 'core' => ['foo', 'bar', 'baz']
* ],
* 'strings' => [
* 'core' => ['home', 'help']
* ]
* ]
*
* @param string $source The template source
* @return array
*/
protected function scan_template_source_for_dependencies(string $source): array {
$tokenizer = new Mustache_Tokenizer();
$tokens = $tokenizer->scan($source);
$templates = [];
$strings = [];
$addtodependencies = function($dependencies, $component, $id) {
$id = trim($id);
$component = trim($component);
if (!isset($dependencies[$component])) {
// Initialise the component if we haven't seen it before.
$dependencies[$component] = [];
}
// Add this id to the list of dependencies.
array_push($dependencies[$component], $id);
return $dependencies;
};
foreach ($tokens as $index => $token) {
$type = $token['type'];
$name = isset($token['name']) ? $token['name'] : null;
if ($name) {
switch ($type) {
case Mustache_Tokenizer::T_PARTIAL:
list($component, $id) = explode('/', $name, 2);
$templates = $addtodependencies($templates, $component, $id);
break;
case Mustache_Tokenizer::T_PARENT:
list($component, $id) = explode('/', $name, 2);
$templates = $addtodependencies($templates, $component, $id);
break;
case Mustache_Tokenizer::T_SECTION:
if ($name == 'str') {
list($id, $component) = $this->get_string_identifiers($tokens, $index);
if ($id) {
$strings = $addtodependencies($strings, $component, $id);
}
}
break;
}
}
}
return [
'templates' => $templates,
'strings' => $strings
];
}
/**
* Gets the identifier and component of the string.
*
* The string could be defined on one, or multiple lines.
*
* @param array $tokens The templates token.
* @param int $start The index of the start of the string token.
* @return array A list of the string identifier and component.
*/
protected function get_string_identifiers(array $tokens, int $start): array {
$current = $start + 1;
$parts = [];
// Get the contents of the string tag.
while ($tokens[$current]['type'] !== Mustache_Tokenizer::T_END_SECTION) {
if (!isset($tokens[$current]['value']) || empty(trim($tokens[$current]['value']))) {
// An empty line, so we should ignore it.
$current++;
continue;
}
// We need to remove any spaces before and after the string.
$nospaces = trim($tokens[$current]['value']);
// We need to remove any trailing commas so that the explode will not add an
// empty entry where two paramters are on multiple lines.
$clean = rtrim($nospaces, ',');
// We separate the parts of a string with commas.
$subparts = explode(',', $clean);
// Store the parts.
$parts = array_merge($parts, $subparts);
$current++;
}
// The first text should be the first part of a str tag.
$id = isset($parts[0]) ? trim($parts[0]) : null;
// Default to 'core' for the component, if not specified.
$component = isset($parts[1]) ? trim($parts[1]) : 'core';
return [$id, $component];
}
}
@@ -0,0 +1,52 @@
<?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/>.
/**
* Mustache helper that will add JS to the end of the page.
*
* @package core
* @category output
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Lazy create a uniqid per instance of the class. The id is only generated
* when this class it converted to a string.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
class mustache_uniqid_helper {
/** @var string $uniqid The unique id */
private $uniqid = null;
/**
* Init the random variable and return it as a string.
*
* @return string random id.
*/
public function __toString() {
if ($this->uniqid === null) {
$this->uniqid = \html_writer::random_id(uniqid());
}
return $this->uniqid;
}
}
@@ -0,0 +1,64 @@
<?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/>.
/**
* Mustache helper that will convert a timestamp to a date string.
*
* @package core
* @category output
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
use Mustache_LambdaHelper;
/**
* Mustache helper that will convert a timestamp to a date string.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_user_date_helper {
/**
* Read a timestamp and format from the string.
*
* {{#userdate}}1487655635, %Y %m %d{{/userdate}}
*
* There is a list of formats in lang/en/langconfig.php that can be used as the date format.
*
* Both args are required. The timestamp must come first.
*
* @param string $args The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function transform($args, Mustache_LambdaHelper $helper) {
// Split the text into an array of variables.
list($timestamp, $format) = explode(',', $args, 2);
$timestamp = trim($timestamp);
$format = trim($format);
$timestamp = $helper->render($timestamp);
$format = $helper->render($format);
return userdate($timestamp, $format);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?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\output;
use templatable;
/**
* A subset of templatable which provides the name of the template to use.
*
* @package core
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface named_templatable extends 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;
}
+189
View File
@@ -0,0 +1,189 @@
<?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/>.
/**
* Notification renderable component.
*
* @package core
* @copyright 2015 Jetha Chan
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Data structure representing a notification.
*
* @copyright 2015 Jetha Chan
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.9
* @package core
* @category output
*/
class notification implements \renderable, \templatable {
/**
* A notification of level 'success'.
*/
const NOTIFY_SUCCESS = 'success';
/**
* A notification of level 'warning'.
*/
const NOTIFY_WARNING = 'warning';
/**
* A notification of level 'info'.
*/
const NOTIFY_INFO = 'info';
/**
* A notification of level 'error'.
*/
const NOTIFY_ERROR = 'error';
/**
* @var string Message payload.
*/
protected $message = '';
/**
* @var string Message type.
*/
protected $messagetype = self::NOTIFY_WARNING;
/**
* @var bool $announce Whether this notification should be announced assertively to screen readers.
*/
protected $announce = true;
/**
* @var bool $closebutton Whether this notification should inlcude a button to dismiss itself.
*/
protected $closebutton = true;
/**
* @var array $extraclasses A list of any extra classes that may be required.
*/
protected $extraclasses = array();
/**
* Notification constructor.
*
* @param string $message the message to print out
* @param ?string $messagetype one of the NOTIFY_* constants..
* @param bool $closebutton Whether to show a close icon to remove the notification (default true).
*/
public function __construct($message, $messagetype = null, $closebutton = true) {
$this->message = $message;
if (empty($messagetype)) {
$messagetype = self::NOTIFY_ERROR;
}
$this->messagetype = $messagetype;
$this->closebutton = $closebutton;
}
/**
* Set whether this notification should be announced assertively to screen readers.
*
* @param bool $announce
* @return $this
*/
public function set_announce($announce = false) {
$this->announce = (bool) $announce;
return $this;
}
/**
* Set whether this notification should include a button to disiss itself.
*
* @param bool $button
* @return $this
*/
public function set_show_closebutton($button = false) {
$this->closebutton = (bool) $button;
return $this;
}
/**
* Add any extra classes that this notification requires.
*
* @param array $classes
* @return $this
*/
public function set_extra_classes($classes = array()) {
$this->extraclasses = $classes;
return $this;
}
/**
* Get the message for this notification.
*
* @return string message
*/
public function get_message() {
return $this->message;
}
/**
* Get the message type for this notification.
*
* @return string message type
*/
public function get_message_type() {
return $this->messagetype;
}
/**
* 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) {
return array(
'message' => clean_text($this->message),
'extraclasses' => implode(' ', $this->extraclasses),
'announce' => $this->announce,
'closebutton' => $this->closebutton,
'issuccess' => $this->messagetype === 'success',
'isinfo' => $this->messagetype === 'info',
'iswarning' => $this->messagetype === 'warning',
'iserror' => $this->messagetype === 'error',
);
}
public function get_template_name() {
$templatemappings = [
// Current types mapped to template names.
'success' => 'core/notification_success',
'info' => 'core/notification_info',
'warning' => 'core/notification_warning',
'error' => 'core/notification_error',
];
if (isset($templatemappings[$this->messagetype])) {
return $templatemappings[$this->messagetype];
}
return $templatemappings['error'];
}
}
@@ -0,0 +1,228 @@
<?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\output;
use context_course;
use moodle_page;
use navigation_node;
use moodle_url;
/**
* Class responsible for generating the action bar (tertiary nav) elements in the participants page and related pages.
*
* @package core
* @copyright 2021 Peter Dias
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants_action_bar implements \renderable {
/** @var object $course The course we are dealing with. */
private $course;
/** @var moodle_page $page The current page. */
private $page;
/** @var navigation_node $node The settings node for the participants page. */
private $node;
/** @var string|null $renderedcontent Rendered buttons to be displayed in-line with the select box */
private $renderedcontent;
/**
* Constructor participants_action_bar
* @param object $course The course that we are generating the nav for
* @param moodle_page $page The page object
* @param string|null $renderedcontent Any additional rendered content/actions to be displayed in line with the nav
*/
public function __construct(object $course, moodle_page $page, ?string $renderedcontent) {
$this->course = $course;
$this->page = $page;
$node = 'users';
if ($this->page->context->contextlevel == CONTEXT_MODULE) {
$node = 'modulesettings';
} else if ($this->page->context->contextlevel == CONTEXT_COURSECAT) {
$node = 'categorysettings';
}
$this->node = $this->page->settingsnav->find($node, null);
$this->renderedcontent = $renderedcontent;
}
/**
* Return the nodes required to be displayed in the url_select box.
* The nodes are divided into 3 sections each with the heading as the key.
*
* @return array
*/
protected function get_ordered_nodes(): array {
return [
'enrolments:enrol' => [
'review',
'manageinstances',
'renameroles',
],
'groups:group' => [
'groups'
],
'permissions:role' => [
'override',
'roles',
'otherusers',
'permissions',
'roleoverride',
'rolecheck',
'roleassign',
],
];
}
/**
* Get the content for the url_select select box.
*
* @return array
*/
protected function get_content_for_select(): array {
if (!$this->node) {
return [];
}
$formattedcontent = [];
$enrolmentsheading = get_string('enrolments', 'enrol');
if ($this->page->context->contextlevel != CONTEXT_MODULE &&
$this->page->context->contextlevel != CONTEXT_COURSECAT) {
// Pre-populate the formatted tertiary nav items with the "Enrolled users" node if user can view the participants page.
$coursecontext = context_course::instance($this->course->id);
$canviewparticipants = course_can_view_participants($coursecontext);
if ($canviewparticipants) {
$participantsurl = (new moodle_url('/user/index.php', ['id' => $this->course->id]))->out();
$formattedcontent[] = [
$enrolmentsheading => [
$participantsurl => get_string('enrolledusers', 'enrol'),
]
];
}
}
$nodes = $this->get_ordered_nodes();
foreach ($nodes as $description => $content) {
list($stringid, $location) = explode(':', $description);
$heading = get_string($stringid, $location);
$items = [];
foreach ($content as $key) {
if ($node = $this->node->find($key, null)) {
if ($node->has_action()) {
$items[$node->action()->out()] = $node->text;
}
// Additional items to be added.
if ($key === 'groups') {
$params = ['id' => $this->course->id];
$items += [
(new moodle_url('/group/groupings.php', $params))->out() => get_string('groupings', 'group'),
(new moodle_url('/group/overview.php', $params))->out() => get_string('overview', 'group')
];
}
}
}
if ($items) {
if ($heading === $enrolmentsheading) {
// Merge the contents of the "Enrolments" group with the ones from the course settings nav.
$formattedcontent[0][$heading] = array_merge($formattedcontent[0][$heading], $items);
} else {
$formattedcontent[][$heading] = $items;
}
}
}
// If we are accessing a page from a module/category context additional nodes will not be visible.
if ($this->page->context->contextlevel != CONTEXT_MODULE &&
$this->page->context->contextlevel != CONTEXT_COURSECAT) {
// Need to do some funky code here to find out if we have added third party navigation nodes.
$thirdpartynodearray = $this->get_thirdparty_node_array() ?: [];
$formattedcontent = array_merge($formattedcontent, $thirdpartynodearray);
}
return $formattedcontent;
}
/**
* Gets an array of third party navigation nodes in an array formatted for a url_select element.
*
* @return array|null The thirdparty node array.
*/
protected function get_thirdparty_node_array(): ?array {
$results = [];
$flatnodes = array_merge(...(array_values($this->get_ordered_nodes())));
foreach ($this->node->children as $child) {
if (array_search($child->key, $flatnodes) === false) {
$results[] = $child;
}
}
return \core\navigation\views\secondary::create_menu_element($results, true);
}
/**
* Recursively tries to find a matching url
* @param array $urlcontent The content for the url_select
* @param int $strictness Strictness for the compare criteria
* @return string The matching active url
*/
protected function find_active_page(array $urlcontent, int $strictness = URL_MATCH_EXACT): string {
foreach ($urlcontent as $key => $value) {
if (is_array($value) && $activeitem = $this->find_active_page($value, $strictness)) {
return $activeitem;
} else if ($this->page->url->compare(new moodle_url($key), $strictness)) {
return $key;
}
}
return "";
}
/**
* Gets the url_select to be displayed in the participants page if available.
*
* @param \renderer_base $output
* @return object|null The content required to render the tertiary navigation
*/
public function get_dropdown(\renderer_base $output): ?object {
if ($urlselectcontent = $this->get_content_for_select()) {
$activeurl = $this->find_active_page($urlselectcontent);
$activeurl = $activeurl ?: $this->find_active_page($urlselectcontent, URL_MATCH_BASE);
$selectmenu = new select_menu('participantsnavigation', $urlselectcontent, $activeurl);
$selectmenu->set_label(get_string('participantsnavigation', 'course'), ['class' => 'sr-only']);
return $selectmenu->export_for_template($output);
}
return null;
}
/**
* Export the content to be displayed on the participants page.
*
* @param \renderer_base $output
* @return array Consists of the following:
* - navigation A stdclass representing the standard navigation options to be fed into a urlselect
* - renderedcontent Rendered content to be displayed in line with the tertiary nav
*/
public function export_for_template(\renderer_base $output) {
return [
'navigation' => $this->get_dropdown($output),
'renderedcontent' => $this->renderedcontent,
];
}
}
+172
View File
@@ -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/>.
declare(strict_types=1);
namespace core\output;
use renderer_base;
/**
* A single-select combobox widget that is functionally similar to an HTML select element.
*
* @package core
* @category output
* @copyright 2022 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class select_menu implements \renderable, \templatable {
/** @var array List of options. */
protected $options;
/** @var string|null The value of the preselected option. */
protected $selected;
/** @var string The combobox label */
protected $label;
/** @var array Button label's attributes */
protected $labelattributes = [];
/** @var string Name of the combobox element */
protected $name;
/**
* select_menu constructor.
*
* @param string $name Name of the combobox element
* @param array $options List of options in an associative array format like ['val' => 'Option'].
* Supports grouped options as well.
* @param string|null $selected The value of the preselected option.
*/
public function __construct(string $name, array $options, string $selected = null) {
$this->name = $name;
$this->options = $options;
$this->selected = $selected;
}
/**
* Sets the select menu's label.
*
* @param string $label The label.
* @param array $attributes List of attributes to apply on the label element.
*/
public function set_label(string $label, array $attributes = []) {
$this->label = $label;
$this->labelattributes = $attributes;
}
/**
* Flatten the options for Mustache.
*
* @return array
*/
protected function flatten_options(): array {
$flattened = [];
foreach ($this->options as $value => $option) {
if (is_array($option)) {
foreach ($option as $groupname => $optoptions) {
if (!isset($flattened[$groupname])) {
$flattened[$groupname] = [
'name' => $groupname,
'isgroup' => true,
'id' => \html_writer::random_id('select-menu-group'),
'options' => []
];
}
foreach ($optoptions as $optvalue => $optoption) {
$flattened[$groupname]['options'][$optvalue] = [
'name' => $optoption,
'value' => $optvalue,
'selected' => $this->selected == $optvalue,
'id' => \html_writer::random_id('select-menu-option'),
];
}
}
} else {
$flattened[$value] = [
'name' => $option,
'value' => $value,
'selected' => $this->selected == $value,
'id' => \html_writer::random_id('select-menu-option'),
];
}
}
// Make non-associative array.
foreach ($flattened as $key => $value) {
if (!empty($value['options'])) {
$flattened[$key]['options'] = array_values($value['options']);
}
}
$flattened = array_values($flattened);
return $flattened;
}
/**
* Return the name of the selected option.
*
* @return string|null The name of the selected option or null.
*/
private function get_selected_option(): ?string {
foreach ($this->options as $value => $option) {
if (is_array($option)) { // This is a group.
foreach ($option as $groupname => $optoptions) {
// Loop through the options within the group to check whether any of them matches the 'selected' value.
foreach ($optoptions as $optvalue => $optoption) {
// If the value of the option matches the 'selected' value, return the name of the option.
if ($this->selected == $optvalue) {
return $optoption;
}
}
}
} else { // This is a standard option item.
// If the value of the option matches the 'selected' value, return the name of the option.
if ($this->selected == $value) {
return $option;
}
}
}
return null;
}
/**
* Export for template.
*
* @param renderer_base $output The renderer.
* @return \stdClass
*/
public function export_for_template(renderer_base $output): \stdClass {
$data = new \stdClass();
$data->baseid = \html_writer::random_id('select-menu');
$data->label = $this->label;
$data->options = $this->flatten_options();
$data->selectedoption = $this->get_selected_option();
$data->name = $this->name;
$data->value = $this->selected;
// Label attributes.
$data->labelattributes = [];
// Map the label attributes.
foreach ($this->labelattributes as $key => $value) {
$data->labelattributes[] = ['name' => $key, 'value' => $value];
}
return $data;
}
}
+148
View File
@@ -0,0 +1,148 @@
<?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\output;
use renderable;
/**
* Class to render a sticky footer element.
*
* Sticky footer can be rendered at any moment if the page (even inside a form) but
* it will be displayed at the bottom of the page.
*
* Important: note that pages can only display one sticky footer at once.
*
* Important: not all themes are compatible with sticky footer. If the current theme
* is not compatible it will be rendered as a standard div element.
*
* @package core
* @category output
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sticky_footer implements named_templatable, renderable {
/**
* @var string content of the sticky footer.
*/
protected $stickycontent = '';
/**
* @var string extra CSS classes. By default, elements are justified to the end.
*/
protected $stickyclasses = 'justify-content-end';
/**
* @var bool if the footer should auto enable or not.
*/
protected $autoenable = true;
/**
* @var array extra HTML attributes (attribute => value).
*/
protected $attributes = [];
/**
* Constructor.
*
* @param string $stickycontent the footer content
* @param string|null $stickyclasses extra CSS classes
* @param array $attributes extra html attributes (attribute => value)
*/
public function __construct(string $stickycontent = '', ?string $stickyclasses = null, array $attributes = []) {
$this->stickycontent = $stickycontent;
if ($stickyclasses !== null) {
$this->stickyclasses = $stickyclasses;
}
$this->attributes = $attributes;
}
/**
* Set the footer contents.
*
* @param string $stickycontent the footer content
*/
public function set_content(string $stickycontent) {
$this->stickycontent = $stickycontent;
}
/**
* Set the auto enable value.
*
* @param bool $autoenable the footer content
*/
public function set_auto_enable(bool $autoenable) {
$this->autoenable = $autoenable;
}
/**
* Add extra classes to the sticky footer.
*
* @param string $stickyclasses the extra classes
*/
public function add_classes(string $stickyclasses) {
if (!empty($this->stickyclasses)) {
$this->stickyclasses .= ' ';
}
$this->stickyclasses = $stickyclasses;
}
/**
* Add extra attributes to the sticky footer element.
*
* @param string $atribute the attribute
* @param string $value the value
*/
public function add_attribute(string $atribute, string $value) {
$this->attributes[$atribute] = $value;
}
/**
* 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 array data context for a mustache template
*/
public function export_for_template(\renderer_base $output) {
$extras = [];
foreach ($this->attributes as $attribute => $value) {
$extras[] = [
'attribute' => $attribute,
'value' => $value,
];
}
$data = [
'stickycontent' => (string)$this->stickycontent,
'stickyclasses' => $this->stickyclasses,
'extras' => $extras,
];
if (!$this->autoenable) {
$data['disable'] = true;
}
return $data;
}
/**
* Get the name of the template to use for this templatable.
*
* @param \renderer_base $renderer The renderer requesting the template name
* @return string the template name
*/
public function get_template_name(\renderer_base $renderer): string {
return 'core/sticky_footer';
}
}
+127
View File
@@ -0,0 +1,127 @@
<?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\output;
/**
* This class houses methods for checking theme usage in a given context.
*
* @package core
* @category output
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class theme_usage {
/** @var string The theme usage type for users. */
public const THEME_USAGE_TYPE_USER = 'user';
/** @var string The theme usage type for courses. */
public const THEME_USAGE_TYPE_COURSE = 'course';
/** @var string The theme usage type for cohorts. */
public const THEME_USAGE_TYPE_COHORT = 'cohort';
/** @var string The theme usage type for categories. */
public const THEME_USAGE_TYPE_CATEGORY = 'category';
/** @var string The theme usage type for all. */
public const THEME_USAGE_TYPE_ALL = 'all';
/** @var int The theme is used in context. */
public const THEME_IS_USED = 1;
/** @var int The theme is not used in context. */
public const THEME_IS_NOT_USED = 0;
/**
* Check if the theme is used in any context (e.g. user, course, cohort, category).
*
* This query is cached.
*
* @param string $themename The theme to check.
* @return int Return 1 if at least one record was found, 0 if none.
*/
public static function is_theme_used_in_any_context(string $themename): int {
global $DB;
$cache = \cache::make('core', 'theme_usedincontext');
$isused = $cache->get($themename);
if ($isused === false) {
$sqlunions = [];
// For each context, check if the config is enabled and there is at least one use.
if (get_config('core', 'allowuserthemes')) {
$sqlunions[self::THEME_USAGE_TYPE_USER] = "
SELECT u.id
FROM {user} u
WHERE u.theme = :usertheme
";
}
if (get_config('core', 'allowcoursethemes')) {
$sqlunions[self::THEME_USAGE_TYPE_COURSE] = "
SELECT c.id
FROM {course} c
WHERE c.theme = :coursetheme
";
}
if (get_config('core', 'allowcohortthemes')) {
$sqlunions[self::THEME_USAGE_TYPE_COHORT] = "
SELECT co.id
FROM {cohort} co
WHERE co.theme = :cohorttheme
";
}
if (get_config('core', 'allowcategorythemes')) {
$sqlunions[self::THEME_USAGE_TYPE_CATEGORY] = "
SELECT cat.id
FROM {course_categories} cat
WHERE cat.theme = :categorytheme
";
}
// Union the sql statements from the different tables.
if (!empty($sqlunions)) {
$sql = implode(' UNION ', $sqlunions);
// Prepare params.
$params = [];
foreach ($sqlunions as $type => $val) {
$params[$type . 'theme'] = $themename;
}
$result = $DB->record_exists_sql($sql, $params);
}
if (!empty($result)) {
$isused = self::THEME_IS_USED;
} else {
$isused = self::THEME_IS_NOT_USED;
}
// Cache the result so we don't have to keep checking for this theme.
$cache->set($themename, $isused);
return $isused;
} else {
return $isused;
}
}
}
+58
View File
@@ -0,0 +1,58 @@
<?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/>.
/**
* URL rewriter base.
*
* @package core
* @author Brendan Heywood <brendan@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
defined('MOODLE_INTERNAL') || die();
/**
* URL rewriter interface
*
* @package core
* @author Brendan Heywood <brendan@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface url_rewriter {
/**
* Rewrite moodle_urls into another form.
*
* @param \moodle_url $url a url to potentially rewrite
* @return \moodle_url Returns a new, or the original, moodle_url;
*/
public static function url_rewrite(\moodle_url $url);
/**
* Gives a url rewriting plugin a chance to rewrite the current page url
* avoiding redirects and improving performance.
*
* @return void
*/
public static function html_head_setup();
}