mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
cff22cd5ab
It's not available during, and breaks, site installation. The output class doesn't necessarily need it in 78db6bbce8, so switch to simple templatable interface instead.
5367 lines
173 KiB
PHP
5367 lines
173 KiB
PHP
<?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/>.
|
|
|
|
/**
|
|
* Classes representing HTML elements, used by $OUTPUT methods
|
|
*
|
|
* Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
|
|
* for an overview.
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2009 Tim Hunt
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
use core\output\local\action_menu\subpanel;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Interface marking other classes as suitable for renderer_base::render()
|
|
*
|
|
* @copyright 2010 Petr Skoda (skodak) info@skodak.org
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
interface renderable {
|
|
// intentionally empty
|
|
}
|
|
|
|
/**
|
|
* Interface marking other classes having the ability to export their data for use by templates.
|
|
*
|
|
* @copyright 2015 Damyon Wiese
|
|
* @package core
|
|
* @category output
|
|
* @since 2.9
|
|
*/
|
|
interface templatable {
|
|
|
|
/**
|
|
* Function to export the renderer data in a format that is suitable for a
|
|
* mustache template. This means:
|
|
* 1. No complex types - only stdClass, array, int, string, float, bool
|
|
* 2. Any additional info that is required for the template is pre-calculated (e.g. capability checks).
|
|
*
|
|
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
|
* @return stdClass|array
|
|
*/
|
|
public function export_for_template(renderer_base $output);
|
|
}
|
|
|
|
/**
|
|
* Data structure representing a file picker.
|
|
*
|
|
* @copyright 2010 Dongsheng Cai
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class file_picker implements renderable {
|
|
|
|
/**
|
|
* @var stdClass An object containing options for the file picker
|
|
*/
|
|
public $options;
|
|
|
|
/**
|
|
* Constructs a file picker object.
|
|
*
|
|
* The following are possible options for the filepicker:
|
|
* - accepted_types (*)
|
|
* - return_types (FILE_INTERNAL)
|
|
* - env (filepicker)
|
|
* - client_id (uniqid)
|
|
* - itemid (0)
|
|
* - maxbytes (-1)
|
|
* - maxfiles (1)
|
|
* - buttonname (false)
|
|
*
|
|
* @param stdClass $options An object containing options for the file picker.
|
|
*/
|
|
public function __construct(stdClass $options) {
|
|
global $CFG, $USER, $PAGE;
|
|
require_once($CFG->dirroot. '/repository/lib.php');
|
|
$defaults = array(
|
|
'accepted_types'=>'*',
|
|
'return_types'=>FILE_INTERNAL,
|
|
'env' => 'filepicker',
|
|
'client_id' => uniqid(),
|
|
'itemid' => 0,
|
|
'maxbytes'=>-1,
|
|
'maxfiles'=>1,
|
|
'buttonname'=>false
|
|
);
|
|
foreach ($defaults as $key=>$value) {
|
|
if (empty($options->$key)) {
|
|
$options->$key = $value;
|
|
}
|
|
}
|
|
|
|
$options->currentfile = '';
|
|
if (!empty($options->itemid)) {
|
|
$fs = get_file_storage();
|
|
$usercontext = context_user::instance($USER->id);
|
|
if (empty($options->filename)) {
|
|
if ($files = $fs->get_area_files($usercontext->id, 'user', 'draft', $options->itemid, 'id DESC', false)) {
|
|
$file = reset($files);
|
|
}
|
|
} else {
|
|
$file = $fs->get_file($usercontext->id, 'user', 'draft', $options->itemid, $options->filepath, $options->filename);
|
|
}
|
|
if (!empty($file)) {
|
|
$options->currentfile = html_writer::link(moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()), $file->get_filename());
|
|
}
|
|
}
|
|
|
|
// initilise options, getting files in root path
|
|
$this->options = initialise_filepicker($options);
|
|
|
|
// copying other options
|
|
foreach ($options as $name=>$value) {
|
|
if (!isset($this->options->$name)) {
|
|
$this->options->$name = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure representing a user picture.
|
|
*
|
|
* @copyright 2009 Nicolas Connault, 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Modle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class user_picture implements renderable {
|
|
/**
|
|
* @var stdClass A user object with at least fields all columns specified
|
|
* in $fields array constant set.
|
|
*/
|
|
public $user;
|
|
|
|
/**
|
|
* @var int The course id. Used when constructing the link to the user's
|
|
* profile, page course id used if not specified.
|
|
*/
|
|
public $courseid;
|
|
|
|
/**
|
|
* @var bool Add course profile link to image
|
|
*/
|
|
public $link = true;
|
|
|
|
/**
|
|
* @var int Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility.
|
|
* Recommended values (supporting user initials too): 16, 35, 64 and 100.
|
|
*/
|
|
public $size = 35;
|
|
|
|
/**
|
|
* @var bool Add non-blank alt-text to the image.
|
|
* Default true, set to false when image alt just duplicates text in screenreaders.
|
|
*/
|
|
public $alttext = true;
|
|
|
|
/**
|
|
* @var bool Whether or not to open the link in a popup window.
|
|
*/
|
|
public $popup = false;
|
|
|
|
/**
|
|
* @var string Image class attribute
|
|
*/
|
|
public $class = 'userpicture';
|
|
|
|
/**
|
|
* @var bool Whether to be visible to screen readers.
|
|
*/
|
|
public $visibletoscreenreaders = true;
|
|
|
|
/**
|
|
* @var bool Whether to include the fullname in the user picture link.
|
|
*/
|
|
public $includefullname = false;
|
|
|
|
/**
|
|
* @var mixed Include user authentication token. True indicates to generate a token for current user, and integer value
|
|
* indicates to generate a token for the user whose id is the value indicated.
|
|
*/
|
|
public $includetoken = false;
|
|
|
|
/**
|
|
* User picture constructor.
|
|
*
|
|
* @param stdClass $user user record with at least id, picture, imagealt, firstname and lastname set.
|
|
* It is recommended to add also contextid of the user for performance reasons.
|
|
*/
|
|
public function __construct(stdClass $user) {
|
|
global $DB;
|
|
|
|
if (empty($user->id)) {
|
|
throw new coding_exception('User id is required when printing user avatar image.');
|
|
}
|
|
|
|
// only touch the DB if we are missing data and complain loudly...
|
|
$needrec = false;
|
|
foreach (\core_user\fields::get_picture_fields() as $field) {
|
|
if (!property_exists($user, $field)) {
|
|
$needrec = true;
|
|
debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
|
|
.'Please use the \core_user\fields API to get the full list of required fields.', DEBUG_DEVELOPER);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($needrec) {
|
|
$this->user = $DB->get_record('user', array('id' => $user->id),
|
|
implode(',', \core_user\fields::get_picture_fields()), MUST_EXIST);
|
|
} else {
|
|
$this->user = clone($user);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a list of required user fields, useful when fetching required user info from db.
|
|
*
|
|
* In some cases we have to fetch the user data together with some other information,
|
|
* the idalias is useful there because the id would otherwise override the main
|
|
* id of the result record. Please note it has to be converted back to id before rendering.
|
|
*
|
|
* @param string $tableprefix name of database table prefix in query
|
|
* @param array $extrafields extra fields to be included in result (do not include TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE)
|
|
* @param string $idalias alias of id field
|
|
* @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id'
|
|
* @return string
|
|
* @deprecated since Moodle 3.11 MDL-45242
|
|
* @see \core_user\fields
|
|
*/
|
|
public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') {
|
|
debugging('user_picture::fields() is deprecated. Please use the \core_user\fields API instead.', DEBUG_DEVELOPER);
|
|
$userfields = \core_user\fields::for_userpic();
|
|
if ($extrafields) {
|
|
$userfields->including(...$extrafields);
|
|
}
|
|
$selects = $userfields->get_sql($tableprefix, false, $fieldprefix, $idalias, false)->selects;
|
|
if ($tableprefix === '') {
|
|
// If no table alias is specified, don't add {user}. in front of fields.
|
|
$selects = str_replace('{user}.', '', $selects);
|
|
}
|
|
// Maintain legacy behaviour where the field list was done with 'implode' and no spaces.
|
|
$selects = str_replace(', ', ',', $selects);
|
|
return $selects;
|
|
}
|
|
|
|
/**
|
|
* Extract the aliased user fields from a given record
|
|
*
|
|
* Given a record that was previously obtained using {@link self::fields()} with aliases,
|
|
* this method extracts user related unaliased fields.
|
|
*
|
|
* @param stdClass $record containing user picture fields
|
|
* @param array $extrafields extra fields included in the $record
|
|
* @param string $idalias alias of the id field
|
|
* @param string $fieldprefix prefix added to all columns in their aliases, does not apply to 'id'
|
|
* @return stdClass object with unaliased user fields
|
|
*/
|
|
public static function unalias(stdClass $record, array $extrafields = null, $idalias = 'id', $fieldprefix = '') {
|
|
|
|
if (empty($idalias)) {
|
|
$idalias = 'id';
|
|
}
|
|
|
|
$return = new stdClass();
|
|
|
|
foreach (\core_user\fields::get_picture_fields() as $field) {
|
|
if ($field === 'id') {
|
|
if (property_exists($record, $idalias)) {
|
|
$return->id = $record->{$idalias};
|
|
}
|
|
} else {
|
|
if (property_exists($record, $fieldprefix.$field)) {
|
|
$return->{$field} = $record->{$fieldprefix.$field};
|
|
}
|
|
}
|
|
}
|
|
// add extra fields if not already there
|
|
if ($extrafields) {
|
|
foreach ($extrafields as $e) {
|
|
if ($e === 'id' or property_exists($return, $e)) {
|
|
continue;
|
|
}
|
|
$return->{$e} = $record->{$fieldprefix.$e};
|
|
}
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Works out the URL for the users picture.
|
|
*
|
|
* This method is recommended as it avoids costly redirects of user pictures
|
|
* if requests are made for non-existent files etc.
|
|
*
|
|
* @param moodle_page $page
|
|
* @param renderer_base $renderer
|
|
* @return moodle_url
|
|
*/
|
|
public function get_url(moodle_page $page, renderer_base $renderer = null) {
|
|
global $CFG;
|
|
|
|
if (is_null($renderer)) {
|
|
$renderer = $page->get_renderer('core');
|
|
}
|
|
|
|
// Sort out the filename and size. Size is only required for the gravatar
|
|
// implementation presently.
|
|
if (empty($this->size)) {
|
|
$filename = 'f2';
|
|
$size = 35;
|
|
} else if ($this->size === true or $this->size == 1) {
|
|
$filename = 'f1';
|
|
$size = 100;
|
|
} else if ($this->size > 100) {
|
|
$filename = 'f3';
|
|
$size = (int)$this->size;
|
|
} else if ($this->size >= 50) {
|
|
$filename = 'f1';
|
|
$size = (int)$this->size;
|
|
} else {
|
|
$filename = 'f2';
|
|
$size = (int)$this->size;
|
|
}
|
|
|
|
$defaulturl = $renderer->image_url('u/'.$filename); // default image
|
|
|
|
if ((!empty($CFG->forcelogin) and !isloggedin()) ||
|
|
(!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) {
|
|
// Protect images if login required and not logged in;
|
|
// also if login is required for profile images and is not logged in or guest
|
|
// do not use require_login() because it is expensive and not suitable here anyway.
|
|
return $defaulturl;
|
|
}
|
|
|
|
// First try to detect deleted users - but do not read from database for performance reasons!
|
|
if (!empty($this->user->deleted) or strpos($this->user->email, '@') === false) {
|
|
// All deleted users should have email replaced by md5 hash,
|
|
// all active users are expected to have valid email.
|
|
return $defaulturl;
|
|
}
|
|
|
|
// Did the user upload a picture?
|
|
if ($this->user->picture > 0) {
|
|
if (!empty($this->user->contextid)) {
|
|
$contextid = $this->user->contextid;
|
|
} else {
|
|
$context = context_user::instance($this->user->id, IGNORE_MISSING);
|
|
if (!$context) {
|
|
// This must be an incorrectly deleted user, all other users have context.
|
|
return $defaulturl;
|
|
}
|
|
$contextid = $context->id;
|
|
}
|
|
|
|
$path = '/';
|
|
if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) {
|
|
// We append the theme name to the file path if we have it so that
|
|
// in the circumstance that the profile picture is not available
|
|
// when the user actually requests it they still get the profile
|
|
// picture for the correct theme.
|
|
$path .= $page->theme->name.'/';
|
|
}
|
|
// Set the image URL to the URL for the uploaded file and return.
|
|
$url = moodle_url::make_pluginfile_url(
|
|
$contextid, 'user', 'icon', null, $path, $filename, false, $this->includetoken);
|
|
$url->param('rev', $this->user->picture);
|
|
return $url;
|
|
}
|
|
|
|
if ($this->user->picture == 0 and !empty($CFG->enablegravatar)) {
|
|
// Normalise the size variable to acceptable bounds
|
|
if ($size < 1 || $size > 512) {
|
|
$size = 35;
|
|
}
|
|
// Hash the users email address
|
|
$md5 = md5(strtolower(trim($this->user->email)));
|
|
// Build a gravatar URL with what we know.
|
|
|
|
// Find the best default image URL we can (MDL-35669)
|
|
if (empty($CFG->gravatardefaulturl)) {
|
|
$absoluteimagepath = $page->theme->resolve_image_location('u/'.$filename, 'core');
|
|
if (strpos($absoluteimagepath, $CFG->dirroot) === 0) {
|
|
$gravatardefault = $CFG->wwwroot . substr($absoluteimagepath, strlen($CFG->dirroot));
|
|
} else {
|
|
$gravatardefault = $CFG->wwwroot . '/pix/u/' . $filename . '.png';
|
|
}
|
|
} else {
|
|
$gravatardefault = $CFG->gravatardefaulturl;
|
|
}
|
|
|
|
// If the currently requested page is https then we'll return an
|
|
// https gravatar page.
|
|
if (is_https()) {
|
|
return new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault));
|
|
} else {
|
|
return new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault));
|
|
}
|
|
}
|
|
|
|
return $defaulturl;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure representing a help icon.
|
|
*
|
|
* @copyright 2010 Petr Skoda (info@skodak.org)
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class help_icon implements renderable, templatable {
|
|
|
|
/**
|
|
* @var string lang pack identifier (without the "_help" suffix),
|
|
* both get_string($identifier, $component) and get_string($identifier.'_help', $component)
|
|
* must exist.
|
|
*/
|
|
public $identifier;
|
|
|
|
/**
|
|
* @var string Component name, the same as in get_string()
|
|
*/
|
|
public $component;
|
|
|
|
/**
|
|
* @var string Extra descriptive text next to the icon
|
|
*/
|
|
public $linktext = null;
|
|
|
|
/**
|
|
* @var mixed An object, string or number that can be used within translation strings
|
|
*/
|
|
public $a = null;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $identifier string for help page title,
|
|
* string with _help suffix is used for the actual help text.
|
|
* string with _link suffix is used to create a link to further info (if it exists)
|
|
* @param string $component
|
|
* @param string|object|array|int $a An object, string or number that can be used
|
|
* within translation strings
|
|
*/
|
|
public function __construct($identifier, $component, $a = null) {
|
|
$this->identifier = $identifier;
|
|
$this->component = $component;
|
|
$this->a = $a;
|
|
}
|
|
|
|
/**
|
|
* Verifies that both help strings exists, shows debug warnings if not
|
|
*/
|
|
public function diag_strings() {
|
|
$sm = get_string_manager();
|
|
if (!$sm->string_exists($this->identifier, $this->component)) {
|
|
debugging("Help title string does not exist: [$this->identifier, $this->component]");
|
|
}
|
|
if (!$sm->string_exists($this->identifier.'_help', $this->component)) {
|
|
debugging("Help contents string does not exist: [{$this->identifier}_help, $this->component]");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
global $CFG;
|
|
|
|
$title = get_string($this->identifier, $this->component, $this->a);
|
|
|
|
if (empty($this->linktext)) {
|
|
$alt = get_string('helpprefix2', '', trim($title, ". \t"));
|
|
} else {
|
|
$alt = get_string('helpwiththis');
|
|
}
|
|
|
|
$data = get_formatted_help_string($this->identifier, $this->component, false, $this->a);
|
|
|
|
$data->alt = $alt;
|
|
$data->icon = (new pix_icon('help', $alt, 'core', ['class' => 'iconhelp']))->export_for_template($output);
|
|
$data->linktext = $this->linktext;
|
|
$data->title = get_string('helpprefix2', '', trim($title, ". \t"));
|
|
|
|
$options = [
|
|
'component' => $this->component,
|
|
'identifier' => $this->identifier,
|
|
'lang' => current_language()
|
|
];
|
|
|
|
// Debugging feature lets you display string identifier and component.
|
|
if (isset($CFG->debugstringids) && $CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) {
|
|
$options['strings'] = 1;
|
|
}
|
|
|
|
$data->url = (new moodle_url('/help.php', $options))->out(false);
|
|
$data->ltr = !right_to_left();
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Data structure representing an icon font.
|
|
*
|
|
* @copyright 2016 Damyon Wiese
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class pix_icon_font implements templatable {
|
|
|
|
/**
|
|
* @var pix_icon $pixicon The original icon.
|
|
*/
|
|
private $pixicon = null;
|
|
|
|
/**
|
|
* @var string $key The mapped key.
|
|
*/
|
|
private $key;
|
|
|
|
/**
|
|
* @var bool $mapped The icon could not be mapped.
|
|
*/
|
|
private $mapped;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param pix_icon $pixicon The original icon
|
|
*/
|
|
public function __construct(pix_icon $pixicon) {
|
|
global $PAGE;
|
|
|
|
$this->pixicon = $pixicon;
|
|
$this->mapped = false;
|
|
$iconsystem = \core\output\icon_system::instance();
|
|
|
|
$this->key = $iconsystem->remap_icon_name($pixicon->pix, $pixicon->component);
|
|
if (!empty($this->key)) {
|
|
$this->mapped = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return true if this pix_icon was successfully mapped to an icon font.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_mapped() {
|
|
return $this->mapped;
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
|
* @return array
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
|
|
$pixdata = $this->pixicon->export_for_template($output);
|
|
|
|
$title = isset($this->pixicon->attributes['title']) ? $this->pixicon->attributes['title'] : '';
|
|
$alt = isset($this->pixicon->attributes['alt']) ? $this->pixicon->attributes['alt'] : '';
|
|
if (empty($title)) {
|
|
$title = $alt;
|
|
}
|
|
$data = array(
|
|
'extraclasses' => $pixdata['extraclasses'],
|
|
'title' => $title,
|
|
'alt' => $alt,
|
|
'key' => $this->key
|
|
);
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure representing an icon subtype.
|
|
*
|
|
* @copyright 2016 Damyon Wiese
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class pix_icon_fontawesome extends pix_icon_font {
|
|
|
|
}
|
|
|
|
/**
|
|
* Data structure representing an icon.
|
|
*
|
|
* @copyright 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class pix_icon implements renderable, templatable {
|
|
|
|
/**
|
|
* @var string The icon name
|
|
*/
|
|
var $pix;
|
|
|
|
/**
|
|
* @var string The component the icon belongs to.
|
|
*/
|
|
var $component;
|
|
|
|
/**
|
|
* @var array An array of attributes to use on the icon
|
|
*/
|
|
var $attributes = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $pix short icon name
|
|
* @param string $alt The alt text to use for the icon
|
|
* @param string $component component name
|
|
* @param array $attributes html attributes
|
|
*/
|
|
public function __construct($pix, $alt, $component='moodle', array $attributes = null) {
|
|
global $PAGE;
|
|
|
|
$this->pix = $pix;
|
|
$this->component = $component;
|
|
$this->attributes = (array)$attributes;
|
|
|
|
if (empty($this->attributes['class'])) {
|
|
$this->attributes['class'] = '';
|
|
}
|
|
|
|
// Set an additional class for big icons so that they can be styled properly.
|
|
if (substr($pix, 0, 2) === 'b/') {
|
|
$this->attributes['class'] .= ' iconsize-big';
|
|
}
|
|
|
|
// If the alt is empty, don't place it in the attributes, otherwise it will override parent alt text.
|
|
if (!is_null($alt)) {
|
|
$this->attributes['alt'] = $alt;
|
|
|
|
// If there is no title, set it to the attribute.
|
|
if (!isset($this->attributes['title'])) {
|
|
$this->attributes['title'] = $this->attributes['alt'];
|
|
}
|
|
} else {
|
|
unset($this->attributes['alt']);
|
|
}
|
|
|
|
if (empty($this->attributes['title'])) {
|
|
// Remove the title attribute if empty, we probably want to use the parent node's title
|
|
// and some browsers might overwrite it with an empty title.
|
|
unset($this->attributes['title']);
|
|
}
|
|
|
|
// Hide icons from screen readers that have no alt.
|
|
if (empty($this->attributes['alt'])) {
|
|
$this->attributes['aria-hidden'] = 'true';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
|
* @return array
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$attributes = $this->attributes;
|
|
$extraclasses = '';
|
|
|
|
foreach ($attributes as $key => $item) {
|
|
if ($key == 'class') {
|
|
$extraclasses = $item;
|
|
unset($attributes[$key]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
$attributes['src'] = $output->image_url($this->pix, $this->component)->out(false);
|
|
$templatecontext = array();
|
|
foreach ($attributes as $name => $value) {
|
|
$templatecontext[] = array('name' => $name, 'value' => $value);
|
|
}
|
|
$title = isset($attributes['title']) ? $attributes['title'] : '';
|
|
if (empty($title)) {
|
|
$title = isset($attributes['alt']) ? $attributes['alt'] : '';
|
|
}
|
|
$data = array(
|
|
'attributes' => $templatecontext,
|
|
'extraclasses' => $extraclasses
|
|
);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Much simpler version of export that will produce the data required to render this pix with the
|
|
* pix helper in a mustache tag.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function export_for_pix() {
|
|
$title = isset($this->attributes['title']) ? $this->attributes['title'] : '';
|
|
if (empty($title)) {
|
|
$title = isset($this->attributes['alt']) ? $this->attributes['alt'] : '';
|
|
}
|
|
return [
|
|
'key' => $this->pix,
|
|
'component' => $this->component,
|
|
'title' => (string) $title,
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure representing an activity icon.
|
|
*
|
|
* The difference is that activity icons will always render with the standard icon system (no font icons).
|
|
*
|
|
* @copyright 2017 Damyon Wiese
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @package core
|
|
*/
|
|
class image_icon extends pix_icon {
|
|
}
|
|
|
|
/**
|
|
* Data structure representing an emoticon image
|
|
*
|
|
* @copyright 2010 David Mudrak
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class pix_emoticon extends pix_icon implements renderable {
|
|
|
|
/**
|
|
* Constructor
|
|
* @param string $pix short icon name
|
|
* @param string $alt alternative text
|
|
* @param string $component emoticon image provider
|
|
* @param array $attributes explicit HTML attributes
|
|
*/
|
|
public function __construct($pix, $alt, $component = 'moodle', array $attributes = array()) {
|
|
if (empty($attributes['class'])) {
|
|
$attributes['class'] = 'emoticon';
|
|
}
|
|
parent::__construct($pix, $alt, $component, $attributes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure representing a simple form with only one button.
|
|
*
|
|
* @copyright 2009 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class single_button implements renderable {
|
|
|
|
/**
|
|
* Possible button types. From boostrap.
|
|
*/
|
|
const BUTTON_TYPES = [
|
|
self::BUTTON_PRIMARY,
|
|
self::BUTTON_SECONDARY,
|
|
self::BUTTON_SUCCESS,
|
|
self::BUTTON_DANGER,
|
|
self::BUTTON_WARNING,
|
|
self::BUTTON_INFO
|
|
];
|
|
|
|
/**
|
|
* Possible button types - Primary.
|
|
*/
|
|
const BUTTON_PRIMARY = 'primary';
|
|
/**
|
|
* Possible button types - Secondary.
|
|
*/
|
|
const BUTTON_SECONDARY = 'secondary';
|
|
/**
|
|
* Possible button types - Danger.
|
|
*/
|
|
const BUTTON_DANGER = 'danger';
|
|
/**
|
|
* Possible button types - Success.
|
|
*/
|
|
const BUTTON_SUCCESS = 'success';
|
|
/**
|
|
* Possible button types - Warning.
|
|
*/
|
|
const BUTTON_WARNING = 'warning';
|
|
/**
|
|
* Possible button types - Info.
|
|
*/
|
|
const BUTTON_INFO = 'info';
|
|
|
|
/**
|
|
* @var moodle_url Target url
|
|
*/
|
|
public $url;
|
|
|
|
/**
|
|
* @var string Button label
|
|
*/
|
|
public $label;
|
|
|
|
/**
|
|
* @var string Form submit method post or get
|
|
*/
|
|
public $method = 'post';
|
|
|
|
/**
|
|
* @var string Wrapping div class
|
|
*/
|
|
public $class = 'singlebutton';
|
|
|
|
/**
|
|
* @var string Type of button (from defined types). Used for styling.
|
|
*/
|
|
protected $type;
|
|
|
|
/**
|
|
* @var bool True if button is primary button. Used for styling.
|
|
* @deprecated since Moodle 4.2
|
|
*/
|
|
private $primary = false;
|
|
|
|
/**
|
|
* @var bool True if button disabled, false if normal
|
|
*/
|
|
public $disabled = false;
|
|
|
|
/**
|
|
* @var string Button tooltip
|
|
*/
|
|
public $tooltip = null;
|
|
|
|
/**
|
|
* @var string Form id
|
|
*/
|
|
public $formid;
|
|
|
|
/**
|
|
* @var array List of attached actions
|
|
*/
|
|
public $actions = array();
|
|
|
|
/**
|
|
* @var array $params URL Params
|
|
*/
|
|
public $params;
|
|
|
|
/**
|
|
* @var string Action id
|
|
*/
|
|
public $actionid;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $attributes = [];
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param moodle_url $url
|
|
* @param string $label button text
|
|
* @param string $method get or post submit method
|
|
* @param string $type whether this is a primary button or another type, used for styling
|
|
* @param array $attributes Attributes for the HTML button tag
|
|
*/
|
|
public function __construct(moodle_url $url, $label, $method = 'post', $type = self::BUTTON_SECONDARY,
|
|
$attributes = []) {
|
|
if (is_bool($type)) {
|
|
debugging('The boolean $primary is deprecated and replaced by $type,
|
|
use single_button::BUTTON_PRIMARY or self::BUTTON_SECONDARY instead');
|
|
$type = $type ? self::BUTTON_PRIMARY : self::BUTTON_SECONDARY;
|
|
}
|
|
$this->url = clone($url);
|
|
$this->label = $label;
|
|
$this->method = $method;
|
|
$this->type = $type;
|
|
$this->attributes = $attributes;
|
|
}
|
|
|
|
/**
|
|
* Shortcut for adding a JS confirm dialog when the button is clicked.
|
|
* The message must be a yes/no question.
|
|
*
|
|
* @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
|
|
*/
|
|
public function add_confirm_action($confirmmessage) {
|
|
$this->add_action(new confirm_action($confirmmessage));
|
|
}
|
|
|
|
/**
|
|
* Add action to the button.
|
|
* @param component_action $action
|
|
*/
|
|
public function add_action(component_action $action) {
|
|
$this->actions[] = $action;
|
|
}
|
|
|
|
/**
|
|
* Sets an attribute for the HTML button tag.
|
|
*
|
|
* @param string $name The attribute name
|
|
* @param mixed $value The value
|
|
* @return null
|
|
*/
|
|
public function set_attribute($name, $value) {
|
|
$this->attributes[$name] = $value;
|
|
}
|
|
|
|
/**
|
|
* Magic setter method.
|
|
*
|
|
* This method manages access to some properties and will display deprecation message when accessing 'primary' property.
|
|
*
|
|
* @param string $name
|
|
* @param mixed $value
|
|
*/
|
|
public function __set($name, $value) {
|
|
switch ($name) {
|
|
case 'primary':
|
|
debugging('The primary field is deprecated, use the type field instead');
|
|
// Here just in case we modified the primary field from outside {@see \mod_quiz_renderer::summary_page_controls}.
|
|
$this->type = $value ? self::BUTTON_PRIMARY : self::BUTTON_SECONDARY;
|
|
break;
|
|
case 'type':
|
|
$this->type = in_array($value, self::BUTTON_TYPES) ? $value : self::BUTTON_SECONDARY;
|
|
break;
|
|
default:
|
|
$this->$name = $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Magic method getter.
|
|
*
|
|
* This method manages access to some properties and will display deprecation message when accessing 'primary' property.
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
public function __get($name) {
|
|
switch ($name) {
|
|
case 'primary':
|
|
debugging('The primary field is deprecated, use type field instead');
|
|
return $this->type == self::BUTTON_PRIMARY;
|
|
case 'type':
|
|
return $this->type;
|
|
default:
|
|
return $this->$name;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export data.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$url = $this->method === 'get' ? $this->url->out_omit_querystring(true) : $this->url->out_omit_querystring();
|
|
|
|
$data = new stdClass();
|
|
$data->id = html_writer::random_id('single_button');
|
|
$data->formid = $this->formid;
|
|
$data->method = $this->method;
|
|
$data->url = $url === '' ? '#' : $url;
|
|
$data->label = $this->label;
|
|
$data->classes = $this->class;
|
|
$data->disabled = $this->disabled;
|
|
$data->tooltip = $this->tooltip;
|
|
$data->type = $this->type;
|
|
$data->attributes = [];
|
|
foreach ($this->attributes as $key => $value) {
|
|
$data->attributes[] = ['name' => $key, 'value' => $value];
|
|
}
|
|
|
|
// Form parameters.
|
|
$actionurl = new moodle_url($this->url);
|
|
if ($this->method === 'post') {
|
|
$actionurl->param('sesskey', sesskey());
|
|
}
|
|
$data->params = $actionurl->export_params_for_template();
|
|
|
|
// Button actions.
|
|
$actions = $this->actions;
|
|
$data->actions = array_map(function($action) use ($output) {
|
|
return $action->export_for_template($output);
|
|
}, $actions);
|
|
$data->hasactions = !empty($data->actions);
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Simple form with just one select field that gets submitted automatically.
|
|
*
|
|
* If JS not enabled small go button is printed too.
|
|
*
|
|
* @copyright 2009 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class single_select implements renderable, templatable {
|
|
|
|
/**
|
|
* @var moodle_url Target url - includes hidden fields
|
|
*/
|
|
var $url;
|
|
|
|
/**
|
|
* @var string Name of the select element.
|
|
*/
|
|
var $name;
|
|
|
|
/**
|
|
* @var array $options associative array value=>label ex.: array(1=>'One, 2=>Two)
|
|
* it is also possible to specify optgroup as complex label array ex.:
|
|
* array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
|
|
* array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
|
|
*/
|
|
var $options;
|
|
|
|
/**
|
|
* @var string Selected option
|
|
*/
|
|
var $selected;
|
|
|
|
/**
|
|
* @var array Nothing selected
|
|
*/
|
|
var $nothing;
|
|
|
|
/**
|
|
* @var array Extra select field attributes
|
|
*/
|
|
var $attributes = array();
|
|
|
|
/**
|
|
* @var string Button label
|
|
*/
|
|
var $label = '';
|
|
|
|
/**
|
|
* @var array Button label's attributes
|
|
*/
|
|
var $labelattributes = array();
|
|
|
|
/**
|
|
* @var string Form submit method post or get
|
|
*/
|
|
var $method = 'get';
|
|
|
|
/**
|
|
* @var string Wrapping div class
|
|
*/
|
|
var $class = 'singleselect';
|
|
|
|
/**
|
|
* @var bool True if button disabled, false if normal
|
|
*/
|
|
var $disabled = false;
|
|
|
|
/**
|
|
* @var string Button tooltip
|
|
*/
|
|
var $tooltip = null;
|
|
|
|
/**
|
|
* @var string Form id
|
|
*/
|
|
var $formid = null;
|
|
|
|
/**
|
|
* @var help_icon The help icon for this element.
|
|
*/
|
|
var $helpicon = null;
|
|
|
|
/** @var component_action[] component action. */
|
|
public $actions = [];
|
|
|
|
/**
|
|
* Constructor
|
|
* @param moodle_url $url form action target, includes hidden fields
|
|
* @param string $name name of selection field - the changing parameter in url
|
|
* @param array $options list of options
|
|
* @param string $selected selected element
|
|
* @param ?array $nothing
|
|
* @param string $formid
|
|
*/
|
|
public function __construct(moodle_url $url, $name, array $options, $selected = '', $nothing = array('' => 'choosedots'), $formid = null) {
|
|
$this->url = $url;
|
|
$this->name = $name;
|
|
$this->options = $options;
|
|
$this->selected = $selected;
|
|
$this->nothing = $nothing;
|
|
$this->formid = $formid;
|
|
}
|
|
|
|
/**
|
|
* Shortcut for adding a JS confirm dialog when the button is clicked.
|
|
* The message must be a yes/no question.
|
|
*
|
|
* @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
|
|
*/
|
|
public function add_confirm_action($confirmmessage) {
|
|
$this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
|
|
}
|
|
|
|
/**
|
|
* Add action to the button.
|
|
*
|
|
* @param component_action $action
|
|
*/
|
|
public function add_action(component_action $action) {
|
|
$this->actions[] = $action;
|
|
}
|
|
|
|
/**
|
|
* Adds help icon.
|
|
*
|
|
* @deprecated since Moodle 2.0
|
|
*/
|
|
public function set_old_help_icon($helppage, $title, $component = 'moodle') {
|
|
throw new coding_exception('set_old_help_icon() can not be used any more, please see set_help_icon().');
|
|
}
|
|
|
|
/**
|
|
* Adds help icon.
|
|
*
|
|
* @param string $identifier The keyword that defines a help page
|
|
* @param string $component
|
|
*/
|
|
public function set_help_icon($identifier, $component = 'moodle') {
|
|
$this->helpicon = new help_icon($identifier, $component);
|
|
}
|
|
|
|
/**
|
|
* Sets select's label
|
|
*
|
|
* @param string $label
|
|
* @param array $attributes (optional)
|
|
*/
|
|
public function set_label($label, $attributes = array()) {
|
|
$this->label = $label;
|
|
$this->labelattributes = $attributes;
|
|
|
|
}
|
|
|
|
/**
|
|
* Export data.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$attributes = $this->attributes;
|
|
|
|
$data = new stdClass();
|
|
$data->name = $this->name;
|
|
$data->method = $this->method;
|
|
$data->action = $this->method === 'get' ? $this->url->out_omit_querystring(true) : $this->url->out_omit_querystring();
|
|
$data->classes = $this->class;
|
|
$data->label = $this->label;
|
|
$data->disabled = $this->disabled;
|
|
$data->title = $this->tooltip;
|
|
$data->formid = !empty($this->formid) ? $this->formid : html_writer::random_id('single_select_f');
|
|
$data->id = !empty($attributes['id']) ? $attributes['id'] : html_writer::random_id('single_select');
|
|
|
|
// Select element attributes.
|
|
// Unset attributes that are already predefined in the template.
|
|
unset($attributes['id']);
|
|
unset($attributes['class']);
|
|
unset($attributes['name']);
|
|
unset($attributes['title']);
|
|
unset($attributes['disabled']);
|
|
|
|
// Map the attributes.
|
|
$data->attributes = array_map(function($key) use ($attributes) {
|
|
return ['name' => $key, 'value' => $attributes[$key]];
|
|
}, array_keys($attributes));
|
|
|
|
// Form parameters.
|
|
$actionurl = new moodle_url($this->url);
|
|
if ($this->method === 'post') {
|
|
$actionurl->param('sesskey', sesskey());
|
|
}
|
|
$data->params = $actionurl->export_params_for_template();
|
|
|
|
// Select options.
|
|
$hasnothing = false;
|
|
if (is_string($this->nothing) && $this->nothing !== '') {
|
|
$nothing = ['' => $this->nothing];
|
|
$hasnothing = true;
|
|
$nothingkey = '';
|
|
} else if (is_array($this->nothing)) {
|
|
$nothingvalue = reset($this->nothing);
|
|
if ($nothingvalue === 'choose' || $nothingvalue === 'choosedots') {
|
|
$nothing = [key($this->nothing) => get_string('choosedots')];
|
|
} else {
|
|
$nothing = $this->nothing;
|
|
}
|
|
$hasnothing = true;
|
|
$nothingkey = key($this->nothing);
|
|
}
|
|
if ($hasnothing) {
|
|
$options = $nothing + $this->options;
|
|
} else {
|
|
$options = $this->options;
|
|
}
|
|
|
|
foreach ($options as $value => $name) {
|
|
if (is_array($options[$value])) {
|
|
foreach ($options[$value] as $optgroupname => $optgroupvalues) {
|
|
$sublist = [];
|
|
foreach ($optgroupvalues as $optvalue => $optname) {
|
|
$option = [
|
|
'value' => $optvalue,
|
|
'name' => $optname,
|
|
'selected' => strval($this->selected) === strval($optvalue),
|
|
];
|
|
|
|
if ($hasnothing && $nothingkey === $optvalue) {
|
|
$option['ignore'] = 'data-ignore';
|
|
}
|
|
|
|
$sublist[] = $option;
|
|
}
|
|
$data->options[] = [
|
|
'name' => $optgroupname,
|
|
'optgroup' => true,
|
|
'options' => $sublist
|
|
];
|
|
}
|
|
} else {
|
|
$option = [
|
|
'value' => $value,
|
|
'name' => $options[$value],
|
|
'selected' => strval($this->selected) === strval($value),
|
|
'optgroup' => false
|
|
];
|
|
|
|
if ($hasnothing && $nothingkey === $value) {
|
|
$option['ignore'] = 'data-ignore';
|
|
}
|
|
|
|
$data->options[] = $option;
|
|
}
|
|
}
|
|
|
|
// Label attributes.
|
|
$data->labelattributes = [];
|
|
// Unset label attributes that are already in the template.
|
|
unset($this->labelattributes['for']);
|
|
// Map the label attributes.
|
|
foreach ($this->labelattributes as $key => $value) {
|
|
$data->labelattributes[] = ['name' => $key, 'value' => $value];
|
|
}
|
|
|
|
// Help icon.
|
|
$data->helpicon = !empty($this->helpicon) ? $this->helpicon->export_for_template($output) : false;
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple URL selection widget description.
|
|
*
|
|
* @copyright 2009 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class url_select implements renderable, templatable {
|
|
/**
|
|
* @var array $urls associative array value=>label ex.: array(1=>'One, 2=>Two)
|
|
* it is also possible to specify optgroup as complex label array ex.:
|
|
* array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
|
|
* array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
|
|
*/
|
|
var $urls;
|
|
|
|
/**
|
|
* @var string Selected option
|
|
*/
|
|
var $selected;
|
|
|
|
/**
|
|
* @var array Nothing selected
|
|
*/
|
|
var $nothing;
|
|
|
|
/**
|
|
* @var array Extra select field attributes
|
|
*/
|
|
var $attributes = array();
|
|
|
|
/**
|
|
* @var string Button label
|
|
*/
|
|
var $label = '';
|
|
|
|
/**
|
|
* @var array Button label's attributes
|
|
*/
|
|
var $labelattributes = array();
|
|
|
|
/**
|
|
* @var string Wrapping div class
|
|
*/
|
|
var $class = 'urlselect';
|
|
|
|
/**
|
|
* @var bool True if button disabled, false if normal
|
|
*/
|
|
var $disabled = false;
|
|
|
|
/**
|
|
* @var string Button tooltip
|
|
*/
|
|
var $tooltip = null;
|
|
|
|
/**
|
|
* @var string Form id
|
|
*/
|
|
var $formid = null;
|
|
|
|
/**
|
|
* @var help_icon The help icon for this element.
|
|
*/
|
|
var $helpicon = null;
|
|
|
|
/**
|
|
* @var string If set, makes button visible with given name for button
|
|
*/
|
|
var $showbutton = null;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param array $urls list of options
|
|
* @param string $selected selected element
|
|
* @param array $nothing
|
|
* @param string $formid
|
|
* @param string $showbutton Set to text of button if it should be visible
|
|
* or null if it should be hidden (hidden version always has text 'go')
|
|
*/
|
|
public function __construct(array $urls, $selected = '', $nothing = array('' => 'choosedots'), $formid = null, $showbutton = null) {
|
|
$this->urls = $urls;
|
|
$this->selected = $selected;
|
|
$this->nothing = $nothing;
|
|
$this->formid = $formid;
|
|
$this->showbutton = $showbutton;
|
|
}
|
|
|
|
/**
|
|
* Adds help icon.
|
|
*
|
|
* @deprecated since Moodle 2.0
|
|
*/
|
|
public function set_old_help_icon($helppage, $title, $component = 'moodle') {
|
|
throw new coding_exception('set_old_help_icon() can not be used any more, please see set_help_icon().');
|
|
}
|
|
|
|
/**
|
|
* Adds help icon.
|
|
*
|
|
* @param string $identifier The keyword that defines a help page
|
|
* @param string $component
|
|
*/
|
|
public function set_help_icon($identifier, $component = 'moodle') {
|
|
$this->helpicon = new help_icon($identifier, $component);
|
|
}
|
|
|
|
/**
|
|
* Sets select's label
|
|
*
|
|
* @param string $label
|
|
* @param array $attributes (optional)
|
|
*/
|
|
public function set_label($label, $attributes = array()) {
|
|
$this->label = $label;
|
|
$this->labelattributes = $attributes;
|
|
}
|
|
|
|
/**
|
|
* Clean a URL.
|
|
*
|
|
* @param string $value The URL.
|
|
* @return string The cleaned URL.
|
|
*/
|
|
protected function clean_url($value) {
|
|
global $CFG;
|
|
|
|
if (empty($value)) {
|
|
// Nothing.
|
|
|
|
} else if (strpos($value, $CFG->wwwroot . '/') === 0) {
|
|
$value = str_replace($CFG->wwwroot, '', $value);
|
|
|
|
} else if (strpos($value, '/') !== 0) {
|
|
debugging("Invalid url_select urls parameter: url '$value' is not local relative url!", DEBUG_DEVELOPER);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Flatten the options for Mustache.
|
|
*
|
|
* This also cleans the URLs.
|
|
*
|
|
* @param array $options The options.
|
|
* @param array $nothing The nothing option.
|
|
* @return array
|
|
*/
|
|
protected function flatten_options($options, $nothing) {
|
|
$flattened = [];
|
|
|
|
foreach ($options as $value => $option) {
|
|
if (is_array($option)) {
|
|
foreach ($option as $groupname => $optoptions) {
|
|
if (!isset($flattened[$groupname])) {
|
|
$flattened[$groupname] = [
|
|
'name' => $groupname,
|
|
'isgroup' => true,
|
|
'options' => []
|
|
];
|
|
}
|
|
foreach ($optoptions as $optvalue => $optoption) {
|
|
$cleanedvalue = $this->clean_url($optvalue);
|
|
$flattened[$groupname]['options'][$cleanedvalue] = [
|
|
'name' => $optoption,
|
|
'value' => $cleanedvalue,
|
|
'selected' => $this->selected == $optvalue,
|
|
];
|
|
}
|
|
}
|
|
|
|
} else {
|
|
$cleanedvalue = $this->clean_url($value);
|
|
$flattened[$cleanedvalue] = [
|
|
'name' => $option,
|
|
'value' => $cleanedvalue,
|
|
'selected' => $this->selected == $value,
|
|
];
|
|
}
|
|
}
|
|
|
|
if (!empty($nothing)) {
|
|
$value = key($nothing);
|
|
$name = reset($nothing);
|
|
$flattened = [
|
|
$value => ['name' => $name, 'value' => $value, 'selected' => $this->selected == $value]
|
|
] + $flattened;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$attributes = $this->attributes;
|
|
|
|
$data = new stdClass();
|
|
$data->formid = !empty($this->formid) ? $this->formid : html_writer::random_id('url_select_f');
|
|
$data->classes = $this->class;
|
|
$data->label = $this->label;
|
|
$data->disabled = $this->disabled;
|
|
$data->title = $this->tooltip;
|
|
$data->id = !empty($attributes['id']) ? $attributes['id'] : html_writer::random_id('url_select');
|
|
$data->sesskey = sesskey();
|
|
$data->action = (new moodle_url('/course/jumpto.php'))->out(false);
|
|
|
|
// Remove attributes passed as property directly.
|
|
unset($attributes['class']);
|
|
unset($attributes['id']);
|
|
unset($attributes['name']);
|
|
unset($attributes['title']);
|
|
unset($attributes['disabled']);
|
|
|
|
$data->showbutton = $this->showbutton;
|
|
|
|
// Select options.
|
|
$nothing = false;
|
|
if (is_string($this->nothing) && $this->nothing !== '') {
|
|
$nothing = ['' => $this->nothing];
|
|
} else if (is_array($this->nothing)) {
|
|
$nothingvalue = reset($this->nothing);
|
|
if ($nothingvalue === 'choose' || $nothingvalue === 'choosedots') {
|
|
$nothing = [key($this->nothing) => get_string('choosedots')];
|
|
} else {
|
|
$nothing = $this->nothing;
|
|
}
|
|
}
|
|
$data->options = $this->flatten_options($this->urls, $nothing);
|
|
|
|
// Label attributes.
|
|
$data->labelattributes = [];
|
|
// Unset label attributes that are already in the template.
|
|
unset($this->labelattributes['for']);
|
|
// Map the label attributes.
|
|
foreach ($this->labelattributes as $key => $value) {
|
|
$data->labelattributes[] = ['name' => $key, 'value' => $value];
|
|
}
|
|
|
|
// Help icon.
|
|
$data->helpicon = !empty($this->helpicon) ? $this->helpicon->export_for_template($output) : false;
|
|
|
|
// Finally all the remaining attributes.
|
|
$data->attributes = [];
|
|
foreach ($attributes as $key => $value) {
|
|
$data->attributes[] = ['name' => $key, 'value' => $value];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data structure describing html link with special action attached.
|
|
*
|
|
* @copyright 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class action_link implements renderable {
|
|
|
|
/**
|
|
* @var moodle_url Href url
|
|
*/
|
|
public $url;
|
|
|
|
/**
|
|
* @var string Link text HTML fragment
|
|
*/
|
|
public $text;
|
|
|
|
/**
|
|
* @var array HTML attributes
|
|
*/
|
|
public $attributes;
|
|
|
|
/**
|
|
* @var array List of actions attached to link
|
|
*/
|
|
public $actions;
|
|
|
|
/**
|
|
* @var pix_icon Optional pix icon to render with the link
|
|
*/
|
|
public $icon;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param moodle_url $url
|
|
* @param string $text HTML fragment
|
|
* @param component_action $action
|
|
* @param array $attributes associative array of html link attributes + disabled
|
|
* @param pix_icon $icon optional pix_icon to render with the link text
|
|
*/
|
|
public function __construct(moodle_url $url,
|
|
$text,
|
|
component_action $action=null,
|
|
array $attributes=null,
|
|
pix_icon $icon=null) {
|
|
$this->url = clone($url);
|
|
$this->text = $text;
|
|
if (empty($attributes['id'])) {
|
|
$attributes['id'] = html_writer::random_id('action_link');
|
|
}
|
|
$this->attributes = (array)$attributes;
|
|
if ($action) {
|
|
$this->add_action($action);
|
|
}
|
|
$this->icon = $icon;
|
|
}
|
|
|
|
/**
|
|
* Add action to the link.
|
|
*
|
|
* @param component_action $action
|
|
*/
|
|
public function add_action(component_action $action) {
|
|
$this->actions[] = $action;
|
|
}
|
|
|
|
/**
|
|
* Adds a CSS class to this action link object
|
|
* @param string $class
|
|
*/
|
|
public function add_class($class) {
|
|
if (empty($this->attributes['class'])) {
|
|
$this->attributes['class'] = $class;
|
|
} else {
|
|
$this->attributes['class'] .= ' ' . $class;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the specified class has been added to this link.
|
|
* @param string $class
|
|
* @return bool
|
|
*/
|
|
public function has_class($class) {
|
|
return strpos(' ' . $this->attributes['class'] . ' ', ' ' . $class . ' ') !== false;
|
|
}
|
|
|
|
/**
|
|
* Return the rendered HTML for the icon. Useful for rendering action links in a template.
|
|
* @return string
|
|
*/
|
|
public function get_icon_html() {
|
|
global $OUTPUT;
|
|
if (!$this->icon) {
|
|
return '';
|
|
}
|
|
return $OUTPUT->render($this->icon);
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$data = new stdClass();
|
|
$attributes = $this->attributes;
|
|
|
|
$data->id = $attributes['id'];
|
|
unset($attributes['id']);
|
|
|
|
$data->disabled = !empty($attributes['disabled']);
|
|
unset($attributes['disabled']);
|
|
|
|
$data->text = $this->text instanceof renderable ? $output->render($this->text) : (string) $this->text;
|
|
$data->url = $this->url ? $this->url->out(false) : '';
|
|
$data->icon = $this->icon ? $this->icon->export_for_pix() : null;
|
|
$data->classes = isset($attributes['class']) ? $attributes['class'] : '';
|
|
unset($attributes['class']);
|
|
|
|
$data->attributes = array_map(function($key, $value) {
|
|
return [
|
|
'name' => $key,
|
|
'value' => $value
|
|
];
|
|
}, array_keys($attributes), $attributes);
|
|
|
|
$data->actions = array_map(function($action) use ($output) {
|
|
return $action->export_for_template($output);
|
|
}, !empty($this->actions) ? $this->actions : []);
|
|
$data->hasactions = !empty($this->actions);
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple html output class
|
|
*
|
|
* @copyright 2009 Tim Hunt, 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class html_writer {
|
|
|
|
/**
|
|
* Outputs a tag with attributes and contents
|
|
*
|
|
* @param string $tagname The name of tag ('a', 'img', 'span' etc.)
|
|
* @param string $contents What goes between the opening and closing tags
|
|
* @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function tag($tagname, $contents, array $attributes = null) {
|
|
return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname);
|
|
}
|
|
|
|
/**
|
|
* Outputs an opening tag with attributes
|
|
*
|
|
* @param string $tagname The name of tag ('a', 'img', 'span' etc.)
|
|
* @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function start_tag($tagname, array $attributes = null) {
|
|
return '<' . $tagname . self::attributes($attributes) . '>';
|
|
}
|
|
|
|
/**
|
|
* Outputs a closing tag
|
|
*
|
|
* @param string $tagname The name of tag ('a', 'img', 'span' etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function end_tag($tagname) {
|
|
return '</' . $tagname . '>';
|
|
}
|
|
|
|
/**
|
|
* Outputs an empty tag with attributes
|
|
*
|
|
* @param string $tagname The name of tag ('input', 'img', 'br' etc.)
|
|
* @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function empty_tag($tagname, array $attributes = null) {
|
|
return '<' . $tagname . self::attributes($attributes) . ' />';
|
|
}
|
|
|
|
/**
|
|
* Outputs a tag, but only if the contents are not empty
|
|
*
|
|
* @param string $tagname The name of tag ('a', 'img', 'span' etc.)
|
|
* @param string $contents What goes between the opening and closing tags
|
|
* @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function nonempty_tag($tagname, $contents, array $attributes = null) {
|
|
if ($contents === '' || is_null($contents)) {
|
|
return '';
|
|
}
|
|
return self::tag($tagname, $contents, $attributes);
|
|
}
|
|
|
|
/**
|
|
* Outputs a HTML attribute and value
|
|
*
|
|
* @param string $name The name of the attribute ('src', 'href', 'class' etc.)
|
|
* @param string $value The value of the attribute. The value will be escaped with {@link s()}
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function attribute($name, $value) {
|
|
if ($value instanceof moodle_url) {
|
|
return ' ' . $name . '="' . $value->out() . '"';
|
|
}
|
|
|
|
// special case, we do not want these in output
|
|
if ($value === null) {
|
|
return '';
|
|
}
|
|
|
|
// no sloppy trimming here!
|
|
return ' ' . $name . '="' . s($value) . '"';
|
|
}
|
|
|
|
/**
|
|
* Outputs a list of HTML attributes and values
|
|
*
|
|
* @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
|
|
* The values will be escaped with {@link s()}
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function attributes(array $attributes = null) {
|
|
$attributes = (array)$attributes;
|
|
$output = '';
|
|
foreach ($attributes as $name => $value) {
|
|
$output .= self::attribute($name, $value);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Generates a simple image tag with attributes.
|
|
*
|
|
* @param string $src The source of image
|
|
* @param string $alt The alternate text for image
|
|
* @param array $attributes The tag attributes (array('height' => $max_height, 'class' => 'class1') etc.)
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function img($src, $alt, array $attributes = null) {
|
|
$attributes = (array)$attributes;
|
|
$attributes['src'] = $src;
|
|
// In case a null alt text is provided, set it to an empty string.
|
|
$attributes['alt'] = $alt ?? '';
|
|
if (array_key_exists('role', $attributes) && core_text::strtolower($attributes['role']) === 'presentation') {
|
|
// A presentation role is not necessary for the img tag.
|
|
// If a non-empty alt text is provided, the presentation role will conflict with the alt text.
|
|
// An empty alt text denotes a decorative image. The presence of a presentation role is redundant.
|
|
unset($attributes['role']);
|
|
debugging('The presentation role is not necessary for an img tag.', DEBUG_DEVELOPER);
|
|
}
|
|
|
|
return self::empty_tag('img', $attributes);
|
|
}
|
|
|
|
/**
|
|
* Generates random html element id.
|
|
*
|
|
* @staticvar int $counter
|
|
* @staticvar string $uniq
|
|
* @param string $base A string fragment that will be included in the random ID.
|
|
* @return string A unique ID
|
|
*/
|
|
public static function random_id($base='random') {
|
|
static $counter = 0;
|
|
static $uniq;
|
|
|
|
if (!isset($uniq)) {
|
|
$uniq = uniqid();
|
|
}
|
|
|
|
$counter++;
|
|
return $base.$uniq.$counter;
|
|
}
|
|
|
|
/**
|
|
* Generates a simple html link
|
|
*
|
|
* @param string|moodle_url $url The URL
|
|
* @param string $text The text
|
|
* @param array $attributes HTML attributes
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function link($url, $text, array $attributes = null) {
|
|
$attributes = (array)$attributes;
|
|
$attributes['href'] = $url;
|
|
return self::tag('a', $text, $attributes);
|
|
}
|
|
|
|
/**
|
|
* Generates a simple checkbox with optional label
|
|
*
|
|
* @param string $name The name of the checkbox
|
|
* @param string $value The value of the checkbox
|
|
* @param bool $checked Whether the checkbox is checked
|
|
* @param string $label The label for the checkbox
|
|
* @param array $attributes Any attributes to apply to the checkbox
|
|
* @param array $labelattributes Any attributes to apply to the label, if present
|
|
* @return string html fragment
|
|
*/
|
|
public static function checkbox($name, $value, $checked = true, $label = '',
|
|
array $attributes = null, array $labelattributes = null) {
|
|
$attributes = (array) $attributes;
|
|
$output = '';
|
|
|
|
if ($label !== '' and !is_null($label)) {
|
|
if (empty($attributes['id'])) {
|
|
$attributes['id'] = self::random_id('checkbox_');
|
|
}
|
|
}
|
|
$attributes['type'] = 'checkbox';
|
|
$attributes['value'] = $value;
|
|
$attributes['name'] = $name;
|
|
$attributes['checked'] = $checked ? 'checked' : null;
|
|
|
|
$output .= self::empty_tag('input', $attributes);
|
|
|
|
if ($label !== '' and !is_null($label)) {
|
|
$labelattributes = (array) $labelattributes;
|
|
$labelattributes['for'] = $attributes['id'];
|
|
$output .= self::tag('label', $label, $labelattributes);
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Generates a simple select yes/no form field
|
|
*
|
|
* @param string $name name of select element
|
|
* @param bool $selected
|
|
* @param array $attributes - html select element attributes
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function select_yes_no($name, $selected=true, array $attributes = null) {
|
|
$options = array('1'=>get_string('yes'), '0'=>get_string('no'));
|
|
return self::select($options, $name, $selected, null, $attributes);
|
|
}
|
|
|
|
/**
|
|
* Generates a simple select form field
|
|
*
|
|
* Note this function does HTML escaping on the optgroup labels, but not on the choice labels.
|
|
*
|
|
* @param array $options associative array value=>label ex.:
|
|
* array(1=>'One, 2=>Two)
|
|
* it is also possible to specify optgroup as complex label array ex.:
|
|
* array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
|
|
* array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
|
|
* @param string $name name of select element
|
|
* @param string|array $selected value or array of values depending on multiple attribute
|
|
* @param array|bool|null $nothing add nothing selected option, or false of not added
|
|
* @param array $attributes html select element attributes
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function select(array $options, $name, $selected = '', $nothing = array('' => 'choosedots'), array $attributes = null) {
|
|
$attributes = (array)$attributes;
|
|
if (is_array($nothing)) {
|
|
foreach ($nothing as $k=>$v) {
|
|
if ($v === 'choose' or $v === 'choosedots') {
|
|
$nothing[$k] = get_string('choosedots');
|
|
}
|
|
}
|
|
$options = $nothing + $options; // keep keys, do not override
|
|
|
|
} else if (is_string($nothing) and $nothing !== '') {
|
|
// BC
|
|
$options = array(''=>$nothing) + $options;
|
|
}
|
|
|
|
// we may accept more values if multiple attribute specified
|
|
$selected = (array)$selected;
|
|
foreach ($selected as $k=>$v) {
|
|
$selected[$k] = (string)$v;
|
|
}
|
|
|
|
if (!isset($attributes['id'])) {
|
|
$id = 'menu'.$name;
|
|
// name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
|
|
$id = str_replace('[', '', $id);
|
|
$id = str_replace(']', '', $id);
|
|
$attributes['id'] = $id;
|
|
}
|
|
|
|
if (!isset($attributes['class'])) {
|
|
$class = 'menu'.$name;
|
|
// name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
|
|
$class = str_replace('[', '', $class);
|
|
$class = str_replace(']', '', $class);
|
|
$attributes['class'] = $class;
|
|
}
|
|
$attributes['class'] = 'select custom-select ' . $attributes['class']; // Add 'select' selector always.
|
|
|
|
$attributes['name'] = $name;
|
|
|
|
if (!empty($attributes['disabled'])) {
|
|
$attributes['disabled'] = 'disabled';
|
|
} else {
|
|
unset($attributes['disabled']);
|
|
}
|
|
|
|
$output = '';
|
|
foreach ($options as $value=>$label) {
|
|
if (is_array($label)) {
|
|
// ignore key, it just has to be unique
|
|
$output .= self::select_optgroup(key($label), current($label), $selected);
|
|
} else {
|
|
$output .= self::select_option($label, $value, $selected);
|
|
}
|
|
}
|
|
return self::tag('select', $output, $attributes);
|
|
}
|
|
|
|
/**
|
|
* Returns HTML to display a select box option.
|
|
*
|
|
* @param string $label The label to display as the option.
|
|
* @param string|int $value The value the option represents
|
|
* @param array $selected An array of selected options
|
|
* @return string HTML fragment
|
|
*/
|
|
private static function select_option($label, $value, array $selected) {
|
|
$attributes = array();
|
|
$value = (string)$value;
|
|
if (in_array($value, $selected, true)) {
|
|
$attributes['selected'] = 'selected';
|
|
}
|
|
$attributes['value'] = $value;
|
|
return self::tag('option', $label, $attributes);
|
|
}
|
|
|
|
/**
|
|
* Returns HTML to display a select box option group.
|
|
*
|
|
* @param string $groupname The label to use for the group
|
|
* @param array $options The options in the group
|
|
* @param array $selected An array of selected values.
|
|
* @return string HTML fragment.
|
|
*/
|
|
private static function select_optgroup($groupname, $options, array $selected) {
|
|
if (empty($options)) {
|
|
return '';
|
|
}
|
|
$attributes = array('label'=>$groupname);
|
|
$output = '';
|
|
foreach ($options as $value=>$label) {
|
|
$output .= self::select_option($label, $value, $selected);
|
|
}
|
|
return self::tag('optgroup', $output, $attributes);
|
|
}
|
|
|
|
/**
|
|
* This is a shortcut for making an hour selector menu.
|
|
*
|
|
* @param string $type The type of selector (years, months, days, hours, minutes)
|
|
* @param string $name fieldname
|
|
* @param int $currenttime A default timestamp in GMT
|
|
* @param int $step minute spacing
|
|
* @param array $attributes - html select element attributes
|
|
* @param float|int|string $timezone the timezone to use to calculate the time
|
|
* {@link https://moodledev.io/docs/apis/subsystems/time#timezone}
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function select_time($type, $name, $currenttime = 0, $step = 5, array $attributes = null, $timezone = 99) {
|
|
global $OUTPUT;
|
|
|
|
if (!$currenttime) {
|
|
$currenttime = time();
|
|
}
|
|
$calendartype = \core_calendar\type_factory::get_calendar_instance();
|
|
$currentdate = $calendartype->timestamp_to_date_array($currenttime, $timezone);
|
|
|
|
$userdatetype = $type;
|
|
$timeunits = array();
|
|
|
|
switch ($type) {
|
|
case 'years':
|
|
$timeunits = $calendartype->get_years();
|
|
$userdatetype = 'year';
|
|
break;
|
|
case 'months':
|
|
$timeunits = $calendartype->get_months();
|
|
$userdatetype = 'month';
|
|
$currentdate['month'] = (int)$currentdate['mon'];
|
|
break;
|
|
case 'days':
|
|
$timeunits = $calendartype->get_days();
|
|
$userdatetype = 'mday';
|
|
break;
|
|
case 'hours':
|
|
for ($i=0; $i<=23; $i++) {
|
|
$timeunits[$i] = sprintf("%02d",$i);
|
|
}
|
|
break;
|
|
case 'minutes':
|
|
if ($step != 1) {
|
|
$currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
|
|
}
|
|
|
|
for ($i=0; $i<=59; $i+=$step) {
|
|
$timeunits[$i] = sprintf("%02d",$i);
|
|
}
|
|
break;
|
|
default:
|
|
throw new coding_exception("Time type $type is not supported by html_writer::select_time().");
|
|
}
|
|
|
|
$attributes = (array) $attributes;
|
|
$data = (object) [
|
|
'name' => $name,
|
|
'id' => !empty($attributes['id']) ? $attributes['id'] : self::random_id('ts_'),
|
|
'label' => get_string(substr($type, 0, -1), 'form'),
|
|
'options' => array_map(function($value) use ($timeunits, $currentdate, $userdatetype) {
|
|
return [
|
|
'name' => $timeunits[$value],
|
|
'value' => $value,
|
|
'selected' => $currentdate[$userdatetype] == $value
|
|
];
|
|
}, array_keys($timeunits)),
|
|
];
|
|
|
|
unset($attributes['id']);
|
|
unset($attributes['name']);
|
|
$data->attributes = array_map(function($name) use ($attributes) {
|
|
return [
|
|
'name' => $name,
|
|
'value' => $attributes[$name]
|
|
];
|
|
}, array_keys($attributes));
|
|
|
|
return $OUTPUT->render_from_template('core/select_time', $data);
|
|
}
|
|
|
|
/**
|
|
* Shortcut for quick making of lists
|
|
*
|
|
* Note: 'list' is a reserved keyword ;-)
|
|
*
|
|
* @param array $items
|
|
* @param array $attributes
|
|
* @param string $tag ul or ol
|
|
* @return string
|
|
*/
|
|
public static function alist(array $items, array $attributes = null, $tag = 'ul') {
|
|
$output = html_writer::start_tag($tag, $attributes)."\n";
|
|
foreach ($items as $item) {
|
|
$output .= html_writer::tag('li', $item)."\n";
|
|
}
|
|
$output .= html_writer::end_tag($tag);
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Returns hidden input fields created from url parameters.
|
|
*
|
|
* @param moodle_url $url
|
|
* @param array $exclude list of excluded parameters
|
|
* @return string HTML fragment
|
|
*/
|
|
public static function input_hidden_params(moodle_url $url, array $exclude = null) {
|
|
$exclude = (array)$exclude;
|
|
$params = $url->params();
|
|
foreach ($exclude as $key) {
|
|
unset($params[$key]);
|
|
}
|
|
|
|
$output = '';
|
|
foreach ($params as $key => $value) {
|
|
$attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
|
|
$output .= self::empty_tag('input', $attributes)."\n";
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Generate a script tag containing the the specified code.
|
|
*
|
|
* @param string $jscode the JavaScript code
|
|
* @param moodle_url|string $url optional url of the external script, $code ignored if specified
|
|
* @return string HTML, the code wrapped in <script> tags.
|
|
*/
|
|
public static function script($jscode, $url=null) {
|
|
if ($jscode) {
|
|
return self::tag('script', "\n//<![CDATA[\n$jscode\n//]]>\n") . "\n";
|
|
|
|
} else if ($url) {
|
|
return self::tag('script', '', ['src' => $url]) . "\n";
|
|
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders HTML table
|
|
*
|
|
* This method may modify the passed instance by adding some default properties if they are not set yet.
|
|
* If this is not what you want, you should make a full clone of your data before passing them to this
|
|
* method. In most cases this is not an issue at all so we do not clone by default for performance
|
|
* and memory consumption reasons.
|
|
*
|
|
* @param html_table $table data to be rendered
|
|
* @return string HTML code
|
|
*/
|
|
public static function table(html_table $table) {
|
|
// prepare table data and populate missing properties with reasonable defaults
|
|
if (!empty($table->align)) {
|
|
foreach ($table->align as $key => $aa) {
|
|
if ($aa) {
|
|
$table->align[$key] = 'text-align:'. fix_align_rtl($aa) .';'; // Fix for RTL languages
|
|
} else {
|
|
$table->align[$key] = null;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($table->size)) {
|
|
foreach ($table->size as $key => $ss) {
|
|
if ($ss) {
|
|
$table->size[$key] = 'width:'. $ss .';';
|
|
} else {
|
|
$table->size[$key] = null;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($table->wrap)) {
|
|
foreach ($table->wrap as $key => $ww) {
|
|
if ($ww) {
|
|
$table->wrap[$key] = 'white-space:nowrap;';
|
|
} else {
|
|
$table->wrap[$key] = '';
|
|
}
|
|
}
|
|
}
|
|
if (!empty($table->head)) {
|
|
foreach ($table->head as $key => $val) {
|
|
if (!isset($table->align[$key])) {
|
|
$table->align[$key] = null;
|
|
}
|
|
if (!isset($table->size[$key])) {
|
|
$table->size[$key] = null;
|
|
}
|
|
if (!isset($table->wrap[$key])) {
|
|
$table->wrap[$key] = null;
|
|
}
|
|
|
|
}
|
|
}
|
|
if (empty($table->attributes['class'])) {
|
|
$table->attributes['class'] = 'generaltable';
|
|
}
|
|
if (!empty($table->tablealign)) {
|
|
$table->attributes['class'] .= ' boxalign' . $table->tablealign;
|
|
}
|
|
|
|
// explicitly assigned properties override those defined via $table->attributes
|
|
$table->attributes['class'] = trim($table->attributes['class']);
|
|
$attributes = array_merge($table->attributes, array(
|
|
'id' => $table->id,
|
|
'width' => $table->width,
|
|
'summary' => $table->summary,
|
|
'cellpadding' => $table->cellpadding,
|
|
'cellspacing' => $table->cellspacing,
|
|
));
|
|
$output = html_writer::start_tag('table', $attributes) . "\n";
|
|
|
|
$countcols = 0;
|
|
|
|
// Output a caption if present.
|
|
if (!empty($table->caption)) {
|
|
$captionattributes = array();
|
|
if ($table->captionhide) {
|
|
$captionattributes['class'] = 'accesshide';
|
|
}
|
|
$output .= html_writer::tag(
|
|
'caption',
|
|
$table->caption,
|
|
$captionattributes
|
|
);
|
|
}
|
|
|
|
if (!empty($table->head)) {
|
|
$countcols = count($table->head);
|
|
|
|
$output .= html_writer::start_tag('thead', array()) . "\n";
|
|
$output .= html_writer::start_tag('tr', array()) . "\n";
|
|
$keys = array_keys($table->head);
|
|
$lastkey = end($keys);
|
|
|
|
foreach ($table->head as $key => $heading) {
|
|
// Convert plain string headings into html_table_cell objects
|
|
if (!($heading instanceof html_table_cell)) {
|
|
$headingtext = $heading;
|
|
$heading = new html_table_cell();
|
|
$heading->text = $headingtext;
|
|
$heading->header = true;
|
|
}
|
|
|
|
if ($heading->header !== false) {
|
|
$heading->header = true;
|
|
}
|
|
|
|
$tagtype = 'td';
|
|
if ($heading->header && (string)$heading->text != '') {
|
|
$tagtype = 'th';
|
|
}
|
|
|
|
$heading->attributes['class'] .= ' header c' . $key;
|
|
if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
|
|
$heading->colspan = $table->headspan[$key];
|
|
$countcols += $table->headspan[$key] - 1;
|
|
}
|
|
|
|
if ($key == $lastkey) {
|
|
$heading->attributes['class'] .= ' lastcol';
|
|
}
|
|
if (isset($table->colclasses[$key])) {
|
|
$heading->attributes['class'] .= ' ' . $table->colclasses[$key];
|
|
}
|
|
$heading->attributes['class'] = trim($heading->attributes['class']);
|
|
$attributes = array_merge($heading->attributes, [
|
|
'style' => $table->align[$key] . $table->size[$key] . $heading->style,
|
|
'colspan' => $heading->colspan,
|
|
]);
|
|
|
|
if ($tagtype == 'th') {
|
|
$attributes['scope'] = !empty($heading->scope) ? $heading->scope : 'col';
|
|
}
|
|
|
|
$output .= html_writer::tag($tagtype, $heading->text, $attributes) . "\n";
|
|
}
|
|
$output .= html_writer::end_tag('tr') . "\n";
|
|
$output .= html_writer::end_tag('thead') . "\n";
|
|
|
|
if (empty($table->data)) {
|
|
// For valid XHTML strict every table must contain either a valid tr
|
|
// or a valid tbody... both of which must contain a valid td
|
|
$output .= html_writer::start_tag('tbody', array('class' => 'empty'));
|
|
$output .= html_writer::tag('tr', html_writer::tag('td', '', array('colspan'=>count($table->head))));
|
|
$output .= html_writer::end_tag('tbody');
|
|
}
|
|
}
|
|
|
|
if (!empty($table->data)) {
|
|
$keys = array_keys($table->data);
|
|
$lastrowkey = end($keys);
|
|
$output .= html_writer::start_tag('tbody', array());
|
|
|
|
foreach ($table->data as $key => $row) {
|
|
if (($row === 'hr') && ($countcols)) {
|
|
$output .= html_writer::tag('td', html_writer::tag('div', '', array('class' => 'tabledivider')), array('colspan' => $countcols));
|
|
} else {
|
|
// Convert array rows to html_table_rows and cell strings to html_table_cell objects
|
|
if (!($row instanceof html_table_row)) {
|
|
$newrow = new html_table_row();
|
|
|
|
foreach ($row as $cell) {
|
|
if (!($cell instanceof html_table_cell)) {
|
|
$cell = new html_table_cell($cell);
|
|
}
|
|
$newrow->cells[] = $cell;
|
|
}
|
|
$row = $newrow;
|
|
}
|
|
|
|
if (isset($table->rowclasses[$key])) {
|
|
$row->attributes['class'] .= ' ' . $table->rowclasses[$key];
|
|
}
|
|
|
|
if ($key == $lastrowkey) {
|
|
$row->attributes['class'] .= ' lastrow';
|
|
}
|
|
|
|
// Explicitly assigned properties should override those defined in the attributes.
|
|
$row->attributes['class'] = trim($row->attributes['class']);
|
|
$trattributes = array_merge($row->attributes, array(
|
|
'id' => $row->id,
|
|
'style' => $row->style,
|
|
));
|
|
$output .= html_writer::start_tag('tr', $trattributes) . "\n";
|
|
$keys2 = array_keys($row->cells);
|
|
$lastkey = end($keys2);
|
|
|
|
$gotlastkey = false; //flag for sanity checking
|
|
foreach ($row->cells as $key => $cell) {
|
|
if ($gotlastkey) {
|
|
//This should never happen. Why do we have a cell after the last cell?
|
|
mtrace("A cell with key ($key) was found after the last key ($lastkey)");
|
|
}
|
|
|
|
if (!($cell instanceof html_table_cell)) {
|
|
$mycell = new html_table_cell();
|
|
$mycell->text = $cell;
|
|
$cell = $mycell;
|
|
}
|
|
|
|
if (($cell->header === true) && empty($cell->scope)) {
|
|
$cell->scope = 'row';
|
|
}
|
|
|
|
if (isset($table->colclasses[$key])) {
|
|
$cell->attributes['class'] .= ' ' . $table->colclasses[$key];
|
|
}
|
|
|
|
$cell->attributes['class'] .= ' cell c' . $key;
|
|
if ($key == $lastkey) {
|
|
$cell->attributes['class'] .= ' lastcol';
|
|
$gotlastkey = true;
|
|
}
|
|
$tdstyle = '';
|
|
$tdstyle .= isset($table->align[$key]) ? $table->align[$key] : '';
|
|
$tdstyle .= isset($table->size[$key]) ? $table->size[$key] : '';
|
|
$tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : '';
|
|
$cell->attributes['class'] = trim($cell->attributes['class']);
|
|
$tdattributes = array_merge($cell->attributes, array(
|
|
'style' => $tdstyle . $cell->style,
|
|
'colspan' => $cell->colspan,
|
|
'rowspan' => $cell->rowspan,
|
|
'id' => $cell->id,
|
|
'abbr' => $cell->abbr,
|
|
'scope' => $cell->scope,
|
|
));
|
|
$tagtype = 'td';
|
|
if ($cell->header === true) {
|
|
$tagtype = 'th';
|
|
}
|
|
$output .= html_writer::tag($tagtype, $cell->text, $tdattributes) . "\n";
|
|
}
|
|
}
|
|
$output .= html_writer::end_tag('tr') . "\n";
|
|
}
|
|
$output .= html_writer::end_tag('tbody') . "\n";
|
|
}
|
|
$output .= html_writer::end_tag('table') . "\n";
|
|
|
|
if ($table->responsive) {
|
|
return self::div($output, 'table-responsive');
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Renders form element label
|
|
*
|
|
* By default, the label is suffixed with a label separator defined in the
|
|
* current language pack (colon by default in the English lang pack).
|
|
* Adding the colon can be explicitly disabled if needed. Label separators
|
|
* are put outside the label tag itself so they are not read by
|
|
* screenreaders (accessibility).
|
|
*
|
|
* Parameter $for explicitly associates the label with a form control. When
|
|
* set, the value of this attribute must be the same as the value of
|
|
* the id attribute of the form control in the same document. When null,
|
|
* the label being defined is associated with the control inside the label
|
|
* element.
|
|
*
|
|
* @param string $text content of the label tag
|
|
* @param string|null $for id of the element this label is associated with, null for no association
|
|
* @param bool $colonize add label separator (colon) to the label text, if it is not there yet
|
|
* @param array $attributes to be inserted in the tab, for example array('accesskey' => 'a')
|
|
* @return string HTML of the label element
|
|
*/
|
|
public static function label($text, $for, $colonize = true, array $attributes=array()) {
|
|
if (!is_null($for)) {
|
|
$attributes = array_merge($attributes, array('for' => $for));
|
|
}
|
|
$text = trim($text ?? '');
|
|
$label = self::tag('label', $text, $attributes);
|
|
|
|
// TODO MDL-12192 $colonize disabled for now yet
|
|
// if (!empty($text) and $colonize) {
|
|
// // the $text may end with the colon already, though it is bad string definition style
|
|
// $colon = get_string('labelsep', 'langconfig');
|
|
// if (!empty($colon)) {
|
|
// $trimmed = trim($colon);
|
|
// if ((substr($text, -strlen($trimmed)) == $trimmed) or (substr($text, -1) == ':')) {
|
|
// //debugging('The label text should not end with colon or other label separator,
|
|
// // please fix the string definition.', DEBUG_DEVELOPER);
|
|
// } else {
|
|
// $label .= $colon;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
return $label;
|
|
}
|
|
|
|
/**
|
|
* Combines a class parameter with other attributes. Aids in code reduction
|
|
* because the class parameter is very frequently used.
|
|
*
|
|
* If the class attribute is specified both in the attributes and in the
|
|
* class parameter, the two values are combined with a space between.
|
|
*
|
|
* @param string $class Optional CSS class (or classes as space-separated list)
|
|
* @param array $attributes Optional other attributes as array
|
|
* @return array Attributes (or null if still none)
|
|
*/
|
|
private static function add_class($class = '', array $attributes = null) {
|
|
if ($class !== '') {
|
|
$classattribute = array('class' => $class);
|
|
if ($attributes) {
|
|
if (array_key_exists('class', $attributes)) {
|
|
$attributes['class'] = trim($attributes['class'] . ' ' . $class);
|
|
} else {
|
|
$attributes = $classattribute + $attributes;
|
|
}
|
|
} else {
|
|
$attributes = $classattribute;
|
|
}
|
|
}
|
|
return $attributes;
|
|
}
|
|
|
|
/**
|
|
* Creates a <div> tag. (Shortcut function.)
|
|
*
|
|
* @param string $content HTML content of tag
|
|
* @param string $class Optional CSS class (or classes as space-separated list)
|
|
* @param array $attributes Optional other attributes as array
|
|
* @return string HTML code for div
|
|
*/
|
|
public static function div($content, $class = '', array $attributes = null) {
|
|
return self::tag('div', $content, self::add_class($class, $attributes));
|
|
}
|
|
|
|
/**
|
|
* Starts a <div> tag. (Shortcut function.)
|
|
*
|
|
* @param string $class Optional CSS class (or classes as space-separated list)
|
|
* @param array $attributes Optional other attributes as array
|
|
* @return string HTML code for open div tag
|
|
*/
|
|
public static function start_div($class = '', array $attributes = null) {
|
|
return self::start_tag('div', self::add_class($class, $attributes));
|
|
}
|
|
|
|
/**
|
|
* Ends a <div> tag. (Shortcut function.)
|
|
*
|
|
* @return string HTML code for close div tag
|
|
*/
|
|
public static function end_div() {
|
|
return self::end_tag('div');
|
|
}
|
|
|
|
/**
|
|
* Creates a <span> tag. (Shortcut function.)
|
|
*
|
|
* @param string $content HTML content of tag
|
|
* @param string $class Optional CSS class (or classes as space-separated list)
|
|
* @param array $attributes Optional other attributes as array
|
|
* @return string HTML code for span
|
|
*/
|
|
public static function span($content, $class = '', array $attributes = null) {
|
|
return self::tag('span', $content, self::add_class($class, $attributes));
|
|
}
|
|
|
|
/**
|
|
* Starts a <span> tag. (Shortcut function.)
|
|
*
|
|
* @param string $class Optional CSS class (or classes as space-separated list)
|
|
* @param array $attributes Optional other attributes as array
|
|
* @return string HTML code for open span tag
|
|
*/
|
|
public static function start_span($class = '', array $attributes = null) {
|
|
return self::start_tag('span', self::add_class($class, $attributes));
|
|
}
|
|
|
|
/**
|
|
* Ends a <span> tag. (Shortcut function.)
|
|
*
|
|
* @return string HTML code for close span tag
|
|
*/
|
|
public static function end_span() {
|
|
return self::end_tag('span');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple javascript output class
|
|
*
|
|
* @copyright 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class js_writer {
|
|
|
|
/**
|
|
* Returns javascript code calling the function
|
|
*
|
|
* @param string $function function name, can be complex like Y.Event.purgeElement
|
|
* @param array $arguments parameters
|
|
* @param int $delay execution delay in seconds
|
|
* @return string JS code fragment
|
|
*/
|
|
public static function function_call($function, array $arguments = null, $delay=0) {
|
|
if ($arguments) {
|
|
$arguments = array_map('json_encode', convert_to_array($arguments));
|
|
$arguments = implode(', ', $arguments);
|
|
} else {
|
|
$arguments = '';
|
|
}
|
|
$js = "$function($arguments);";
|
|
|
|
if ($delay) {
|
|
$delay = $delay * 1000; // in miliseconds
|
|
$js = "setTimeout(function() { $js }, $delay);";
|
|
}
|
|
return $js . "\n";
|
|
}
|
|
|
|
/**
|
|
* Special function which adds Y as first argument of function call.
|
|
*
|
|
* @param string $function The function to call
|
|
* @param array $extraarguments Any arguments to pass to it
|
|
* @return string Some JS code
|
|
*/
|
|
public static function function_call_with_Y($function, array $extraarguments = null) {
|
|
if ($extraarguments) {
|
|
$extraarguments = array_map('json_encode', convert_to_array($extraarguments));
|
|
$arguments = 'Y, ' . implode(', ', $extraarguments);
|
|
} else {
|
|
$arguments = 'Y';
|
|
}
|
|
return "$function($arguments);\n";
|
|
}
|
|
|
|
/**
|
|
* Returns JavaScript code to initialise a new object
|
|
*
|
|
* @param string $var If it is null then no var is assigned the new object.
|
|
* @param string $class The class to initialise an object for.
|
|
* @param array $arguments An array of args to pass to the init method.
|
|
* @param array $requirements Any modules required for this class.
|
|
* @param int $delay The delay before initialisation. 0 = no delay.
|
|
* @return string Some JS code
|
|
*/
|
|
public static function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) {
|
|
if (is_array($arguments)) {
|
|
$arguments = array_map('json_encode', convert_to_array($arguments));
|
|
$arguments = implode(', ', $arguments);
|
|
}
|
|
|
|
if ($var === null) {
|
|
$js = "new $class(Y, $arguments);";
|
|
} else if (strpos($var, '.')!==false) {
|
|
$js = "$var = new $class(Y, $arguments);";
|
|
} else {
|
|
$js = "var $var = new $class(Y, $arguments);";
|
|
}
|
|
|
|
if ($delay) {
|
|
$delay = $delay * 1000; // in miliseconds
|
|
$js = "setTimeout(function() { $js }, $delay);";
|
|
}
|
|
|
|
if (count($requirements) > 0) {
|
|
$requirements = implode("', '", $requirements);
|
|
$js = "Y.use('$requirements', function(Y){ $js });";
|
|
}
|
|
return $js."\n";
|
|
}
|
|
|
|
/**
|
|
* Returns code setting value to variable
|
|
*
|
|
* @param string $name
|
|
* @param mixed $value json serialised value
|
|
* @param bool $usevar add var definition, ignored for nested properties
|
|
* @return string JS code fragment
|
|
*/
|
|
public static function set_variable($name, $value, $usevar = true) {
|
|
$output = '';
|
|
|
|
if ($usevar) {
|
|
if (strpos($name, '.')) {
|
|
$output .= '';
|
|
} else {
|
|
$output .= 'var ';
|
|
}
|
|
}
|
|
|
|
$output .= "$name = ".json_encode($value).";";
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Writes event handler attaching code
|
|
*
|
|
* @param array|string $selector standard YUI selector for elements, may be
|
|
* array or string, element id is in the form "#idvalue"
|
|
* @param string $event A valid DOM event (click, mousedown, change etc.)
|
|
* @param string $function The name of the function to call
|
|
* @param array $arguments An optional array of argument parameters to pass to the function
|
|
* @return string JS code fragment
|
|
*/
|
|
public static function event_handler($selector, $event, $function, array $arguments = null) {
|
|
$selector = json_encode($selector);
|
|
$output = "Y.on('$event', $function, $selector, null";
|
|
if (!empty($arguments)) {
|
|
$output .= ', ' . json_encode($arguments);
|
|
}
|
|
return $output . ");\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds all the information required to render a <table> by {@link core_renderer::table()}
|
|
*
|
|
* Example of usage:
|
|
* $t = new html_table();
|
|
* ... // set various properties of the object $t as described below
|
|
* echo html_writer::table($t);
|
|
*
|
|
* @copyright 2009 David Mudrak <david.mudrak@gmail.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class html_table {
|
|
|
|
/**
|
|
* @var string Value to use for the id attribute of the table
|
|
*/
|
|
public $id = null;
|
|
|
|
/**
|
|
* @var array Attributes of HTML attributes for the <table> element
|
|
*/
|
|
public $attributes = array();
|
|
|
|
/**
|
|
* @var array An array of headings. The n-th array item is used as a heading of the n-th column.
|
|
* For more control over the rendering of the headers, an array of html_table_cell objects
|
|
* can be passed instead of an array of strings.
|
|
*
|
|
* Example of usage:
|
|
* $t->head = array('Student', 'Grade');
|
|
*/
|
|
public $head;
|
|
|
|
/**
|
|
* @var array An array that can be used to make a heading span multiple columns.
|
|
* In this example, {@link html_table:$data} is supposed to have three columns. For the first two columns,
|
|
* the same heading is used. Therefore, {@link html_table::$head} should consist of two items.
|
|
*
|
|
* Example of usage:
|
|
* $t->headspan = array(2,1);
|
|
*/
|
|
public $headspan;
|
|
|
|
/**
|
|
* @var array An array of column alignments.
|
|
* The value is used as CSS 'text-align' property. Therefore, possible
|
|
* values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
|
|
* of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
|
|
*
|
|
* Examples of usage:
|
|
* $t->align = array(null, 'right');
|
|
* or
|
|
* $t->align[1] = 'right';
|
|
*/
|
|
public $align;
|
|
|
|
/**
|
|
* @var array The value is used as CSS 'size' property.
|
|
*
|
|
* Examples of usage:
|
|
* $t->size = array('50%', '50%');
|
|
* or
|
|
* $t->size[1] = '120px';
|
|
*/
|
|
public $size;
|
|
|
|
/**
|
|
* @var array An array of wrapping information.
|
|
* The only possible value is 'nowrap' that sets the
|
|
* CSS property 'white-space' to the value 'nowrap' in the given column.
|
|
*
|
|
* Example of usage:
|
|
* $t->wrap = array(null, 'nowrap');
|
|
*/
|
|
public $wrap;
|
|
|
|
/**
|
|
* @var array Array of arrays or html_table_row objects containing the data. Alternatively, if you have
|
|
* $head specified, the string 'hr' (for horizontal ruler) can be used
|
|
* instead of an array of cells data resulting in a divider rendered.
|
|
*
|
|
* Example of usage with array of arrays:
|
|
* $row1 = array('Harry Potter', '76 %');
|
|
* $row2 = array('Hermione Granger', '100 %');
|
|
* $t->data = array($row1, $row2);
|
|
*
|
|
* Example with array of html_table_row objects: (used for more fine-grained control)
|
|
* $cell1 = new html_table_cell();
|
|
* $cell1->text = 'Harry Potter';
|
|
* $cell1->colspan = 2;
|
|
* $row1 = new html_table_row();
|
|
* $row1->cells[] = $cell1;
|
|
* $cell2 = new html_table_cell();
|
|
* $cell2->text = 'Hermione Granger';
|
|
* $cell3 = new html_table_cell();
|
|
* $cell3->text = '100 %';
|
|
* $row2 = new html_table_row();
|
|
* $row2->cells = array($cell2, $cell3);
|
|
* $t->data = array($row1, $row2);
|
|
*/
|
|
public $data = [];
|
|
|
|
/**
|
|
* @deprecated since Moodle 2.0. Styling should be in the CSS.
|
|
* @var string Width of the table, percentage of the page preferred.
|
|
*/
|
|
public $width = null;
|
|
|
|
/**
|
|
* @deprecated since Moodle 2.0. Styling should be in the CSS.
|
|
* @var string Alignment for the whole table. Can be 'right', 'left' or 'center' (default).
|
|
*/
|
|
public $tablealign = null;
|
|
|
|
/**
|
|
* @deprecated since Moodle 2.0. Styling should be in the CSS.
|
|
* @var int Padding on each cell, in pixels
|
|
*/
|
|
public $cellpadding = null;
|
|
|
|
/**
|
|
* @var int Spacing between cells, in pixels
|
|
* @deprecated since Moodle 2.0. Styling should be in the CSS.
|
|
*/
|
|
public $cellspacing = null;
|
|
|
|
/**
|
|
* @var array Array of classes to add to particular rows, space-separated string.
|
|
* Class 'lastrow' is added automatically for the last row in the table.
|
|
*
|
|
* Example of usage:
|
|
* $t->rowclasses[9] = 'tenth'
|
|
*/
|
|
public $rowclasses;
|
|
|
|
/**
|
|
* @var array An array of classes to add to every cell in a particular column,
|
|
* space-separated string. Class 'cell' is added automatically by the renderer.
|
|
* Classes 'c0' or 'c1' are added automatically for every odd or even column,
|
|
* respectively. Class 'lastcol' is added automatically for all last cells
|
|
* in a row.
|
|
*
|
|
* Example of usage:
|
|
* $t->colclasses = array(null, 'grade');
|
|
*/
|
|
public $colclasses;
|
|
|
|
/**
|
|
* @var string Description of the contents for screen readers.
|
|
*
|
|
* The "summary" attribute on the "table" element is not supported in HTML5.
|
|
* Consider describing the structure of the table in a "caption" element or in a "figure" element containing the table;
|
|
* or, simplify the structure of the table so that no description is needed.
|
|
*
|
|
* @deprecated since Moodle 3.9.
|
|
*/
|
|
public $summary;
|
|
|
|
/**
|
|
* @var string Caption for the table, typically a title.
|
|
*
|
|
* Example of usage:
|
|
* $t->caption = "TV Guide";
|
|
*/
|
|
public $caption;
|
|
|
|
/**
|
|
* @var bool Whether to hide the table's caption from sighted users.
|
|
*
|
|
* Example of usage:
|
|
* $t->caption = "TV Guide";
|
|
* $t->captionhide = true;
|
|
*/
|
|
public $captionhide = false;
|
|
|
|
/** @var bool Whether to make the table to be scrolled horizontally with ease. Make table responsive across all viewports. */
|
|
public $responsive = true;
|
|
|
|
/** @var string class name to add to this html table. */
|
|
public $class;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
$this->attributes['class'] = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Component representing a table row.
|
|
*
|
|
* @copyright 2009 Nicolas Connault
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class html_table_row {
|
|
|
|
/**
|
|
* @var string Value to use for the id attribute of the row.
|
|
*/
|
|
public $id = null;
|
|
|
|
/**
|
|
* @var array Array of html_table_cell objects
|
|
*/
|
|
public $cells = array();
|
|
|
|
/**
|
|
* @var string Value to use for the style attribute of the table row
|
|
*/
|
|
public $style = null;
|
|
|
|
/**
|
|
* @var array Attributes of additional HTML attributes for the <tr> element
|
|
*/
|
|
public $attributes = array();
|
|
|
|
/**
|
|
* Constructor
|
|
* @param array $cells
|
|
*/
|
|
public function __construct(array $cells=null) {
|
|
$this->attributes['class'] = '';
|
|
$cells = (array)$cells;
|
|
foreach ($cells as $cell) {
|
|
if ($cell instanceof html_table_cell) {
|
|
$this->cells[] = $cell;
|
|
} else {
|
|
$this->cells[] = new html_table_cell($cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Component representing a table cell.
|
|
*
|
|
* @copyright 2009 Nicolas Connault
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class html_table_cell {
|
|
|
|
/**
|
|
* @var string Value to use for the id attribute of the cell.
|
|
*/
|
|
public $id = null;
|
|
|
|
/**
|
|
* @var string The contents of the cell.
|
|
*/
|
|
public $text;
|
|
|
|
/**
|
|
* @var string Abbreviated version of the contents of the cell.
|
|
*/
|
|
public $abbr = null;
|
|
|
|
/**
|
|
* @var int Number of columns this cell should span.
|
|
*/
|
|
public $colspan = null;
|
|
|
|
/**
|
|
* @var int Number of rows this cell should span.
|
|
*/
|
|
public $rowspan = null;
|
|
|
|
/**
|
|
* @var string Defines a way to associate header cells and data cells in a table.
|
|
*/
|
|
public $scope = null;
|
|
|
|
/**
|
|
* @var bool Whether or not this cell is a header cell.
|
|
*/
|
|
public $header = null;
|
|
|
|
/**
|
|
* @var string Value to use for the style attribute of the table cell
|
|
*/
|
|
public $style = null;
|
|
|
|
/**
|
|
* @var array Attributes of additional HTML attributes for the <td> element
|
|
*/
|
|
public $attributes = array();
|
|
|
|
/**
|
|
* Constructs a table cell
|
|
*
|
|
* @param string $text
|
|
*/
|
|
public function __construct($text = null) {
|
|
$this->text = $text;
|
|
$this->attributes['class'] = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Component representing a paging bar.
|
|
*
|
|
* @copyright 2009 Nicolas Connault
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class paging_bar implements renderable, templatable {
|
|
|
|
/**
|
|
* @var int The maximum number of pagelinks to display.
|
|
*/
|
|
public $maxdisplay = 18;
|
|
|
|
/**
|
|
* @var int The total number of entries to be pages through..
|
|
*/
|
|
public $totalcount;
|
|
|
|
/**
|
|
* @var int The page you are currently viewing.
|
|
*/
|
|
public $page;
|
|
|
|
/**
|
|
* @var int The number of entries that should be shown per page.
|
|
*/
|
|
public $perpage;
|
|
|
|
/**
|
|
* @var string|moodle_url If this is a string then it is the url which will be appended with $pagevar,
|
|
* an equals sign and the page number.
|
|
* If this is a moodle_url object then the pagevar param will be replaced by
|
|
* the page no, for each page.
|
|
*/
|
|
public $baseurl;
|
|
|
|
/**
|
|
* @var string This is the variable name that you use for the pagenumber in your
|
|
* code (ie. 'tablepage', 'blogpage', etc)
|
|
*/
|
|
public $pagevar;
|
|
|
|
/**
|
|
* @var string A HTML link representing the "previous" page.
|
|
*/
|
|
public $previouslink = null;
|
|
|
|
/**
|
|
* @var string A HTML link representing the "next" page.
|
|
*/
|
|
public $nextlink = null;
|
|
|
|
/**
|
|
* @var string A HTML link representing the first page.
|
|
*/
|
|
public $firstlink = null;
|
|
|
|
/**
|
|
* @var string A HTML link representing the last page.
|
|
*/
|
|
public $lastlink = null;
|
|
|
|
/**
|
|
* @var array An array of strings. One of them is just a string: the current page
|
|
*/
|
|
public $pagelinks = array();
|
|
|
|
/**
|
|
* Constructor paging_bar with only the required params.
|
|
*
|
|
* @param int $totalcount The total number of entries available to be paged through
|
|
* @param int $page The page you are currently viewing
|
|
* @param int $perpage The number of entries that should be shown per page
|
|
* @param string|moodle_url $baseurl url of the current page, the $pagevar parameter is added
|
|
* @param string $pagevar name of page parameter that holds the page number
|
|
*/
|
|
public function __construct($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') {
|
|
$this->totalcount = $totalcount;
|
|
$this->page = $page;
|
|
$this->perpage = $perpage;
|
|
$this->baseurl = $baseurl;
|
|
$this->pagevar = $pagevar;
|
|
}
|
|
|
|
/**
|
|
* Prepares the paging bar for output.
|
|
*
|
|
* This method validates the arguments set up for the paging bar and then
|
|
* produces fragments of HTML to assist display later on.
|
|
*
|
|
* @param renderer_base $output
|
|
* @param moodle_page $page
|
|
* @param string $target
|
|
* @throws coding_exception
|
|
*/
|
|
public function prepare(renderer_base $output, moodle_page $page, $target) {
|
|
if (!isset($this->totalcount) || is_null($this->totalcount)) {
|
|
throw new coding_exception('paging_bar requires a totalcount value.');
|
|
}
|
|
if (!isset($this->page) || is_null($this->page)) {
|
|
throw new coding_exception('paging_bar requires a page value.');
|
|
}
|
|
if (empty($this->perpage)) {
|
|
throw new coding_exception('paging_bar requires a perpage value.');
|
|
}
|
|
if (empty($this->baseurl)) {
|
|
throw new coding_exception('paging_bar requires a baseurl value.');
|
|
}
|
|
|
|
if ($this->totalcount > $this->perpage) {
|
|
$pagenum = $this->page - 1;
|
|
|
|
if ($this->page > 0) {
|
|
$this->previouslink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('previous'), array('class'=>'previous'));
|
|
}
|
|
|
|
if ($this->perpage > 0) {
|
|
$lastpage = ceil($this->totalcount / $this->perpage);
|
|
} else {
|
|
$lastpage = 1;
|
|
}
|
|
|
|
if ($this->page > round(($this->maxdisplay/3)*2)) {
|
|
$currpage = $this->page - round($this->maxdisplay/3);
|
|
|
|
$this->firstlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>0)), '1', array('class'=>'first'));
|
|
} else {
|
|
$currpage = 0;
|
|
}
|
|
|
|
$displaycount = $displaypage = 0;
|
|
|
|
while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
|
|
$displaypage = $currpage + 1;
|
|
|
|
if ($this->page == $currpage) {
|
|
$this->pagelinks[] = html_writer::span($displaypage, 'current-page');
|
|
} else {
|
|
$pagelink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$currpage)), $displaypage);
|
|
$this->pagelinks[] = $pagelink;
|
|
}
|
|
|
|
$displaycount++;
|
|
$currpage++;
|
|
}
|
|
|
|
if ($currpage < $lastpage) {
|
|
$lastpageactual = $lastpage - 1;
|
|
$this->lastlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$lastpageactual)), $lastpage, array('class'=>'last'));
|
|
}
|
|
|
|
$pagenum = $this->page + 1;
|
|
|
|
if ($pagenum != $lastpage) {
|
|
$this->nextlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('next'), array('class'=>'next'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$data = new stdClass();
|
|
$data->previous = null;
|
|
$data->next = null;
|
|
$data->first = null;
|
|
$data->last = null;
|
|
$data->label = get_string('page');
|
|
$data->pages = [];
|
|
$data->haspages = $this->totalcount > $this->perpage;
|
|
$data->pagesize = $this->perpage;
|
|
|
|
if (!$data->haspages) {
|
|
return $data;
|
|
}
|
|
|
|
if ($this->page > 0) {
|
|
$data->previous = [
|
|
'page' => $this->page,
|
|
'url' => (new moodle_url($this->baseurl, [$this->pagevar => $this->page - 1]))->out(false)
|
|
];
|
|
}
|
|
|
|
$currpage = 0;
|
|
if ($this->page > round(($this->maxdisplay / 3) * 2)) {
|
|
$currpage = $this->page - round($this->maxdisplay / 3);
|
|
$data->first = [
|
|
'page' => 1,
|
|
'url' => (new moodle_url($this->baseurl, [$this->pagevar => 0]))->out(false)
|
|
];
|
|
}
|
|
|
|
$lastpage = 1;
|
|
if ($this->perpage > 0) {
|
|
$lastpage = ceil($this->totalcount / $this->perpage);
|
|
}
|
|
|
|
$displaycount = 0;
|
|
$displaypage = 0;
|
|
while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
|
|
$displaypage = $currpage + 1;
|
|
|
|
$iscurrent = $this->page == $currpage;
|
|
$link = new moodle_url($this->baseurl, [$this->pagevar => $currpage]);
|
|
|
|
$data->pages[] = [
|
|
'page' => $displaypage,
|
|
'active' => $iscurrent,
|
|
'url' => $iscurrent ? null : $link->out(false)
|
|
];
|
|
|
|
$displaycount++;
|
|
$currpage++;
|
|
}
|
|
|
|
if ($currpage < $lastpage) {
|
|
$data->last = [
|
|
'page' => $lastpage,
|
|
'url' => (new moodle_url($this->baseurl, [$this->pagevar => $lastpage - 1]))->out(false)
|
|
];
|
|
}
|
|
|
|
if ($this->page + 1 != $lastpage) {
|
|
$data->next = [
|
|
'page' => $this->page + 2,
|
|
'url' => (new moodle_url($this->baseurl, [$this->pagevar => $this->page + 1]))->out(false)
|
|
];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Component representing initials bar.
|
|
*
|
|
* @copyright 2017 Ilya Tregubov
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 3.3
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class initials_bar implements renderable, templatable {
|
|
|
|
/**
|
|
* @var string Currently selected letter.
|
|
*/
|
|
public $current;
|
|
|
|
/**
|
|
* @var string Class name to add to this initial bar.
|
|
*/
|
|
public $class;
|
|
|
|
/**
|
|
* @var string The name to put in front of this initial bar.
|
|
*/
|
|
public $title;
|
|
|
|
/**
|
|
* @var string URL parameter name for this initial.
|
|
*/
|
|
public $urlvar;
|
|
|
|
/**
|
|
* @var moodle_url URL object.
|
|
*/
|
|
public $url;
|
|
|
|
/**
|
|
* @var array An array of letters in the alphabet.
|
|
*/
|
|
public $alpha;
|
|
|
|
/**
|
|
* @var bool Omit links if we are doing a mini render.
|
|
*/
|
|
public $minirender;
|
|
|
|
/**
|
|
* Constructor initials_bar with only the required params.
|
|
*
|
|
* @param string $current the currently selected letter.
|
|
* @param string $class class name to add to this initial bar.
|
|
* @param string $title the name to put in front of this initial bar.
|
|
* @param string $urlvar URL parameter name for this initial.
|
|
* @param string $url URL object.
|
|
* @param array $alpha of letters in the alphabet.
|
|
* @param bool $minirender Return a trimmed down view of the initials bar.
|
|
*/
|
|
public function __construct($current, $class, $title, $urlvar, $url, $alpha = null, bool $minirender = false) {
|
|
$this->current = $current;
|
|
$this->class = $class;
|
|
$this->title = $title;
|
|
$this->urlvar = $urlvar;
|
|
$this->url = $url;
|
|
$this->alpha = $alpha;
|
|
$this->minirender = $minirender;
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$data = new stdClass();
|
|
|
|
if ($this->alpha == null) {
|
|
$this->alpha = explode(',', get_string('alphabet', 'langconfig'));
|
|
}
|
|
|
|
if ($this->current == 'all') {
|
|
$this->current = '';
|
|
}
|
|
|
|
// We want to find a letter grouping size which suits the language so
|
|
// find the largest group size which is less than 15 chars.
|
|
// The choice of 15 chars is the largest number of chars that reasonably
|
|
// fits on the smallest supported screen size. By always using a max number
|
|
// of groups which is a factor of 2, we always get nice wrapping, and the
|
|
// last row is always the shortest.
|
|
$groupsize = count($this->alpha);
|
|
$groups = 1;
|
|
while ($groupsize > 15) {
|
|
$groups *= 2;
|
|
$groupsize = ceil(count($this->alpha) / $groups);
|
|
}
|
|
|
|
$groupsizelimit = 0;
|
|
$groupnumber = 0;
|
|
foreach ($this->alpha as $letter) {
|
|
if ($groupsizelimit++ > 0 && $groupsizelimit % $groupsize == 1) {
|
|
$groupnumber++;
|
|
}
|
|
$groupletter = new stdClass();
|
|
$groupletter->name = $letter;
|
|
if (!$this->minirender) {
|
|
$groupletter->url = $this->url->out(false, array($this->urlvar => $letter));
|
|
} else {
|
|
$groupletter->input = $letter;
|
|
}
|
|
if ($letter == $this->current) {
|
|
$groupletter->selected = $this->current;
|
|
}
|
|
if (!isset($data->group[$groupnumber])) {
|
|
$data->group[$groupnumber] = new stdClass();
|
|
}
|
|
$data->group[$groupnumber]->letter[] = $groupletter;
|
|
}
|
|
|
|
$data->class = $this->class;
|
|
$data->title = $this->title;
|
|
if (!$this->minirender) {
|
|
$data->url = $this->url->out(false, array($this->urlvar => ''));
|
|
} else {
|
|
$data->input = 'ALL';
|
|
}
|
|
$data->current = $this->current;
|
|
$data->all = get_string('all');
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents how a block appears on a page.
|
|
*
|
|
* During output, each block instance is asked to return a block_contents object,
|
|
* those are then passed to the $OUTPUT->block function for display.
|
|
*
|
|
* contents should probably be generated using a moodle_block_..._renderer.
|
|
*
|
|
* Other block-like things that need to appear on the page, for example the
|
|
* add new block UI, are also represented as block_contents objects.
|
|
*
|
|
* @copyright 2009 Tim Hunt
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class block_contents {
|
|
|
|
/** Used when the block cannot be collapsed **/
|
|
const NOT_HIDEABLE = 0;
|
|
|
|
/** Used when the block can be collapsed but currently is not **/
|
|
const VISIBLE = 1;
|
|
|
|
/** Used when the block has been collapsed **/
|
|
const HIDDEN = 2;
|
|
|
|
/**
|
|
* @var int Used to set $skipid.
|
|
*/
|
|
protected static $idcounter = 1;
|
|
|
|
/**
|
|
* @var int All the blocks (or things that look like blocks) printed on
|
|
* a page are given a unique number that can be used to construct id="" attributes.
|
|
* This is set automatically be the {@link prepare()} method.
|
|
* Do not try to set it manually.
|
|
*/
|
|
public $skipid;
|
|
|
|
/**
|
|
* @var int If this is the contents of a real block, this should be set
|
|
* to the block_instance.id. Otherwise this should be set to 0.
|
|
*/
|
|
public $blockinstanceid = 0;
|
|
|
|
/**
|
|
* @var int If this is a real block instance, and there is a corresponding
|
|
* block_position.id for the block on this page, this should be set to that id.
|
|
* Otherwise it should be 0.
|
|
*/
|
|
public $blockpositionid = 0;
|
|
|
|
/**
|
|
* @var array An array of attribute => value pairs that are put on the outer div of this
|
|
* block. {@link $id} and {@link $classes} attributes should be set separately.
|
|
*/
|
|
public $attributes;
|
|
|
|
/**
|
|
* @var string The title of this block. If this came from user input, it should already
|
|
* have had format_string() processing done on it. This will be output inside
|
|
* <h2> tags. Please do not cause invalid XHTML.
|
|
*/
|
|
public $title = '';
|
|
|
|
/**
|
|
* @var string The label to use when the block does not, or will not have a visible title.
|
|
* You should never set this as well as title... it will just be ignored.
|
|
*/
|
|
public $arialabel = '';
|
|
|
|
/**
|
|
* @var string HTML for the content
|
|
*/
|
|
public $content = '';
|
|
|
|
/**
|
|
* @var array An alternative to $content, it you want a list of things with optional icons.
|
|
*/
|
|
public $footer = '';
|
|
|
|
/**
|
|
* @var string Any small print that should appear under the block to explain
|
|
* to the teacher about the block, for example 'This is a sticky block that was
|
|
* added in the system context.'
|
|
*/
|
|
public $annotation = '';
|
|
|
|
/**
|
|
* @var int One of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
|
|
* the user can toggle whether this block is visible.
|
|
*/
|
|
public $collapsible = self::NOT_HIDEABLE;
|
|
|
|
/**
|
|
* Set this to true if the block is dockable.
|
|
* @var bool
|
|
*/
|
|
public $dockable = false;
|
|
|
|
/**
|
|
* @var array A (possibly empty) array of editing controls. Each element of
|
|
* this array should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
|
|
* $icon is the icon name. Fed to $OUTPUT->image_url.
|
|
*/
|
|
public $controls = array();
|
|
|
|
|
|
/**
|
|
* Create new instance of block content
|
|
* @param array $attributes
|
|
*/
|
|
public function __construct(array $attributes = null) {
|
|
$this->skipid = self::$idcounter;
|
|
self::$idcounter += 1;
|
|
|
|
if ($attributes) {
|
|
// standard block
|
|
$this->attributes = $attributes;
|
|
} else {
|
|
// simple "fake" blocks used in some modules and "Add new block" block
|
|
$this->attributes = array('class'=>'block');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add html class to block
|
|
*
|
|
* @param string $class
|
|
*/
|
|
public function add_class($class) {
|
|
$this->attributes['class'] .= ' '.$class;
|
|
}
|
|
|
|
/**
|
|
* Check if the block is a fake block.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function is_fake() {
|
|
return isset($this->attributes['data-block']) && $this->attributes['data-block'] == '_fake';
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This class represents a target for where a block can go when it is being moved.
|
|
*
|
|
* This needs to be rendered as a form with the given hidden from fields, and
|
|
* clicking anywhere in the form should submit it. The form action should be
|
|
* $PAGE->url.
|
|
*
|
|
* @copyright 2009 Tim Hunt
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class block_move_target {
|
|
|
|
/**
|
|
* @var moodle_url Move url
|
|
*/
|
|
public $url;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param moodle_url $url
|
|
*/
|
|
public function __construct(moodle_url $url) {
|
|
$this->url = $url;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Custom menu item
|
|
*
|
|
* This class is used to represent one item within a custom menu that may or may
|
|
* not have children.
|
|
*
|
|
* @copyright 2010 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class custom_menu_item implements renderable, templatable {
|
|
|
|
/**
|
|
* @var string The text to show for the item
|
|
*/
|
|
protected $text;
|
|
|
|
/**
|
|
* @var moodle_url The link to give the icon if it has no children
|
|
*/
|
|
protected $url;
|
|
|
|
/**
|
|
* @var string A title to apply to the item. By default the text
|
|
*/
|
|
protected $title;
|
|
|
|
/**
|
|
* @var int A sort order for the item, not necessary if you order things in
|
|
* the CFG var.
|
|
*/
|
|
protected $sort;
|
|
|
|
/**
|
|
* @var custom_menu_item A reference to the parent for this item or NULL if
|
|
* it is a top level item
|
|
*/
|
|
protected $parent;
|
|
|
|
/**
|
|
* @var array A array in which to store children this item has.
|
|
*/
|
|
protected $children = array();
|
|
|
|
/**
|
|
* @var int A reference to the sort var of the last child that was added
|
|
*/
|
|
protected $lastsort = 0;
|
|
|
|
/** @var array Array of other HTML attributes for the custom menu item. */
|
|
protected $attributes = [];
|
|
|
|
/**
|
|
* Constructs the new custom menu item
|
|
*
|
|
* @param string $text
|
|
* @param moodle_url $url A moodle url to apply as the link for this item [Optional]
|
|
* @param string $title A title to apply to this item [Optional]
|
|
* @param int $sort A sort or to use if we need to sort differently [Optional]
|
|
* @param custom_menu_item $parent A reference to the parent custom_menu_item this child
|
|
* belongs to, only if the child has a parent. [Optional]
|
|
* @param array $attributes Array of other HTML attributes for the custom menu item.
|
|
*/
|
|
public function __construct($text, moodle_url $url = null, $title = null, $sort = null, custom_menu_item $parent = null,
|
|
array $attributes = []) {
|
|
|
|
// Use class setter method for text to ensure it's always a string type.
|
|
$this->set_text($text);
|
|
|
|
$this->url = $url;
|
|
$this->title = $title;
|
|
$this->sort = (int)$sort;
|
|
$this->parent = $parent;
|
|
$this->attributes = $attributes;
|
|
}
|
|
|
|
/**
|
|
* Adds a custom menu item as a child of this node given its properties.
|
|
*
|
|
* @param string $text
|
|
* @param moodle_url $url
|
|
* @param string $title
|
|
* @param int $sort
|
|
* @param array $attributes Array of other HTML attributes for the custom menu item.
|
|
* @return custom_menu_item
|
|
*/
|
|
public function add($text, moodle_url $url = null, $title = null, $sort = null, $attributes = []) {
|
|
$key = count($this->children);
|
|
if (empty($sort)) {
|
|
$sort = $this->lastsort + 1;
|
|
}
|
|
$this->children[$key] = new custom_menu_item($text, $url, $title, $sort, $this, $attributes);
|
|
$this->lastsort = (int)$sort;
|
|
return $this->children[$key];
|
|
}
|
|
|
|
/**
|
|
* Removes a custom menu item that is a child or descendant to the current menu.
|
|
*
|
|
* Returns true if child was found and removed.
|
|
*
|
|
* @param custom_menu_item $menuitem
|
|
* @return bool
|
|
*/
|
|
public function remove_child(custom_menu_item $menuitem) {
|
|
$removed = false;
|
|
if (($key = array_search($menuitem, $this->children)) !== false) {
|
|
unset($this->children[$key]);
|
|
$this->children = array_values($this->children);
|
|
$removed = true;
|
|
} else {
|
|
foreach ($this->children as $child) {
|
|
if ($removed = $child->remove_child($menuitem)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return $removed;
|
|
}
|
|
|
|
/**
|
|
* Returns the text for this item
|
|
* @return string
|
|
*/
|
|
public function get_text() {
|
|
return $this->text;
|
|
}
|
|
|
|
/**
|
|
* Returns the url for this item
|
|
* @return moodle_url
|
|
*/
|
|
public function get_url() {
|
|
return $this->url;
|
|
}
|
|
|
|
/**
|
|
* Returns the title for this item
|
|
* @return string
|
|
*/
|
|
public function get_title() {
|
|
return $this->title;
|
|
}
|
|
|
|
/**
|
|
* Sorts and returns the children for this item
|
|
* @return array
|
|
*/
|
|
public function get_children() {
|
|
$this->sort();
|
|
return $this->children;
|
|
}
|
|
|
|
/**
|
|
* Gets the sort order for this child
|
|
* @return int
|
|
*/
|
|
public function get_sort_order() {
|
|
return $this->sort;
|
|
}
|
|
|
|
/**
|
|
* Gets the parent this child belong to
|
|
* @return custom_menu_item
|
|
*/
|
|
public function get_parent() {
|
|
return $this->parent;
|
|
}
|
|
|
|
/**
|
|
* Sorts the children this item has
|
|
*/
|
|
public function sort() {
|
|
usort($this->children, array('custom_menu','sort_custom_menu_items'));
|
|
}
|
|
|
|
/**
|
|
* Returns true if this item has any children
|
|
* @return bool
|
|
*/
|
|
public function has_children() {
|
|
return (count($this->children) > 0);
|
|
}
|
|
|
|
/**
|
|
* Sets the text for the node
|
|
* @param string $text
|
|
*/
|
|
public function set_text($text) {
|
|
$this->text = (string)$text;
|
|
}
|
|
|
|
/**
|
|
* Sets the title for the node
|
|
* @param string $title
|
|
*/
|
|
public function set_title($title) {
|
|
$this->title = (string)$title;
|
|
}
|
|
|
|
/**
|
|
* Sets the url for the node
|
|
* @param moodle_url $url
|
|
*/
|
|
public function set_url(moodle_url $url) {
|
|
$this->url = $url;
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$syscontext = context_system::instance();
|
|
|
|
$context = new stdClass();
|
|
$context->moremenuid = uniqid();
|
|
$context->text = \core_external\util::format_string($this->text, $syscontext->id);
|
|
$context->url = $this->url ? $this->url->out() : null;
|
|
// No need for the title if it's the same with text.
|
|
if ($this->text !== $this->title) {
|
|
// Show the title attribute only if it's different from the text.
|
|
$context->title = \core_external\util::format_string($this->title, $syscontext->id);
|
|
}
|
|
$context->sort = $this->sort;
|
|
if (!empty($this->attributes)) {
|
|
$context->attributes = $this->attributes;
|
|
}
|
|
$context->children = array();
|
|
if (preg_match("/^#+$/", $this->text)) {
|
|
$context->divider = true;
|
|
}
|
|
$context->haschildren = !empty($this->children) && (count($this->children) > 0);
|
|
foreach ($this->children as $child) {
|
|
$child = $child->export_for_template($output);
|
|
array_push($context->children, $child);
|
|
}
|
|
|
|
return $context;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Custom menu class
|
|
*
|
|
* This class is used to operate a custom menu that can be rendered for the page.
|
|
* The custom menu is built using $CFG->custommenuitems and is a structured collection
|
|
* of custom_menu_item nodes that can be rendered by the core renderer.
|
|
*
|
|
* To configure the custom menu:
|
|
* Settings: Administration > Appearance > Advanced theme settings
|
|
*
|
|
* @copyright 2010 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class custom_menu extends custom_menu_item {
|
|
|
|
/**
|
|
* @var string The language we should render for, null disables multilang support.
|
|
*/
|
|
protected $currentlanguage = null;
|
|
|
|
/**
|
|
* Creates the custom menu
|
|
*
|
|
* @param string $definition the menu items definition in syntax required by {@link convert_text_to_menu_nodes()}
|
|
* @param string $currentlanguage the current language code, null disables multilang support
|
|
*/
|
|
public function __construct($definition = '', $currentlanguage = null) {
|
|
$this->currentlanguage = $currentlanguage;
|
|
parent::__construct('root'); // create virtual root element of the menu
|
|
if (!empty($definition)) {
|
|
$this->override_children(self::convert_text_to_menu_nodes($definition, $currentlanguage));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides the children of this custom menu. Useful when getting children
|
|
* from $CFG->custommenuitems
|
|
*
|
|
* @param array $children
|
|
*/
|
|
public function override_children(array $children) {
|
|
$this->children = array();
|
|
foreach ($children as $child) {
|
|
if ($child instanceof custom_menu_item) {
|
|
$this->children[] = $child;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a string into a structured array of custom_menu_items which can
|
|
* then be added to a custom menu.
|
|
*
|
|
* Structure:
|
|
* text|url|title|langs
|
|
* The number of hyphens at the start determines the depth of the item. The
|
|
* languages are optional, comma separated list of languages the line is for.
|
|
*
|
|
* Example structure:
|
|
* First level first item|http://www.moodle.com/
|
|
* -Second level first item|http://www.moodle.com/partners/
|
|
* -Second level second item|http://www.moodle.com/hq/
|
|
* --Third level first item|http://www.moodle.com/jobs/
|
|
* -Second level third item|http://www.moodle.com/development/
|
|
* First level second item|http://www.moodle.com/feedback/
|
|
* First level third item
|
|
* English only|http://moodle.com|English only item|en
|
|
* German only|http://moodle.de|Deutsch|de,de_du,de_kids
|
|
*
|
|
*
|
|
* @static
|
|
* @param string $text the menu items definition
|
|
* @param string $language the language code, null disables multilang support
|
|
* @return array
|
|
*/
|
|
public static function convert_text_to_menu_nodes($text, $language = null) {
|
|
$root = new custom_menu();
|
|
$lastitem = $root;
|
|
$lastdepth = 0;
|
|
$hiddenitems = array();
|
|
$lines = explode("\n", $text);
|
|
foreach ($lines as $linenumber => $line) {
|
|
$line = trim($line);
|
|
if (strlen($line) == 0) {
|
|
continue;
|
|
}
|
|
// Parse item settings.
|
|
$itemtext = null;
|
|
$itemurl = null;
|
|
$itemtitle = null;
|
|
$itemvisible = true;
|
|
$settings = explode('|', $line);
|
|
foreach ($settings as $i => $setting) {
|
|
$setting = trim($setting);
|
|
if ($setting !== '') {
|
|
switch ($i) {
|
|
case 0: // Menu text.
|
|
$itemtext = ltrim($setting, '-');
|
|
break;
|
|
case 1: // URL.
|
|
try {
|
|
$itemurl = new moodle_url($setting);
|
|
} catch (moodle_exception $exception) {
|
|
// We're not actually worried about this, we don't want to mess up the display
|
|
// just for a wrongly entered URL.
|
|
$itemurl = null;
|
|
}
|
|
break;
|
|
case 2: // Title attribute.
|
|
$itemtitle = $setting;
|
|
break;
|
|
case 3: // Language.
|
|
if (!empty($language)) {
|
|
$itemlanguages = array_map('trim', explode(',', $setting));
|
|
$itemvisible &= in_array($language, $itemlanguages);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Get depth of new item.
|
|
preg_match('/^(\-*)/', $line, $match);
|
|
$itemdepth = strlen($match[1]) + 1;
|
|
// Find parent item for new item.
|
|
while (($lastdepth - $itemdepth) >= 0) {
|
|
$lastitem = $lastitem->get_parent();
|
|
$lastdepth--;
|
|
}
|
|
$lastitem = $lastitem->add($itemtext, $itemurl, $itemtitle, $linenumber + 1);
|
|
$lastdepth++;
|
|
if (!$itemvisible) {
|
|
$hiddenitems[] = $lastitem;
|
|
}
|
|
}
|
|
foreach ($hiddenitems as $item) {
|
|
$item->parent->remove_child($item);
|
|
}
|
|
return $root->get_children();
|
|
}
|
|
|
|
/**
|
|
* Sorts two custom menu items
|
|
*
|
|
* This function is designed to be used with the usort method
|
|
* usort($this->children, array('custom_menu','sort_custom_menu_items'));
|
|
*
|
|
* @static
|
|
* @param custom_menu_item $itema
|
|
* @param custom_menu_item $itemb
|
|
* @return int
|
|
*/
|
|
public static function sort_custom_menu_items(custom_menu_item $itema, custom_menu_item $itemb) {
|
|
$itema = $itema->get_sort_order();
|
|
$itemb = $itemb->get_sort_order();
|
|
if ($itema == $itemb) {
|
|
return 0;
|
|
}
|
|
return ($itema > $itemb) ? +1 : -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stores one tab
|
|
*
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @package core
|
|
*/
|
|
class tabobject implements renderable, templatable {
|
|
/** @var string unique id of the tab in this tree, it is used to find selected and/or inactive tabs */
|
|
var $id;
|
|
/** @var moodle_url|string link */
|
|
var $link;
|
|
/** @var string text on the tab */
|
|
var $text;
|
|
/** @var string title under the link, by defaul equals to text */
|
|
var $title;
|
|
/** @var bool whether to display a link under the tab name when it's selected */
|
|
var $linkedwhenselected = false;
|
|
/** @var bool whether the tab is inactive */
|
|
var $inactive = false;
|
|
/** @var bool indicates that this tab's child is selected */
|
|
var $activated = false;
|
|
/** @var bool indicates that this tab is selected */
|
|
var $selected = false;
|
|
/** @var array stores children tabobjects */
|
|
var $subtree = array();
|
|
/** @var int level of tab in the tree, 0 for root (instance of tabtree), 1 for the first row of tabs */
|
|
var $level = 1;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $id unique id of the tab in this tree, it is used to find selected and/or inactive tabs
|
|
* @param string|moodle_url $link
|
|
* @param string $text text on the tab
|
|
* @param string $title title under the link, by defaul equals to text
|
|
* @param bool $linkedwhenselected whether to display a link under the tab name when it's selected
|
|
*/
|
|
public function __construct($id, $link = null, $text = '', $title = '', $linkedwhenselected = false) {
|
|
$this->id = $id;
|
|
$this->link = $link;
|
|
$this->text = $text;
|
|
$this->title = $title ? $title : $text;
|
|
$this->linkedwhenselected = $linkedwhenselected;
|
|
}
|
|
|
|
/**
|
|
* Travels through tree and finds the tab to mark as selected, all parents are automatically marked as activated
|
|
*
|
|
* @param string $selected the id of the selected tab (whatever row it's on),
|
|
* if null marks all tabs as unselected
|
|
* @return bool whether this tab is selected or contains selected tab in its subtree
|
|
*/
|
|
protected function set_selected($selected) {
|
|
if ((string)$selected === (string)$this->id) {
|
|
$this->selected = true;
|
|
// This tab is selected. No need to travel through subtree.
|
|
return true;
|
|
}
|
|
foreach ($this->subtree as $subitem) {
|
|
if ($subitem->set_selected($selected)) {
|
|
// This tab has child that is selected. Mark it as activated. No need to check other children.
|
|
$this->activated = true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Travels through tree and finds a tab with specified id
|
|
*
|
|
* @param string $id
|
|
* @return tabtree|null
|
|
*/
|
|
public function find($id) {
|
|
if ((string)$this->id === (string)$id) {
|
|
return $this;
|
|
}
|
|
foreach ($this->subtree as $tab) {
|
|
if ($obj = $tab->find($id)) {
|
|
return $obj;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Allows to mark each tab's level in the tree before rendering.
|
|
*
|
|
* @param int $level
|
|
*/
|
|
protected function set_level($level) {
|
|
$this->level = $level;
|
|
foreach ($this->subtree as $tab) {
|
|
$tab->set_level($level + 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return object
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
if ($this->inactive || ($this->selected && !$this->linkedwhenselected) || $this->activated) {
|
|
$link = null;
|
|
} else {
|
|
$link = $this->link;
|
|
}
|
|
$active = $this->activated || $this->selected;
|
|
|
|
return (object) [
|
|
'id' => $this->id,
|
|
'link' => is_object($link) ? $link->out(false) : $link,
|
|
'text' => $this->text,
|
|
'title' => $this->title,
|
|
'inactive' => !$active && $this->inactive,
|
|
'active' => $active,
|
|
'level' => $this->level,
|
|
];
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Renderable for the main page header.
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @since 2.9
|
|
* @copyright 2015 Adrian Greeve <adrian@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class context_header implements renderable, templatable {
|
|
|
|
/**
|
|
* @var string $heading Main heading.
|
|
*/
|
|
public $heading;
|
|
/**
|
|
* @var int $headinglevel Main heading 'h' tag level.
|
|
*/
|
|
public $headinglevel;
|
|
/**
|
|
* @var string|null $imagedata HTML code for the picture in the page header.
|
|
*/
|
|
public $imagedata;
|
|
/**
|
|
* @var array $additionalbuttons Additional buttons for the header e.g. Messaging button for the user header.
|
|
* array elements - title => alternate text for the image, or if no image is available the button text.
|
|
* url => Link for the button to head to. Should be a moodle_url.
|
|
* image => location to the image, or name of the image in /pix/t/{image name}.
|
|
* linkattributes => additional attributes for the <a href> element.
|
|
* page => page object. Don't include if the image is an external image.
|
|
*/
|
|
public $additionalbuttons;
|
|
/**
|
|
* @var string $prefix A string that is before the title.
|
|
*/
|
|
public $prefix;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $heading Main heading data.
|
|
* @param int $headinglevel Main heading 'h' tag level.
|
|
* @param string|null $imagedata HTML code for the picture in the page header.
|
|
* @param string $additionalbuttons Buttons for the header e.g. Messaging button for the user header.
|
|
* @param string $prefix Text that precedes the heading.
|
|
*/
|
|
public function __construct($heading = null, $headinglevel = 1, $imagedata = null, $additionalbuttons = null, $prefix = null) {
|
|
|
|
$this->heading = $heading;
|
|
$this->headinglevel = $headinglevel;
|
|
$this->imagedata = $imagedata;
|
|
$this->additionalbuttons = $additionalbuttons;
|
|
// If we have buttons then format them.
|
|
if (isset($this->additionalbuttons)) {
|
|
$this->format_button_images();
|
|
}
|
|
$this->prefix = $prefix;
|
|
}
|
|
|
|
/**
|
|
* Adds an array element for a formatted image.
|
|
*/
|
|
protected function format_button_images() {
|
|
|
|
foreach ($this->additionalbuttons as $buttontype => $button) {
|
|
$page = $button['page'];
|
|
// If no image is provided then just use the title.
|
|
if (!isset($button['image'])) {
|
|
$this->additionalbuttons[$buttontype]['formattedimage'] = $button['title'];
|
|
} else {
|
|
// Check to see if this is an internal Moodle icon.
|
|
$internalimage = $page->theme->resolve_image_location('t/' . $button['image'], 'moodle');
|
|
if ($internalimage) {
|
|
$this->additionalbuttons[$buttontype]['formattedimage'] = 't/' . $button['image'];
|
|
} else {
|
|
// Treat as an external image.
|
|
$this->additionalbuttons[$buttontype]['formattedimage'] = $button['image'];
|
|
}
|
|
}
|
|
|
|
if (isset($button['linkattributes']['class'])) {
|
|
$class = $button['linkattributes']['class'] . ' btn';
|
|
} else {
|
|
$class = 'btn';
|
|
}
|
|
// Add the bootstrap 'btn' class for formatting.
|
|
$this->additionalbuttons[$buttontype]['linkattributes'] = array_merge($button['linkattributes'],
|
|
array('class' => $class));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return array
|
|
*/
|
|
public function export_for_template(renderer_base $output): array {
|
|
// Heading.
|
|
$headingtext = isset($this->heading) ? $this->heading : $output->get_page()->heading;
|
|
$heading = $output->heading($headingtext, $this->headinglevel, "h2 mb-0");
|
|
|
|
// Buttons.
|
|
if (isset($this->additionalbuttons)) {
|
|
$additionalbuttons = [];
|
|
foreach ($this->additionalbuttons as $button) {
|
|
if (!isset($button->page)) {
|
|
// Include js for messaging.
|
|
if ($button['buttontype'] === 'togglecontact') {
|
|
\core_message\helper::togglecontact_requirejs();
|
|
}
|
|
if ($button['buttontype'] === 'message') {
|
|
\core_message\helper::messageuser_requirejs();
|
|
}
|
|
}
|
|
foreach ($button['linkattributes'] as $key => $value) {
|
|
$button['attributes'][] = ['name' => $key, 'value' => $value];
|
|
}
|
|
$additionalbuttons[] = $button;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'heading' => $heading,
|
|
'headinglevel' => $this->headinglevel,
|
|
'imagedata' => $this->imagedata,
|
|
'prefix' => $this->prefix,
|
|
'hasadditionalbuttons' => !empty($additionalbuttons),
|
|
'additionalbuttons' => $additionalbuttons ?? [],
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stores tabs list
|
|
*
|
|
* Example how to print a single line tabs:
|
|
* $rows = array(
|
|
* new tabobject(...),
|
|
* new tabobject(...)
|
|
* );
|
|
* echo $OUTPUT->tabtree($rows, $selectedid);
|
|
*
|
|
* Multiple row tabs may not look good on some devices but if you want to use them
|
|
* you can specify ->subtree for the active tabobject.
|
|
*
|
|
* @copyright 2013 Marina Glancy
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Moodle 2.5
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class tabtree extends tabobject {
|
|
/**
|
|
* Constuctor
|
|
*
|
|
* It is highly recommended to call constructor when list of tabs is already
|
|
* populated, this way you ensure that selected and inactive tabs are located
|
|
* and attribute level is set correctly.
|
|
*
|
|
* @param array $tabs array of tabs, each of them may have it's own ->subtree
|
|
* @param string|null $selected which tab to mark as selected, all parent tabs will
|
|
* automatically be marked as activated
|
|
* @param array|string|null $inactive list of ids of inactive tabs, regardless of
|
|
* their level. Note that you can as weel specify tabobject::$inactive for separate instances
|
|
*/
|
|
public function __construct($tabs, $selected = null, $inactive = null) {
|
|
$this->subtree = $tabs;
|
|
if ($selected !== null) {
|
|
$this->set_selected($selected);
|
|
}
|
|
if ($inactive !== null) {
|
|
if (is_array($inactive)) {
|
|
foreach ($inactive as $id) {
|
|
if ($tab = $this->find($id)) {
|
|
$tab->inactive = true;
|
|
}
|
|
}
|
|
} else if ($tab = $this->find($inactive)) {
|
|
$tab->inactive = true;
|
|
}
|
|
}
|
|
$this->set_level(0);
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output Renderer.
|
|
* @return object
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$tabs = [];
|
|
$secondrow = false;
|
|
|
|
foreach ($this->subtree as $tab) {
|
|
$tabs[] = $tab->export_for_template($output);
|
|
if (!empty($tab->subtree) && ($tab->level == 0 || $tab->selected || $tab->activated)) {
|
|
$secondrow = new tabtree($tab->subtree);
|
|
}
|
|
}
|
|
|
|
return (object) [
|
|
'tabs' => $tabs,
|
|
'secondrow' => $secondrow ? $secondrow->export_for_template($output) : false
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An action menu.
|
|
*
|
|
* This action menu component takes a series of primary and secondary actions.
|
|
* The primary actions are displayed permanently and the secondary attributes are displayed within a drop
|
|
* down menu.
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2013 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class action_menu implements renderable, templatable {
|
|
|
|
/**
|
|
* Top right alignment.
|
|
*/
|
|
const TL = 1;
|
|
|
|
/**
|
|
* Top right alignment.
|
|
*/
|
|
const TR = 2;
|
|
|
|
/**
|
|
* Top right alignment.
|
|
*/
|
|
const BL = 3;
|
|
|
|
/**
|
|
* Top right alignment.
|
|
*/
|
|
const BR = 4;
|
|
|
|
/**
|
|
* The instance number. This is unique to this instance of the action menu.
|
|
* @var int
|
|
*/
|
|
protected $instance = 0;
|
|
|
|
/**
|
|
* An array of primary actions. Please use {@link action_menu::add_primary_action()} to add actions.
|
|
* @var array
|
|
*/
|
|
protected $primaryactions = array();
|
|
|
|
/**
|
|
* An array of secondary actions. Please use {@link action_menu::add_secondary_action()} to add actions.
|
|
* @var array
|
|
*/
|
|
protected $secondaryactions = array();
|
|
|
|
/**
|
|
* An array of attributes added to the container of the action menu.
|
|
* Initialised with defaults during construction.
|
|
* @var array
|
|
*/
|
|
public $attributes = array();
|
|
/**
|
|
* An array of attributes added to the container of the primary actions.
|
|
* Initialised with defaults during construction.
|
|
* @var array
|
|
*/
|
|
public $attributesprimary = array();
|
|
/**
|
|
* An array of attributes added to the container of the secondary actions.
|
|
* Initialised with defaults during construction.
|
|
* @var array
|
|
*/
|
|
public $attributessecondary = array();
|
|
|
|
/**
|
|
* The string to use next to the icon for the action icon relating to the secondary (dropdown) menu.
|
|
* @var array
|
|
*/
|
|
public $actiontext = null;
|
|
|
|
/**
|
|
* The string to use for the accessible label for the menu.
|
|
* @var array
|
|
*/
|
|
public $actionlabel = null;
|
|
|
|
/**
|
|
* An icon to use for the toggling the secondary menu (dropdown).
|
|
* @var pix_icon
|
|
*/
|
|
public $actionicon;
|
|
|
|
/**
|
|
* Any text to use for the toggling the secondary menu (dropdown).
|
|
* @var string
|
|
*/
|
|
public $menutrigger = '';
|
|
|
|
/**
|
|
* An array of attributes added to the trigger element of the secondary menu.
|
|
* @var array
|
|
*/
|
|
public $triggerattributes = [];
|
|
|
|
/**
|
|
* Any extra classes for toggling to the secondary menu.
|
|
* @var string
|
|
*/
|
|
public $triggerextraclasses = '';
|
|
|
|
/**
|
|
* Place the action menu before all other actions.
|
|
* @var bool
|
|
*/
|
|
public $prioritise = false;
|
|
|
|
/**
|
|
* Dropdown menu alignment class.
|
|
* @var string
|
|
*/
|
|
public $dropdownalignment = '';
|
|
|
|
/**
|
|
* Constructs the action menu with the given items.
|
|
*
|
|
* @param array $actions An array of actions (action_menu_link|pix_icon|string).
|
|
*/
|
|
public function __construct(array $actions = array()) {
|
|
static $initialised = 0;
|
|
$this->instance = $initialised;
|
|
$initialised++;
|
|
|
|
$this->attributes = array(
|
|
'id' => 'action-menu-'.$this->instance,
|
|
'class' => 'moodle-actionmenu',
|
|
'data-enhance' => 'moodle-core-actionmenu'
|
|
);
|
|
$this->attributesprimary = array(
|
|
'id' => 'action-menu-'.$this->instance.'-menubar',
|
|
'class' => 'menubar',
|
|
);
|
|
$this->attributessecondary = array(
|
|
'id' => 'action-menu-'.$this->instance.'-menu',
|
|
'class' => 'menu',
|
|
'data-rel' => 'menu-content',
|
|
'aria-labelledby' => 'action-menu-toggle-'.$this->instance,
|
|
'role' => 'menu'
|
|
);
|
|
$this->dropdownalignment = 'dropdown-menu-right';
|
|
foreach ($actions as $action) {
|
|
$this->add($action);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the label for the menu trigger.
|
|
*
|
|
* @param string $label The text
|
|
*/
|
|
public function set_action_label($label) {
|
|
$this->actionlabel = $label;
|
|
}
|
|
|
|
/**
|
|
* Sets the menu trigger text.
|
|
*
|
|
* @param string $trigger The text
|
|
* @param string $extraclasses Extra classes to style the secondary menu toggle.
|
|
*/
|
|
public function set_menu_trigger($trigger, $extraclasses = '') {
|
|
$this->menutrigger = $trigger;
|
|
$this->triggerextraclasses = $extraclasses;
|
|
}
|
|
|
|
/**
|
|
* Classes for the trigger menu
|
|
*/
|
|
const DEFAULT_KEBAB_TRIGGER_CLASSES = 'btn btn-icon d-flex align-items-center justify-content-center no-caret';
|
|
|
|
/**
|
|
* Setup trigger as in the kebab menu.
|
|
*
|
|
* @param string|null $triggername
|
|
* @param core_renderer|null $output
|
|
* @param string|null $extraclasses extra classes for the trigger {@see self::set_menu_trigger()}
|
|
* @throws coding_exception
|
|
*/
|
|
public function set_kebab_trigger(?string $triggername = null, ?core_renderer $output = null,
|
|
?string $extraclasses = '') {
|
|
global $OUTPUT;
|
|
if (empty($output)) {
|
|
$output = $OUTPUT;
|
|
}
|
|
$label = $triggername ?? get_string('actions');
|
|
$triggerclasses = self::DEFAULT_KEBAB_TRIGGER_CLASSES . ' ' . $extraclasses;
|
|
$icon = $output->pix_icon('i/menu', $label);
|
|
$this->set_menu_trigger($icon, $triggerclasses);
|
|
}
|
|
|
|
/**
|
|
* Return true if there is at least one visible link in the menu.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_empty() {
|
|
return !count($this->primaryactions) && !count($this->secondaryactions);
|
|
}
|
|
|
|
/**
|
|
* Initialises JS required fore the action menu.
|
|
* The JS is only required once as it manages all action menu's on the page.
|
|
*
|
|
* @param moodle_page $page
|
|
*/
|
|
public function initialise_js(moodle_page $page) {
|
|
static $initialised = false;
|
|
if (!$initialised) {
|
|
$page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
|
|
$initialised = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an action to this action menu.
|
|
*
|
|
* @param action_link|pix_icon|subpanel|string $action
|
|
*/
|
|
public function add($action) {
|
|
|
|
if ($action instanceof subpanel) {
|
|
$this->add_secondary_subpanel($action);
|
|
} else if ($action instanceof action_link) {
|
|
if ($action->primary) {
|
|
$this->add_primary_action($action);
|
|
} else {
|
|
$this->add_secondary_action($action);
|
|
}
|
|
} else if ($action instanceof pix_icon) {
|
|
$this->add_primary_action($action);
|
|
} else {
|
|
$this->add_secondary_action($action);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a secondary subpanel.
|
|
* @param subpanel $subpanel
|
|
*/
|
|
public function add_secondary_subpanel(subpanel $subpanel) {
|
|
$this->secondaryactions[] = $subpanel;
|
|
}
|
|
|
|
/**
|
|
* Adds a primary action to the action menu.
|
|
*
|
|
* @param action_menu_link|action_link|pix_icon|string $action
|
|
*/
|
|
public function add_primary_action($action) {
|
|
if ($action instanceof action_link || $action instanceof pix_icon) {
|
|
$action->attributes['role'] = 'menuitem';
|
|
$action->attributes['tabindex'] = '-1';
|
|
if ($action instanceof action_menu_link) {
|
|
$action->actionmenu = $this;
|
|
}
|
|
}
|
|
$this->primaryactions[] = $action;
|
|
}
|
|
|
|
/**
|
|
* Adds a secondary action to the action menu.
|
|
*
|
|
* @param action_link|pix_icon|string $action
|
|
*/
|
|
public function add_secondary_action($action) {
|
|
if ($action instanceof action_link || $action instanceof pix_icon) {
|
|
$action->attributes['role'] = 'menuitem';
|
|
$action->attributes['tabindex'] = '-1';
|
|
if ($action instanceof action_menu_link) {
|
|
$action->actionmenu = $this;
|
|
}
|
|
}
|
|
$this->secondaryactions[] = $action;
|
|
}
|
|
|
|
/**
|
|
* Returns the primary actions ready to be rendered.
|
|
*
|
|
* @param core_renderer $output The renderer to use for getting icons.
|
|
* @return array
|
|
*/
|
|
public function get_primary_actions(core_renderer $output = null) {
|
|
global $OUTPUT;
|
|
if ($output === null) {
|
|
$output = $OUTPUT;
|
|
}
|
|
$pixicon = $this->actionicon;
|
|
$linkclasses = array('toggle-display');
|
|
|
|
$title = '';
|
|
if (!empty($this->menutrigger)) {
|
|
$pixicon = '<b class="caret"></b>';
|
|
$linkclasses[] = 'textmenu';
|
|
} else {
|
|
$title = new lang_string('actionsmenu', 'moodle');
|
|
$this->actionicon = new pix_icon(
|
|
't/edit_menu',
|
|
'',
|
|
'moodle',
|
|
array('class' => 'iconsmall actionmenu', 'title' => '')
|
|
);
|
|
$pixicon = $this->actionicon;
|
|
}
|
|
if ($pixicon instanceof renderable) {
|
|
$pixicon = $output->render($pixicon);
|
|
if ($pixicon instanceof pix_icon && isset($pixicon->attributes['alt'])) {
|
|
$title = $pixicon->attributes['alt'];
|
|
}
|
|
}
|
|
$string = '';
|
|
if ($this->actiontext) {
|
|
$string = $this->actiontext;
|
|
}
|
|
$label = '';
|
|
if ($this->actionlabel) {
|
|
$label = $this->actionlabel;
|
|
} else {
|
|
$label = $title;
|
|
}
|
|
$actions = $this->primaryactions;
|
|
$attributes = array(
|
|
'class' => implode(' ', $linkclasses),
|
|
'title' => $title,
|
|
'aria-label' => $label,
|
|
'id' => 'action-menu-toggle-'.$this->instance,
|
|
'role' => 'menuitem',
|
|
'tabindex' => '-1',
|
|
);
|
|
$link = html_writer::link('#', $string . $this->menutrigger . $pixicon, $attributes);
|
|
if ($this->prioritise) {
|
|
array_unshift($actions, $link);
|
|
} else {
|
|
$actions[] = $link;
|
|
}
|
|
return $actions;
|
|
}
|
|
|
|
/**
|
|
* Returns the secondary actions ready to be rendered.
|
|
* @return array
|
|
*/
|
|
public function get_secondary_actions() {
|
|
return $this->secondaryactions;
|
|
}
|
|
|
|
/**
|
|
* Sets the selector that should be used to find the owning node of this menu.
|
|
* @param string $selector A CSS/YUI selector to identify the owner of the menu.
|
|
*/
|
|
public function set_owner_selector($selector) {
|
|
$this->attributes['data-owner'] = $selector;
|
|
}
|
|
|
|
/**
|
|
* Sets the alignment of the dialogue in relation to button used to toggle it.
|
|
*
|
|
* @deprecated since Moodle 4.0
|
|
*
|
|
* @param int $dialogue One of action_menu::TL, action_menu::TR, action_menu::BL, action_menu::BR.
|
|
* @param int $button One of action_menu::TL, action_menu::TR, action_menu::BL, action_menu::BR.
|
|
*/
|
|
public function set_alignment($dialogue, $button) {
|
|
debugging('The method action_menu::set_alignment() is deprecated, use action_menu::set_menu_left()', DEBUG_DEVELOPER);
|
|
if (isset($this->attributessecondary['data-align'])) {
|
|
// We've already got one set, lets remove the old class so as to avoid troubles.
|
|
$class = $this->attributessecondary['class'];
|
|
$search = 'align-'.$this->attributessecondary['data-align'];
|
|
$this->attributessecondary['class'] = str_replace($search, '', $class);
|
|
}
|
|
$align = $this->get_align_string($dialogue) . '-' . $this->get_align_string($button);
|
|
$this->attributessecondary['data-align'] = $align;
|
|
$this->attributessecondary['class'] .= ' align-'.$align;
|
|
}
|
|
|
|
/**
|
|
* Returns a string to describe the alignment.
|
|
*
|
|
* @param int $align One of action_menu::TL, action_menu::TR, action_menu::BL, action_menu::BR.
|
|
* @return string
|
|
*/
|
|
protected function get_align_string($align) {
|
|
switch ($align) {
|
|
case self::TL :
|
|
return 'tl';
|
|
case self::TR :
|
|
return 'tr';
|
|
case self::BL :
|
|
return 'bl';
|
|
case self::BR :
|
|
return 'br';
|
|
default :
|
|
return 'tl';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aligns the left corner of the dropdown.
|
|
*
|
|
*/
|
|
public function set_menu_left() {
|
|
$this->dropdownalignment = 'dropdown-menu-left';
|
|
}
|
|
|
|
/**
|
|
* Sets a constraint for the dialogue.
|
|
*
|
|
* The constraint is applied when the dialogue is shown and limits the display of the dialogue to within the
|
|
* element the constraint identifies.
|
|
*
|
|
* This is required whenever the action menu is displayed inside any CSS element with the .no-overflow class
|
|
* (flexible_table and any of it's child classes are a likely candidate).
|
|
*
|
|
* @deprecated since Moodle 4.3
|
|
* @param string $ancestorselector A snippet of CSS used to identify the ancestor to contrain the dialogue to.
|
|
*/
|
|
public function set_constraint($ancestorselector) {
|
|
debugging('The method set_constraint() is deprecated. Please use the set_boundary() method instead.', DEBUG_DEVELOPER);
|
|
$this->set_boundary('window');
|
|
}
|
|
|
|
/**
|
|
* Set the overflow constraint boundary of the dropdown menu.
|
|
* @see https://getbootstrap.com/docs/4.6/components/dropdowns/#options The 'boundary' option in the Bootstrap documentation
|
|
*
|
|
* @param string $boundary Accepts the values of 'viewport', 'window', or 'scrollParent'.
|
|
* @throws coding_exception
|
|
*/
|
|
public function set_boundary(string $boundary) {
|
|
if (!in_array($boundary, ['viewport', 'window', 'scrollParent'])) {
|
|
throw new coding_exception("HTMLElement reference boundaries are not supported." .
|
|
"Accepted boundaries are 'viewport', 'window', or 'scrollParent'.", DEBUG_DEVELOPER);
|
|
}
|
|
|
|
$this->triggerattributes['data-boundary'] = $boundary;
|
|
}
|
|
|
|
/**
|
|
* If you call this method the action menu will be displayed but will not be enhanced.
|
|
*
|
|
* By not displaying the menu enhanced all items will be displayed in a single row.
|
|
*
|
|
* @deprecated since Moodle 3.2
|
|
*/
|
|
public function do_not_enhance() {
|
|
debugging('The method action_menu::do_not_enhance() is deprecated, use a list of action_icon instead.', DEBUG_DEVELOPER);
|
|
}
|
|
|
|
/**
|
|
* Returns true if this action menu will be enhanced.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function will_be_enhanced() {
|
|
return isset($this->attributes['data-enhance']);
|
|
}
|
|
|
|
/**
|
|
* Sets nowrap on items. If true menu items should not wrap lines if they are longer than the available space.
|
|
*
|
|
* This property can be useful when the action menu is displayed within a parent element that is either floated
|
|
* or relatively positioned.
|
|
* In that situation the width of the menu is determined by the width of the parent element which may not be large
|
|
* enough for the menu items without them wrapping.
|
|
* This disables the wrapping so that the menu takes on the width of the longest item.
|
|
*
|
|
* @param bool $value If true nowrap gets set, if false it gets removed. Defaults to true.
|
|
*/
|
|
public function set_nowrap_on_items($value = true) {
|
|
$class = 'nowrap-items';
|
|
if (!empty($this->attributes['class'])) {
|
|
$pos = strpos($this->attributes['class'], $class);
|
|
if ($value === true && $pos === false) {
|
|
// The value is true and the class has not been set yet. Add it.
|
|
$this->attributes['class'] .= ' '.$class;
|
|
} else if ($value === false && $pos !== false) {
|
|
// The value is false and the class has been set. Remove it.
|
|
$this->attributes['class'] = substr($this->attributes['class'], $pos, strlen($class));
|
|
}
|
|
} else if ($value) {
|
|
// The value is true and the class has not been set yet. Add it.
|
|
$this->attributes['class'] = $class;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add classes to the action menu for an easier styling.
|
|
*
|
|
* @param string $class The class to add to attributes.
|
|
*/
|
|
public function set_additional_classes(string $class = '') {
|
|
if (!empty($this->attributes['class'])) {
|
|
$this->attributes['class'] .= " ".$class;
|
|
} else {
|
|
$this->attributes['class'] = $class;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$data = new stdClass();
|
|
// Assign a role of menubar to this action menu when:
|
|
// - it contains 2 or more primary actions; or
|
|
// - if it contains a primary action and secondary actions.
|
|
if (count($this->primaryactions) > 1 || (!empty($this->primaryactions) && !empty($this->secondaryactions))) {
|
|
$this->attributes['role'] = 'menubar';
|
|
}
|
|
$attributes = $this->attributes;
|
|
|
|
$data->instance = $this->instance;
|
|
|
|
$data->classes = isset($attributes['class']) ? $attributes['class'] : '';
|
|
unset($attributes['class']);
|
|
|
|
$data->attributes = array_map(function($key, $value) {
|
|
return [ 'name' => $key, 'value' => $value ];
|
|
}, array_keys($attributes), $attributes);
|
|
|
|
$data->primary = $this->export_primary_actions_for_template($output);
|
|
$data->secondary = $this->export_secondary_actions_for_template($output);
|
|
$data->dropdownalignment = $this->dropdownalignment;
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Export the primary actions for the template.
|
|
* @param renderer_base $output
|
|
* @return stdClass
|
|
*/
|
|
protected function export_primary_actions_for_template(renderer_base $output): stdClass {
|
|
$attributes = $this->attributes;
|
|
$attributesprimary = $this->attributesprimary;
|
|
|
|
$primary = new stdClass();
|
|
$primary->title = '';
|
|
$primary->prioritise = $this->prioritise;
|
|
|
|
$primary->classes = isset($attributesprimary['class']) ? $attributesprimary['class'] : '';
|
|
unset($attributesprimary['class']);
|
|
|
|
$primary->attributes = array_map(function ($key, $value) {
|
|
return ['name' => $key, 'value' => $value];
|
|
}, array_keys($attributesprimary), $attributesprimary);
|
|
$primary->triggerattributes = array_map(function ($key, $value) {
|
|
return ['name' => $key, 'value' => $value];
|
|
}, array_keys($this->triggerattributes), $this->triggerattributes);
|
|
|
|
$actionicon = $this->actionicon;
|
|
if (!empty($this->menutrigger)) {
|
|
$primary->menutrigger = $this->menutrigger;
|
|
$primary->triggerextraclasses = $this->triggerextraclasses;
|
|
if ($this->actionlabel) {
|
|
$primary->title = $this->actionlabel;
|
|
} else if ($this->actiontext) {
|
|
$primary->title = $this->actiontext;
|
|
} else {
|
|
$primary->title = strip_tags($this->menutrigger);
|
|
}
|
|
} else {
|
|
$primary->title = get_string('actionsmenu');
|
|
$iconattributes = ['class' => 'iconsmall actionmenu', 'title' => $primary->title];
|
|
$actionicon = new pix_icon('t/edit_menu', '', 'moodle', $iconattributes);
|
|
}
|
|
|
|
// If the menu trigger is within the menubar, assign a role of menuitem. Otherwise, assign as a button.
|
|
$primary->triggerrole = 'button';
|
|
if (isset($attributes['role']) && $attributes['role'] === 'menubar') {
|
|
$primary->triggerrole = 'menuitem';
|
|
}
|
|
|
|
if ($actionicon instanceof pix_icon) {
|
|
$primary->icon = $actionicon->export_for_pix();
|
|
if (!empty($actionicon->attributes['alt'])) {
|
|
$primary->title = $actionicon->attributes['alt'];
|
|
}
|
|
} else {
|
|
$primary->iconraw = $actionicon ? $output->render($actionicon) : '';
|
|
}
|
|
|
|
$primary->actiontext = $this->actiontext ? (string) $this->actiontext : '';
|
|
$primary->items = array_map(function ($item) use ($output) {
|
|
$data = (object) [];
|
|
if ($item instanceof action_menu_link) {
|
|
$data->actionmenulink = $item->export_for_template($output);
|
|
} else if ($item instanceof action_menu_filler) {
|
|
$data->actionmenufiller = $item->export_for_template($output);
|
|
} else if ($item instanceof action_link) {
|
|
$data->actionlink = $item->export_for_template($output);
|
|
} else if ($item instanceof pix_icon) {
|
|
$data->pixicon = $item->export_for_template($output);
|
|
} else {
|
|
$data->rawhtml = ($item instanceof renderable) ? $output->render($item) : $item;
|
|
}
|
|
return $data;
|
|
}, $this->primaryactions);
|
|
return $primary;
|
|
}
|
|
|
|
/**
|
|
* Export the secondary actions for the template.
|
|
* @param renderer_base $output
|
|
* @return stdClass
|
|
*/
|
|
protected function export_secondary_actions_for_template(renderer_base $output): stdClass {
|
|
$attributessecondary = $this->attributessecondary;
|
|
$secondary = new stdClass();
|
|
$secondary->classes = isset($attributessecondary['class']) ? $attributessecondary['class'] : '';
|
|
unset($attributessecondary['class']);
|
|
|
|
$secondary->attributes = array_map(function ($key, $value) {
|
|
return ['name' => $key, 'value' => $value];
|
|
}, array_keys($attributessecondary), $attributessecondary);
|
|
$secondary->items = array_map(function ($item) use ($output) {
|
|
$data = (object) [
|
|
'simpleitem' => true,
|
|
];
|
|
if ($item instanceof action_menu_link) {
|
|
$data->actionmenulink = $item->export_for_template($output);
|
|
$data->simpleitem = false;
|
|
} else if ($item instanceof action_menu_filler) {
|
|
$data->actionmenufiller = $item->export_for_template($output);
|
|
$data->simpleitem = false;
|
|
} else if ($item instanceof subpanel) {
|
|
$data->subpanel = $item->export_for_template($output);
|
|
$data->simpleitem = false;
|
|
} else if ($item instanceof action_link) {
|
|
$data->actionlink = $item->export_for_template($output);
|
|
} else if ($item instanceof pix_icon) {
|
|
$data->pixicon = $item->export_for_template($output);
|
|
} else {
|
|
$data->rawhtml = ($item instanceof renderable) ? $output->render($item) : $item;
|
|
}
|
|
return $data;
|
|
}, $this->secondaryactions);
|
|
return $secondary;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An action menu filler
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2013 Andrew Nicols
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class action_menu_filler extends action_link implements renderable {
|
|
|
|
/**
|
|
* True if this is a primary action. False if not.
|
|
* @var bool
|
|
*/
|
|
public $primary = true;
|
|
|
|
/**
|
|
* Constructs the object.
|
|
*/
|
|
public function __construct() {
|
|
$this->attributes['id'] = html_writer::random_id('action_link');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An action menu action
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2013 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class action_menu_link extends action_link implements renderable {
|
|
|
|
/**
|
|
* True if this is a primary action. False if not.
|
|
* @var bool
|
|
*/
|
|
public $primary = true;
|
|
|
|
/**
|
|
* The action menu this link has been added to.
|
|
* @var action_menu
|
|
*/
|
|
public $actionmenu = null;
|
|
|
|
/**
|
|
* The number of instances of this action menu link (and its subclasses).
|
|
*
|
|
* @deprecated since Moodle 4.4.
|
|
* @var int
|
|
*/
|
|
protected static $instance = 1;
|
|
|
|
/**
|
|
* Constructs the object.
|
|
*
|
|
* @param moodle_url $url The URL for the action.
|
|
* @param pix_icon|null $icon The icon to represent the action.
|
|
* @param string $text The text to represent the action.
|
|
* @param bool $primary Whether this is a primary action or not.
|
|
* @param array $attributes Any attribtues associated with the action.
|
|
*/
|
|
public function __construct(moodle_url $url, ?pix_icon $icon, $text, $primary = true, array $attributes = array()) {
|
|
parent::__construct($url, $text, null, $attributes, $icon);
|
|
$this->primary = (bool)$primary;
|
|
$this->add_class('menu-action');
|
|
$this->attributes['role'] = 'menuitem';
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return stdClass
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
$data = parent::export_for_template($output);
|
|
|
|
// Ignore what the parent did with the attributes, except for ID and class.
|
|
$data->attributes = [];
|
|
$attributes = $this->attributes;
|
|
unset($attributes['id']);
|
|
unset($attributes['class']);
|
|
|
|
// Handle text being a renderable.
|
|
if ($this->text instanceof renderable) {
|
|
$data->text = $this->render($this->text);
|
|
}
|
|
|
|
$data->showtext = (!$this->icon || $this->primary === false);
|
|
|
|
$data->icon = null;
|
|
if ($this->icon) {
|
|
$icon = $this->icon;
|
|
if ($this->primary || !$this->actionmenu->will_be_enhanced()) {
|
|
$attributes['title'] = $data->text;
|
|
}
|
|
$data->icon = $icon ? $icon->export_for_pix() : null;
|
|
}
|
|
|
|
$data->disabled = !empty($attributes['disabled']);
|
|
unset($attributes['disabled']);
|
|
|
|
$data->attributes = array_map(function($key, $value) {
|
|
return [
|
|
'name' => $key,
|
|
'value' => $value
|
|
];
|
|
}, array_keys($attributes), $attributes);
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A primary action menu action
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2013 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class action_menu_link_primary extends action_menu_link {
|
|
/**
|
|
* Constructs the object.
|
|
*
|
|
* @param moodle_url $url
|
|
* @param pix_icon|null $icon
|
|
* @param string $text
|
|
* @param array $attributes
|
|
*/
|
|
public function __construct(moodle_url $url, ?pix_icon $icon, $text, array $attributes = array()) {
|
|
parent::__construct($url, $icon, $text, true, $attributes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A secondary action menu action
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2013 Sam Hemelryk
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class action_menu_link_secondary extends action_menu_link {
|
|
/**
|
|
* Constructs the object.
|
|
*
|
|
* @param moodle_url $url
|
|
* @param pix_icon|null $icon
|
|
* @param string $text
|
|
* @param array $attributes
|
|
*/
|
|
public function __construct(moodle_url $url, ?pix_icon $icon, $text, array $attributes = array()) {
|
|
parent::__construct($url, $icon, $text, false, $attributes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Represents a set of preferences groups.
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2015 Frédéric Massart - FMCorz.net
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class preferences_groups implements renderable {
|
|
|
|
/**
|
|
* Array of preferences_group.
|
|
* @var array
|
|
*/
|
|
public $groups;
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param array $groups of preferences_group
|
|
*/
|
|
public function __construct($groups) {
|
|
$this->groups = $groups;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Represents a group of preferences page link.
|
|
*
|
|
* @package core
|
|
* @category output
|
|
* @copyright 2015 Frédéric Massart - FMCorz.net
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class preferences_group implements renderable {
|
|
|
|
/**
|
|
* Title of the group.
|
|
* @var string
|
|
*/
|
|
public $title;
|
|
|
|
/**
|
|
* Array of navigation_node.
|
|
* @var array
|
|
*/
|
|
public $nodes;
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param string $title The title.
|
|
* @param array $nodes of navigation_node.
|
|
*/
|
|
public function __construct($title, $nodes) {
|
|
$this->title = $title;
|
|
$this->nodes = $nodes;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Progress bar class.
|
|
*
|
|
* Manages the display of a progress bar.
|
|
*
|
|
* To use this class.
|
|
* - construct
|
|
* - call create (or use the 3rd param to the constructor)
|
|
* - call update or update_full() or update() repeatedly
|
|
*
|
|
* @copyright 2008 jamiesensei
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class progress_bar implements renderable, templatable {
|
|
/** @var string html id */
|
|
private $html_id;
|
|
/** @var int total width */
|
|
private $width;
|
|
/** @var int last percentage printed */
|
|
private $percent = 0;
|
|
/** @var int time when last printed */
|
|
private $lastupdate = 0;
|
|
/** @var int when did we start printing this */
|
|
private $time_start = 0;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* Prints JS code if $autostart true.
|
|
*
|
|
* @param string $htmlid The container ID.
|
|
* @param int $width The suggested width.
|
|
* @param bool $autostart Whether to start the progress bar right away.
|
|
*/
|
|
public function __construct($htmlid = '', $width = 500, $autostart = false) {
|
|
if (!CLI_SCRIPT && !NO_OUTPUT_BUFFERING) {
|
|
debugging('progress_bar used in a non-CLI script without setting NO_OUTPUT_BUFFERING.', DEBUG_DEVELOPER);
|
|
}
|
|
|
|
if (!empty($htmlid)) {
|
|
$this->html_id = $htmlid;
|
|
} else {
|
|
$this->html_id = 'pbar_'.uniqid();
|
|
}
|
|
|
|
$this->width = $width;
|
|
|
|
if ($autostart) {
|
|
$this->create();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Getter for ID
|
|
* @return string id
|
|
*/
|
|
public function get_id(): string {
|
|
return $this->html_id;
|
|
}
|
|
|
|
/**
|
|
* Create a new progress bar, this function will output html.
|
|
*
|
|
* @return void Echo's output
|
|
*/
|
|
public function create() {
|
|
global $OUTPUT;
|
|
|
|
$this->time_start = microtime(true);
|
|
|
|
flush();
|
|
echo $OUTPUT->render($this);
|
|
flush();
|
|
}
|
|
|
|
/**
|
|
* Update the progress bar.
|
|
*
|
|
* @param int $percent From 1-100.
|
|
* @param string $msg The message.
|
|
* @return void Echo's output
|
|
* @throws coding_exception
|
|
*/
|
|
private function _update($percent, $msg) {
|
|
global $OUTPUT;
|
|
|
|
if (empty($this->time_start)) {
|
|
throw new coding_exception('You must call create() (or use the $autostart ' .
|
|
'argument to the constructor) before you try updating the progress bar.');
|
|
}
|
|
|
|
$estimate = $this->estimate($percent);
|
|
|
|
if ($estimate === null) {
|
|
// Always do the first and last updates.
|
|
} else if ($estimate == 0) {
|
|
// Always do the last updates.
|
|
} else if ($this->lastupdate + 20 < time()) {
|
|
// We must update otherwise browser would time out.
|
|
} else if (round($this->percent, 2) === round($percent, 2)) {
|
|
// No significant change, no need to update anything.
|
|
return;
|
|
}
|
|
|
|
$estimatemsg = '';
|
|
if ($estimate != 0 && is_numeric($estimate)) {
|
|
// Err on the conservative side and also avoid showing 'now' as the estimate.
|
|
$estimatemsg = format_time(ceil($estimate));
|
|
}
|
|
|
|
$this->percent = $percent;
|
|
$this->lastupdate = microtime(true);
|
|
|
|
echo $OUTPUT->render_progress_bar_update($this->html_id, $this->percent, $msg, $estimatemsg);
|
|
flush();
|
|
}
|
|
|
|
/**
|
|
* Estimate how much time it is going to take.
|
|
*
|
|
* @param int $pt From 1-100.
|
|
* @return mixed Null (unknown), or int.
|
|
*/
|
|
private function estimate($pt) {
|
|
if ($this->lastupdate == 0) {
|
|
return null;
|
|
}
|
|
if ($pt < 0.00001) {
|
|
return null; // We do not know yet how long it will take.
|
|
}
|
|
if ($pt > 99.99999) {
|
|
return 0; // Nearly done, right?
|
|
}
|
|
$consumed = microtime(true) - $this->time_start;
|
|
if ($consumed < 0.001) {
|
|
return null;
|
|
}
|
|
|
|
return (100 - $pt) * ($consumed / $pt);
|
|
}
|
|
|
|
/**
|
|
* Update progress bar according percent.
|
|
*
|
|
* @param int $percent From 1-100.
|
|
* @param string $msg The message needed to be shown.
|
|
*/
|
|
public function update_full($percent, $msg) {
|
|
$percent = max(min($percent, 100), 0);
|
|
$this->_update($percent, $msg);
|
|
}
|
|
|
|
/**
|
|
* Update progress bar according the number of tasks.
|
|
*
|
|
* @param int $cur Current task number.
|
|
* @param int $total Total task number.
|
|
* @param string $msg The message needed to be shown.
|
|
*/
|
|
public function update($cur, $total, $msg) {
|
|
$percent = ($cur / $total) * 100;
|
|
$this->update_full($percent, $msg);
|
|
}
|
|
|
|
/**
|
|
* Restart the progress bar.
|
|
*/
|
|
public function restart() {
|
|
$this->percent = 0;
|
|
$this->lastupdate = 0;
|
|
$this->time_start = 0;
|
|
}
|
|
|
|
/**
|
|
* Export for template.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return array
|
|
*/
|
|
public function export_for_template(renderer_base $output) {
|
|
return [
|
|
'id' => $this->html_id,
|
|
'width' => $this->width,
|
|
];
|
|
}
|
|
}
|