PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/outputcomponents.php

https://bitbucket.org/moodle/moodle
PHP | 5038 lines | 2308 code | 563 blank | 2167 comment | 393 complexity | f477a455f86c0ed8bbb300edae5fcfda MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Classes representing HTML elements, used by $OUTPUT methods
  18. *
  19. * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
  20. * for an overview.
  21. *
  22. * @package core
  23. * @category output
  24. * @copyright 2009 Tim Hunt
  25. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26. */
  27. defined('MOODLE_INTERNAL') || die();
  28. /**
  29. * Interface marking other classes as suitable for renderer_base::render()
  30. *
  31. * @copyright 2010 Petr Skoda (skodak) info@skodak.org
  32. * @package core
  33. * @category output
  34. */
  35. interface renderable {
  36. // intentionally empty
  37. }
  38. /**
  39. * Interface marking other classes having the ability to export their data for use by templates.
  40. *
  41. * @copyright 2015 Damyon Wiese
  42. * @package core
  43. * @category output
  44. * @since 2.9
  45. */
  46. interface templatable {
  47. /**
  48. * Function to export the renderer data in a format that is suitable for a
  49. * mustache template. This means:
  50. * 1. No complex types - only stdClass, array, int, string, float, bool
  51. * 2. Any additional info that is required for the template is pre-calculated (e.g. capability checks).
  52. *
  53. * @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
  54. * @return stdClass|array
  55. */
  56. public function export_for_template(renderer_base $output);
  57. }
  58. /**
  59. * Data structure representing a file picker.
  60. *
  61. * @copyright 2010 Dongsheng Cai
  62. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  63. * @since Moodle 2.0
  64. * @package core
  65. * @category output
  66. */
  67. class file_picker implements renderable {
  68. /**
  69. * @var stdClass An object containing options for the file picker
  70. */
  71. public $options;
  72. /**
  73. * Constructs a file picker object.
  74. *
  75. * The following are possible options for the filepicker:
  76. * - accepted_types (*)
  77. * - return_types (FILE_INTERNAL)
  78. * - env (filepicker)
  79. * - client_id (uniqid)
  80. * - itemid (0)
  81. * - maxbytes (-1)
  82. * - maxfiles (1)
  83. * - buttonname (false)
  84. *
  85. * @param stdClass $options An object containing options for the file picker.
  86. */
  87. public function __construct(stdClass $options) {
  88. global $CFG, $USER, $PAGE;
  89. require_once($CFG->dirroot. '/repository/lib.php');
  90. $defaults = array(
  91. 'accepted_types'=>'*',
  92. 'return_types'=>FILE_INTERNAL,
  93. 'env' => 'filepicker',
  94. 'client_id' => uniqid(),
  95. 'itemid' => 0,
  96. 'maxbytes'=>-1,
  97. 'maxfiles'=>1,
  98. 'buttonname'=>false
  99. );
  100. foreach ($defaults as $key=>$value) {
  101. if (empty($options->$key)) {
  102. $options->$key = $value;
  103. }
  104. }
  105. $options->currentfile = '';
  106. if (!empty($options->itemid)) {
  107. $fs = get_file_storage();
  108. $usercontext = context_user::instance($USER->id);
  109. if (empty($options->filename)) {
  110. if ($files = $fs->get_area_files($usercontext->id, 'user', 'draft', $options->itemid, 'id DESC', false)) {
  111. $file = reset($files);
  112. }
  113. } else {
  114. $file = $fs->get_file($usercontext->id, 'user', 'draft', $options->itemid, $options->filepath, $options->filename);
  115. }
  116. if (!empty($file)) {
  117. $options->currentfile = html_writer::link(moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()), $file->get_filename());
  118. }
  119. }
  120. // initilise options, getting files in root path
  121. $this->options = initialise_filepicker($options);
  122. // copying other options
  123. foreach ($options as $name=>$value) {
  124. if (!isset($this->options->$name)) {
  125. $this->options->$name = $value;
  126. }
  127. }
  128. }
  129. }
  130. /**
  131. * Data structure representing a user picture.
  132. *
  133. * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
  134. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  135. * @since Modle 2.0
  136. * @package core
  137. * @category output
  138. */
  139. class user_picture implements renderable {
  140. /**
  141. * @var stdClass A user object with at least fields all columns specified
  142. * in $fields array constant set.
  143. */
  144. public $user;
  145. /**
  146. * @var int The course id. Used when constructing the link to the user's
  147. * profile, page course id used if not specified.
  148. */
  149. public $courseid;
  150. /**
  151. * @var bool Add course profile link to image
  152. */
  153. public $link = true;
  154. /**
  155. * @var int Size in pixels. Special values are (true/1 = 100px) and
  156. * (false/0 = 35px)
  157. * for backward compatibility.
  158. */
  159. public $size = 35;
  160. /**
  161. * @var bool Add non-blank alt-text to the image.
  162. * Default true, set to false when image alt just duplicates text in screenreaders.
  163. */
  164. public $alttext = true;
  165. /**
  166. * @var bool Whether or not to open the link in a popup window.
  167. */
  168. public $popup = false;
  169. /**
  170. * @var string Image class attribute
  171. */
  172. public $class = 'userpicture';
  173. /**
  174. * @var bool Whether to be visible to screen readers.
  175. */
  176. public $visibletoscreenreaders = true;
  177. /**
  178. * @var bool Whether to include the fullname in the user picture link.
  179. */
  180. public $includefullname = false;
  181. /**
  182. * @var mixed Include user authentication token. True indicates to generate a token for current user, and integer value
  183. * indicates to generate a token for the user whose id is the value indicated.
  184. */
  185. public $includetoken = false;
  186. /**
  187. * User picture constructor.
  188. *
  189. * @param stdClass $user user record with at least id, picture, imagealt, firstname and lastname set.
  190. * It is recommended to add also contextid of the user for performance reasons.
  191. */
  192. public function __construct(stdClass $user) {
  193. global $DB;
  194. if (empty($user->id)) {
  195. throw new coding_exception('User id is required when printing user avatar image.');
  196. }
  197. // only touch the DB if we are missing data and complain loudly...
  198. $needrec = false;
  199. foreach (\core_user\fields::get_picture_fields() as $field) {
  200. if (!property_exists($user, $field)) {
  201. $needrec = true;
  202. debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
  203. .'Please use the \core_user\fields API to get the full list of required fields.', DEBUG_DEVELOPER);
  204. break;
  205. }
  206. }
  207. if ($needrec) {
  208. $this->user = $DB->get_record('user', array('id' => $user->id),
  209. implode(',', \core_user\fields::get_picture_fields()), MUST_EXIST);
  210. } else {
  211. $this->user = clone($user);
  212. }
  213. }
  214. /**
  215. * Returns a list of required user fields, useful when fetching required user info from db.
  216. *
  217. * In some cases we have to fetch the user data together with some other information,
  218. * the idalias is useful there because the id would otherwise override the main
  219. * id of the result record. Please note it has to be converted back to id before rendering.
  220. *
  221. * @param string $tableprefix name of database table prefix in query
  222. * @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)
  223. * @param string $idalias alias of id field
  224. * @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id'
  225. * @return string
  226. * @deprecated since Moodle 3.11 MDL-45242
  227. * @see \core_user\fields
  228. */
  229. public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') {
  230. debugging('user_picture::fields() is deprecated. Please use the \core_user\fields API instead.', DEBUG_DEVELOPER);
  231. $userfields = \core_user\fields::for_userpic();
  232. if ($extrafields) {
  233. $userfields->including(...$extrafields);
  234. }
  235. $selects = $userfields->get_sql($tableprefix, false, $fieldprefix, $idalias, false)->selects;
  236. if ($tableprefix === '') {
  237. // If no table alias is specified, don't add {user}. in front of fields.
  238. $selects = str_replace('{user}.', '', $selects);
  239. }
  240. // Maintain legacy behaviour where the field list was done with 'implode' and no spaces.
  241. $selects = str_replace(', ', ',', $selects);
  242. return $selects;
  243. }
  244. /**
  245. * Extract the aliased user fields from a given record
  246. *
  247. * Given a record that was previously obtained using {@link self::fields()} with aliases,
  248. * this method extracts user related unaliased fields.
  249. *
  250. * @param stdClass $record containing user picture fields
  251. * @param array $extrafields extra fields included in the $record
  252. * @param string $idalias alias of the id field
  253. * @param string $fieldprefix prefix added to all columns in their aliases, does not apply to 'id'
  254. * @return stdClass object with unaliased user fields
  255. */
  256. public static function unalias(stdClass $record, array $extrafields = null, $idalias = 'id', $fieldprefix = '') {
  257. if (empty($idalias)) {
  258. $idalias = 'id';
  259. }
  260. $return = new stdClass();
  261. foreach (\core_user\fields::get_picture_fields() as $field) {
  262. if ($field === 'id') {
  263. if (property_exists($record, $idalias)) {
  264. $return->id = $record->{$idalias};
  265. }
  266. } else {
  267. if (property_exists($record, $fieldprefix.$field)) {
  268. $return->{$field} = $record->{$fieldprefix.$field};
  269. }
  270. }
  271. }
  272. // add extra fields if not already there
  273. if ($extrafields) {
  274. foreach ($extrafields as $e) {
  275. if ($e === 'id' or property_exists($return, $e)) {
  276. continue;
  277. }
  278. $return->{$e} = $record->{$fieldprefix.$e};
  279. }
  280. }
  281. return $return;
  282. }
  283. /**
  284. * Works out the URL for the users picture.
  285. *
  286. * This method is recommended as it avoids costly redirects of user pictures
  287. * if requests are made for non-existent files etc.
  288. *
  289. * @param moodle_page $page
  290. * @param renderer_base $renderer
  291. * @return moodle_url
  292. */
  293. public function get_url(moodle_page $page, renderer_base $renderer = null) {
  294. global $CFG;
  295. if (is_null($renderer)) {
  296. $renderer = $page->get_renderer('core');
  297. }
  298. // Sort out the filename and size. Size is only required for the gravatar
  299. // implementation presently.
  300. if (empty($this->size)) {
  301. $filename = 'f2';
  302. $size = 35;
  303. } else if ($this->size === true or $this->size == 1) {
  304. $filename = 'f1';
  305. $size = 100;
  306. } else if ($this->size > 100) {
  307. $filename = 'f3';
  308. $size = (int)$this->size;
  309. } else if ($this->size >= 50) {
  310. $filename = 'f1';
  311. $size = (int)$this->size;
  312. } else {
  313. $filename = 'f2';
  314. $size = (int)$this->size;
  315. }
  316. $defaulturl = $renderer->image_url('u/'.$filename); // default image
  317. if ((!empty($CFG->forcelogin) and !isloggedin()) ||
  318. (!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) {
  319. // Protect images if login required and not logged in;
  320. // also if login is required for profile images and is not logged in or guest
  321. // do not use require_login() because it is expensive and not suitable here anyway.
  322. return $defaulturl;
  323. }
  324. // First try to detect deleted users - but do not read from database for performance reasons!
  325. if (!empty($this->user->deleted) or strpos($this->user->email, '@') === false) {
  326. // All deleted users should have email replaced by md5 hash,
  327. // all active users are expected to have valid email.
  328. return $defaulturl;
  329. }
  330. // Did the user upload a picture?
  331. if ($this->user->picture > 0) {
  332. if (!empty($this->user->contextid)) {
  333. $contextid = $this->user->contextid;
  334. } else {
  335. $context = context_user::instance($this->user->id, IGNORE_MISSING);
  336. if (!$context) {
  337. // This must be an incorrectly deleted user, all other users have context.
  338. return $defaulturl;
  339. }
  340. $contextid = $context->id;
  341. }
  342. $path = '/';
  343. if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) {
  344. // We append the theme name to the file path if we have it so that
  345. // in the circumstance that the profile picture is not available
  346. // when the user actually requests it they still get the profile
  347. // picture for the correct theme.
  348. $path .= $page->theme->name.'/';
  349. }
  350. // Set the image URL to the URL for the uploaded file and return.
  351. $url = moodle_url::make_pluginfile_url(
  352. $contextid, 'user', 'icon', null, $path, $filename, false, $this->includetoken);
  353. $url->param('rev', $this->user->picture);
  354. return $url;
  355. }
  356. if ($this->user->picture == 0 and !empty($CFG->enablegravatar)) {
  357. // Normalise the size variable to acceptable bounds
  358. if ($size < 1 || $size > 512) {
  359. $size = 35;
  360. }
  361. // Hash the users email address
  362. $md5 = md5(strtolower(trim($this->user->email)));
  363. // Build a gravatar URL with what we know.
  364. // Find the best default image URL we can (MDL-35669)
  365. if (empty($CFG->gravatardefaulturl)) {
  366. $absoluteimagepath = $page->theme->resolve_image_location('u/'.$filename, 'core');
  367. if (strpos($absoluteimagepath, $CFG->dirroot) === 0) {
  368. $gravatardefault = $CFG->wwwroot . substr($absoluteimagepath, strlen($CFG->dirroot));
  369. } else {
  370. $gravatardefault = $CFG->wwwroot . '/pix/u/' . $filename . '.png';
  371. }
  372. } else {
  373. $gravatardefault = $CFG->gravatardefaulturl;
  374. }
  375. // If the currently requested page is https then we'll return an
  376. // https gravatar page.
  377. if (is_https()) {
  378. return new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault));
  379. } else {
  380. return new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault));
  381. }
  382. }
  383. return $defaulturl;
  384. }
  385. }
  386. /**
  387. * Data structure representing a help icon.
  388. *
  389. * @copyright 2010 Petr Skoda (info@skodak.org)
  390. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  391. * @since Moodle 2.0
  392. * @package core
  393. * @category output
  394. */
  395. class help_icon implements renderable, templatable {
  396. /**
  397. * @var string lang pack identifier (without the "_help" suffix),
  398. * both get_string($identifier, $component) and get_string($identifier.'_help', $component)
  399. * must exist.
  400. */
  401. public $identifier;
  402. /**
  403. * @var string Component name, the same as in get_string()
  404. */
  405. public $component;
  406. /**
  407. * @var string Extra descriptive text next to the icon
  408. */
  409. public $linktext = null;
  410. /**
  411. * Constructor
  412. *
  413. * @param string $identifier string for help page title,
  414. * string with _help suffix is used for the actual help text.
  415. * string with _link suffix is used to create a link to further info (if it exists)
  416. * @param string $component
  417. */
  418. public function __construct($identifier, $component) {
  419. $this->identifier = $identifier;
  420. $this->component = $component;
  421. }
  422. /**
  423. * Verifies that both help strings exists, shows debug warnings if not
  424. */
  425. public function diag_strings() {
  426. $sm = get_string_manager();
  427. if (!$sm->string_exists($this->identifier, $this->component)) {
  428. debugging("Help title string does not exist: [$this->identifier, $this->component]");
  429. }
  430. if (!$sm->string_exists($this->identifier.'_help', $this->component)) {
  431. debugging("Help contents string does not exist: [{$this->identifier}_help, $this->component]");
  432. }
  433. }
  434. /**
  435. * Export this data so it can be used as the context for a mustache template.
  436. *
  437. * @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
  438. * @return array
  439. */
  440. public function export_for_template(renderer_base $output) {
  441. global $CFG;
  442. $title = get_string($this->identifier, $this->component);
  443. if (empty($this->linktext)) {
  444. $alt = get_string('helpprefix2', '', trim($title, ". \t"));
  445. } else {
  446. $alt = get_string('helpwiththis');
  447. }
  448. $data = get_formatted_help_string($this->identifier, $this->component, false);
  449. $data->alt = $alt;
  450. $data->icon = (new pix_icon('help', $alt, 'core', ['class' => 'iconhelp']))->export_for_template($output);
  451. $data->linktext = $this->linktext;
  452. $data->title = get_string('helpprefix2', '', trim($title, ". \t"));
  453. $options = [
  454. 'component' => $this->component,
  455. 'identifier' => $this->identifier,
  456. 'lang' => current_language()
  457. ];
  458. // Debugging feature lets you display string identifier and component.
  459. if (isset($CFG->debugstringids) && $CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) {
  460. $options['strings'] = 1;
  461. }
  462. $data->url = (new moodle_url('/help.php', $options))->out(false);
  463. $data->ltr = !right_to_left();
  464. return $data;
  465. }
  466. }
  467. /**
  468. * Data structure representing an icon font.
  469. *
  470. * @copyright 2016 Damyon Wiese
  471. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  472. * @package core
  473. * @category output
  474. */
  475. class pix_icon_font implements templatable {
  476. /**
  477. * @var pix_icon $pixicon The original icon.
  478. */
  479. private $pixicon = null;
  480. /**
  481. * @var string $key The mapped key.
  482. */
  483. private $key;
  484. /**
  485. * @var bool $mapped The icon could not be mapped.
  486. */
  487. private $mapped;
  488. /**
  489. * Constructor
  490. *
  491. * @param pix_icon $pixicon The original icon
  492. */
  493. public function __construct(pix_icon $pixicon) {
  494. global $PAGE;
  495. $this->pixicon = $pixicon;
  496. $this->mapped = false;
  497. $iconsystem = \core\output\icon_system::instance();
  498. $this->key = $iconsystem->remap_icon_name($pixicon->pix, $pixicon->component);
  499. if (!empty($this->key)) {
  500. $this->mapped = true;
  501. }
  502. }
  503. /**
  504. * Return true if this pix_icon was successfully mapped to an icon font.
  505. *
  506. * @return bool
  507. */
  508. public function is_mapped() {
  509. return $this->mapped;
  510. }
  511. /**
  512. * Export this data so it can be used as the context for a mustache template.
  513. *
  514. * @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
  515. * @return array
  516. */
  517. public function export_for_template(renderer_base $output) {
  518. $pixdata = $this->pixicon->export_for_template($output);
  519. $title = isset($this->pixicon->attributes['title']) ? $this->pixicon->attributes['title'] : '';
  520. $alt = isset($this->pixicon->attributes['alt']) ? $this->pixicon->attributes['alt'] : '';
  521. if (empty($title)) {
  522. $title = $alt;
  523. }
  524. $data = array(
  525. 'extraclasses' => $pixdata['extraclasses'],
  526. 'title' => $title,
  527. 'alt' => $alt,
  528. 'key' => $this->key
  529. );
  530. return $data;
  531. }
  532. }
  533. /**
  534. * Data structure representing an icon subtype.
  535. *
  536. * @copyright 2016 Damyon Wiese
  537. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  538. * @package core
  539. * @category output
  540. */
  541. class pix_icon_fontawesome extends pix_icon_font {
  542. }
  543. /**
  544. * Data structure representing an icon.
  545. *
  546. * @copyright 2010 Petr Skoda
  547. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  548. * @since Moodle 2.0
  549. * @package core
  550. * @category output
  551. */
  552. class pix_icon implements renderable, templatable {
  553. /**
  554. * @var string The icon name
  555. */
  556. var $pix;
  557. /**
  558. * @var string The component the icon belongs to.
  559. */
  560. var $component;
  561. /**
  562. * @var array An array of attributes to use on the icon
  563. */
  564. var $attributes = array();
  565. /**
  566. * Constructor
  567. *
  568. * @param string $pix short icon name
  569. * @param string $alt The alt text to use for the icon
  570. * @param string $component component name
  571. * @param array $attributes html attributes
  572. */
  573. public function __construct($pix, $alt, $component='moodle', array $attributes = null) {
  574. global $PAGE;
  575. $this->pix = $pix;
  576. $this->component = $component;
  577. $this->attributes = (array)$attributes;
  578. if (empty($this->attributes['class'])) {
  579. $this->attributes['class'] = '';
  580. }
  581. // Set an additional class for big icons so that they can be styled properly.
  582. if (substr($pix, 0, 2) === 'b/') {
  583. $this->attributes['class'] .= ' iconsize-big';
  584. }
  585. // If the alt is empty, don't place it in the attributes, otherwise it will override parent alt text.
  586. if (!is_null($alt)) {
  587. $this->attributes['alt'] = $alt;
  588. // If there is no title, set it to the attribute.
  589. if (!isset($this->attributes['title'])) {
  590. $this->attributes['title'] = $this->attributes['alt'];
  591. }
  592. } else {
  593. unset($this->attributes['alt']);
  594. }
  595. if (empty($this->attributes['title'])) {
  596. // Remove the title attribute if empty, we probably want to use the parent node's title
  597. // and some browsers might overwrite it with an empty title.
  598. unset($this->attributes['title']);
  599. }
  600. // Hide icons from screen readers that have no alt.
  601. if (empty($this->attributes['alt'])) {
  602. $this->attributes['aria-hidden'] = 'true';
  603. }
  604. }
  605. /**
  606. * Export this data so it can be used as the context for a mustache template.
  607. *
  608. * @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
  609. * @return array
  610. */
  611. public function export_for_template(renderer_base $output) {
  612. $attributes = $this->attributes;
  613. $extraclasses = '';
  614. foreach ($attributes as $key => $item) {
  615. if ($key == 'class') {
  616. $extraclasses = $item;
  617. unset($attributes[$key]);
  618. break;
  619. }
  620. }
  621. $attributes['src'] = $output->image_url($this->pix, $this->component)->out(false);
  622. $templatecontext = array();
  623. foreach ($attributes as $name => $value) {
  624. $templatecontext[] = array('name' => $name, 'value' => $value);
  625. }
  626. $title = isset($attributes['title']) ? $attributes['title'] : '';
  627. if (empty($title)) {
  628. $title = isset($attributes['alt']) ? $attributes['alt'] : '';
  629. }
  630. $data = array(
  631. 'attributes' => $templatecontext,
  632. 'extraclasses' => $extraclasses
  633. );
  634. return $data;
  635. }
  636. /**
  637. * Much simpler version of export that will produce the data required to render this pix with the
  638. * pix helper in a mustache tag.
  639. *
  640. * @return array
  641. */
  642. public function export_for_pix() {
  643. $title = isset($this->attributes['title']) ? $this->attributes['title'] : '';
  644. if (empty($title)) {
  645. $title = isset($this->attributes['alt']) ? $this->attributes['alt'] : '';
  646. }
  647. return [
  648. 'key' => $this->pix,
  649. 'component' => $this->component,
  650. 'title' => (string) $title,
  651. ];
  652. }
  653. }
  654. /**
  655. * Data structure representing an activity icon.
  656. *
  657. * The difference is that activity icons will always render with the standard icon system (no font icons).
  658. *
  659. * @copyright 2017 Damyon Wiese
  660. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  661. * @package core
  662. */
  663. class image_icon extends pix_icon {
  664. }
  665. /**
  666. * Data structure representing an emoticon image
  667. *
  668. * @copyright 2010 David Mudrak
  669. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  670. * @since Moodle 2.0
  671. * @package core
  672. * @category output
  673. */
  674. class pix_emoticon extends pix_icon implements renderable {
  675. /**
  676. * Constructor
  677. * @param string $pix short icon name
  678. * @param string $alt alternative text
  679. * @param string $component emoticon image provider
  680. * @param array $attributes explicit HTML attributes
  681. */
  682. public function __construct($pix, $alt, $component = 'moodle', array $attributes = array()) {
  683. if (empty($attributes['class'])) {
  684. $attributes['class'] = 'emoticon';
  685. }
  686. parent::__construct($pix, $alt, $component, $attributes);
  687. }
  688. }
  689. /**
  690. * Data structure representing a simple form with only one button.
  691. *
  692. * @copyright 2009 Petr Skoda
  693. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  694. * @since Moodle 2.0
  695. * @package core
  696. * @category output
  697. */
  698. class single_button implements renderable {
  699. /**
  700. * @var moodle_url Target url
  701. */
  702. public $url;
  703. /**
  704. * @var string Button label
  705. */
  706. public $label;
  707. /**
  708. * @var string Form submit method post or get
  709. */
  710. public $method = 'post';
  711. /**
  712. * @var string Wrapping div class
  713. */
  714. public $class = 'singlebutton';
  715. /**
  716. * @var bool True if button is primary button. Used for styling.
  717. */
  718. public $primary = false;
  719. /**
  720. * @var bool True if button disabled, false if normal
  721. */
  722. public $disabled = false;
  723. /**
  724. * @var string Button tooltip
  725. */
  726. public $tooltip = null;
  727. /**
  728. * @var string Form id
  729. */
  730. public $formid;
  731. /**
  732. * @var array List of attached actions
  733. */
  734. public $actions = array();
  735. /**
  736. * @var array $params URL Params
  737. */
  738. public $params;
  739. /**
  740. * @var string Action id
  741. */
  742. public $actionid;
  743. /**
  744. * @var array
  745. */
  746. protected $attributes = [];
  747. /**
  748. * Constructor
  749. * @param moodle_url $url
  750. * @param string $label button text
  751. * @param string $method get or post submit method
  752. * @param bool $primary whether this is a primary button, used for styling
  753. * @param array $attributes Attributes for the HTML button tag
  754. */
  755. public function __construct(moodle_url $url, $label, $method='post', $primary=false, $attributes = []) {
  756. $this->url = clone($url);
  757. $this->label = $label;
  758. $this->method = $method;
  759. $this->primary = $primary;
  760. $this->attributes = $attributes;
  761. }
  762. /**
  763. * Shortcut for adding a JS confirm dialog when the button is clicked.
  764. * The message must be a yes/no question.
  765. *
  766. * @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
  767. */
  768. public function add_confirm_action($confirmmessage) {
  769. $this->add_action(new confirm_action($confirmmessage));
  770. }
  771. /**
  772. * Add action to the button.
  773. * @param component_action $action
  774. */
  775. public function add_action(component_action $action) {
  776. $this->actions[] = $action;
  777. }
  778. /**
  779. * Sets an attribute for the HTML button tag.
  780. *
  781. * @param string $name The attribute name
  782. * @param mixed $value The value
  783. * @return null
  784. */
  785. public function set_attribute($name, $value) {
  786. $this->attributes[$name] = $value;
  787. }
  788. /**
  789. * Export data.
  790. *
  791. * @param renderer_base $output Renderer.
  792. * @return stdClass
  793. */
  794. public function export_for_template(renderer_base $output) {
  795. $url = $this->method === 'get' ? $this->url->out_omit_querystring(true) : $this->url->out_omit_querystring();
  796. $data = new stdClass();
  797. $data->id = html_writer::random_id('single_button');
  798. $data->formid = $this->formid;
  799. $data->method = $this->method;
  800. $data->url = $url === '' ? '#' : $url;
  801. $data->label = $this->label;
  802. $data->classes = $this->class;
  803. $data->disabled = $this->disabled;
  804. $data->tooltip = $this->tooltip;
  805. $data->primary = $this->primary;
  806. $data->attributes = [];
  807. foreach ($this->attributes as $key => $value) {
  808. $data->attributes[] = ['name' => $key, 'value' => $value];
  809. }
  810. // Form parameters.
  811. $params = $this->url->params();
  812. if ($this->method === 'post') {
  813. $params['sesskey'] = sesskey();
  814. }
  815. $data->params = array_map(function($key) use ($params) {
  816. return ['name' => $key, 'value' => $params[$key]];
  817. }, array_keys($params));
  818. // Button actions.
  819. $actions = $this->actions;
  820. $data->actions = array_map(function($action) use ($output) {
  821. return $action->export_for_template($output);
  822. }, $actions);
  823. $data->hasactions = !empty($data->actions);
  824. return $data;
  825. }
  826. }
  827. /**
  828. * Simple form with just one select field that gets submitted automatically.
  829. *
  830. * If JS not enabled small go button is printed too.
  831. *
  832. * @copyright 2009 Petr Skoda
  833. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  834. * @since Moodle 2.0
  835. * @package core
  836. * @category output
  837. */
  838. class single_select implements renderable, templatable {
  839. /**
  840. * @var moodle_url Target url - includes hidden fields
  841. */
  842. var $url;
  843. /**
  844. * @var string Name of the select element.
  845. */
  846. var $name;
  847. /**
  848. * @var array $options associative array value=>label ex.: array(1=>'One, 2=>Two)
  849. * it is also possible to specify optgroup as complex label array ex.:
  850. * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
  851. * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
  852. */
  853. var $options;
  854. /**
  855. * @var string Selected option
  856. */
  857. var $selected;
  858. /**
  859. * @var array Nothing selected
  860. */
  861. var $nothing;
  862. /**
  863. * @var array Extra select field attributes
  864. */
  865. var $attributes = array();
  866. /**
  867. * @var string Button label
  868. */
  869. var $label = '';
  870. /**
  871. * @var array Button label's attributes
  872. */
  873. var $labelattributes = array();
  874. /**
  875. * @var string Form submit method post or get
  876. */
  877. var $method = 'get';
  878. /**
  879. * @var string Wrapping div class
  880. */
  881. var $class = 'singleselect';
  882. /**
  883. * @var bool True if button disabled, false if normal
  884. */
  885. var $disabled = false;
  886. /**
  887. * @var string Button tooltip
  888. */
  889. var $tooltip = null;
  890. /**
  891. * @var string Form id
  892. */
  893. var $formid = null;
  894. /**
  895. * @var help_icon The help icon for this element.
  896. */
  897. var $helpicon = null;
  898. /**
  899. * Constructor
  900. * @param moodle_url $url form action target, includes hidden fields
  901. * @param string $name name of selection field - the changing parameter in url
  902. * @param array $options list of options
  903. * @param string $selected selected element
  904. * @param array $nothing
  905. * @param string $formid
  906. */
  907. public function __construct(moodle_url $url, $name, array $options, $selected = '', $nothing = array('' => 'choosedots'), $formid = null) {
  908. $this->url = $url;
  909. $this->name = $name;
  910. $this->options = $options;
  911. $this->selected = $selected;
  912. $this->nothing = $nothing;
  913. $this->formid = $formid;
  914. }
  915. /**
  916. * Shortcut for adding a JS confirm dialog when the button is clicked.
  917. * The message must be a yes/no question.
  918. *
  919. * @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
  920. */
  921. public function add_confirm_action($confirmmessage) {
  922. $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
  923. }
  924. /**
  925. * Add action to the button.
  926. *
  927. * @param component_action $action
  928. */
  929. public function add_action(component_action $action) {
  930. $this->actions[] = $action;
  931. }
  932. /**
  933. * Adds help icon.
  934. *
  935. * @deprecated since Moodle 2.0
  936. */
  937. public function set_old_help_icon($helppage, $title, $component = 'moodle') {
  938. throw new coding_exception('set_old_help_icon() can not be used any more, please see set_help_icon().');
  939. }
  940. /**
  941. * Adds help icon.
  942. *
  943. * @param string $identifier The keyword that defines a help page
  944. * @param string $component
  945. */
  946. public function set_help_icon($identifier, $component = 'moodle') {
  947. $this->helpicon = new help_icon($identifier, $component);
  948. }
  949. /**
  950. * Sets select's label
  951. *
  952. * @param string $label
  953. * @param array $attributes (optional)
  954. */
  955. public function set_label($label, $attributes = array()) {
  956. $this->label = $label;
  957. $this->labelattributes = $attributes;
  958. }
  959. /**
  960. * Export data.
  961. *
  962. * @param renderer_base $output Renderer.
  963. * @return stdClass
  964. */
  965. public function export_for_template(renderer_base $output) {
  966. $attributes = $this->attributes;
  967. $data = new stdClass();
  968. $data->name = $this->name;
  969. $data->method = $this->method;
  970. $data->action = $this->method === 'get' ? $this->url->out_omit_querystring(true) : $this->url->out_omit_querystring();
  971. $data->classes = $this->class;
  972. $data->label = $this->label;
  973. $data->disabled = $this->disabled;
  974. $data->title = $this->tooltip;
  975. $data->formid = !empty($this->formid) ? $this->formid : html_writer::random_id('single_select_f');
  976. $data->id = !empty($attributes['id']) ? $attributes['id'] : html_writer::random_id('single_select');
  977. // Select element attributes.
  978. // Unset attributes that are already predefined in the template.
  979. unset($attributes['id']);
  980. unset($attributes['class']);
  981. unset($attributes['name']);
  982. unset($attributes['title']);
  983. unset($attributes['disabled']);
  984. // Map the attributes.
  985. $data->attributes = array_map(function($key) use ($attributes) {
  986. return ['name' => $key, 'value' => $attributes[$key]];
  987. }, array_keys($attributes));
  988. // Form parameters.
  989. $params = $this->url->params();
  990. if ($this->method === 'post') {
  991. $params['sesskey'] = sesskey();
  992. }
  993. $data->params = array_map(function($key) use ($params) {
  994. return ['name' => $key, 'value' => $params[$key]];
  995. }, array_keys($params));
  996. // Select options.
  997. $hasnothing = false;
  998. if (is_string($this->nothing) && $this->nothing !== '') {
  999. $nothing = ['' => $this->nothing];
  1000. $hasnothing = true;
  1001. $nothingkey = '';
  1002. } else if (is_array($this->nothing)) {
  1003. $nothingvalue = reset($this->nothing);
  1004. if ($nothingvalue === 'choose' || $nothingvalue === 'choosedots') {
  1005. $nothing = [key($this->nothing) => get_string('choosedots')];
  1006. } else {
  1007. $nothing = $this->nothing;
  1008. }
  1009. $hasnothing = true;
  1010. $nothingkey = key($this->nothing);
  1011. }
  1012. if ($hasnothing) {
  1013. $options = $nothing + $this->options;
  1014. } else {
  1015. $options = $this->options;
  1016. }
  1017. foreach ($options as $value => $name) {
  1018. if (is_array($options[$value])) {
  1019. foreach ($options[$value] as $optgroupname => $optgroupvalues) {
  1020. $sublist = [];
  1021. foreach ($optgroupvalues as $optvalue => $optname) {
  1022. $option = [
  1023. 'value' => $optvalue,
  1024. 'name' => $optname,
  1025. 'selected' => strval($this->selected) === strval($optvalue),
  1026. ];
  1027. if ($hasnothing && $nothingkey === $optvalue) {
  1028. $option['ignore'] = 'data-ignore';
  1029. }
  1030. $sublist[] = $option;
  1031. }
  1032. $data->options[] = [
  1033. 'name' => $optgroupname,
  1034. 'optgroup' => true,
  1035. 'options' => $sublist
  1036. ];
  1037. }
  1038. } else {
  1039. $option = [
  1040. 'value' => $value,
  1041. 'name' => $options[$value],
  1042. 'selected' => strval($this->selected) === strval($value),
  1043. 'optgroup' => false
  1044. ];
  1045. if ($hasnothing && $nothingkey === $value) {
  1046. $option['ignore'] = 'data-ignore';
  1047. }
  1048. $data->options[] = $option;
  1049. }
  1050. }
  1051. // Label attributes.
  1052. $data->labelattributes = [];
  1053. // Unset label attributes that are already in the template.
  1054. unset($this->labelattributes['for']);
  1055. // Map the label attributes.
  1056. foreach ($this->labelattributes as $key => $value) {
  1057. $data->labelattributes[] = ['name' => $key, 'value' => $value];
  1058. }
  1059. // Help icon.
  1060. $data->helpicon = !empty($this->helpicon) ? $this->helpicon->export_for_template($output) : false;
  1061. return $data;
  1062. }
  1063. }
  1064. /**
  1065. * Simple URL selection widget description.
  1066. *
  1067. * @copyright 2009 Petr Skoda
  1068. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1069. * @since Moodle 2.0
  1070. * @package core
  1071. * @category output
  1072. */
  1073. class url_select implements renderable, templatable {
  1074. /**
  1075. * @var array $urls associative array value=>label ex.: array(1=>'One, 2=>Two)
  1076. * it is also possible to specify optgroup as complex label array ex.:
  1077. * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
  1078. * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
  1079. */
  1080. var $urls;
  1081. /**
  1082. * @var string Selected option
  1083. */
  1084. var $selected;
  1085. /**
  1086. * @var array Nothing selected
  1087. */
  1088. var $nothing;
  1089. /**
  1090. * @var array Extra select field attributes
  1091. */
  1092. var $attributes = array();
  1093. /**
  1094. * @var string Button label
  1095. */
  1096. var $label = '';
  1097. /**
  1098. * @var array Button label's attributes
  1099. */
  1100. var $labelattributes = array();
  1101. /**
  1102. * @var string Wrapping div class
  1103. */
  1104. var $class = 'urlselect';
  1105. /**
  1106. * @var bool True if button disabled, false if normal
  1107. */
  1108. var $disabled = false;
  1109. /**
  1110. * @var string Button tooltip
  1111. */
  1112. var $tooltip = null;
  1113. /**
  1114. * @var string Form id
  1115. */
  1116. var $formid = null;
  1117. /**
  1118. * @var help_icon The help icon for this element.
  1119. */
  1120. var $helpicon = null;
  1121. /**
  1122. * @var string If set, makes button visible with given name for button
  1123. */
  1124. var $showbutton = null;
  1125. /**
  1126. * Constructor
  1127. * @param array $urls list of options
  1128. * @param string $selected selected element
  1129. * @param array $nothing
  1130. * @param string $formid
  1131. * @param string $showbutton Set to text of button if it should be visible
  1132. * or null if it should be hidden (hidden version always has text 'go')
  1133. */
  1134. public function __construct(array $urls, $selected = '', $nothing = array('' => 'choosedots'), $formid = null, $showbutton = null) {
  1135. $this->urls = $urls;
  1136. $this->selected = $selected;
  1137. $this->nothing = $nothing;
  1138. $this->formid = $formid;
  1139. $this->showbutton = $showbutton;
  1140. }
  1141. /**
  1142. * Adds help icon.
  1143. *
  1144. * @deprecated since Moodle 2.0
  1145. */
  1146. public function set_old_help_icon($helppage, $title, $component = 'moodle') {
  1147. throw new coding_exception('set_old_help_icon() can not be used any more, please see set_help_icon().');
  1148. }
  1149. /**
  1150. * Adds help icon.
  1151. *
  1152. * @param string $identifier The keyword that defines a help page
  1153. * @param string $component
  1154. */
  1155. public function set_help_icon($identifier, $component = 'moodle') {
  1156. $this->helpicon = new help_icon($identifier, $component);
  1157. }
  1158. /**
  1159. * Sets select's label
  1160. *
  1161. * @param string $label
  1162. * @param array $attributes (optional)
  1163. */
  1164. public function set_label($label, $attributes = array()) {
  1165. $this->label = $label;
  1166. $this->labelattributes = $attributes;
  1167. }
  1168. /**
  1169. * Clean a URL.
  1170. *
  1171. * @param string $value The URL.
  1172. * @return The cleaned URL.
  1173. */
  1174. protected function clean_url($value) {
  1175. global $CFG;
  1176. if (empty($value)) {
  1177. // Nothing.
  1178. } else if (strpos($value, $CFG->wwwroot . '/') === 0) {
  1179. $value = str_replace($CFG->wwwroot, '', $value);
  1180. } else if (strpos($value, '/') !== 0) {
  1181. debugging("Invalid url_select urls parameter: url '$value' is not local relative url!", DEBUG_DEVELOPER);
  1182. }
  1183. return $value;
  1184. }
  1185. /**
  1186. * Flatten the options for Mustache.
  1187. *
  1188. * This also cleans the URLs.
  1189. *
  1190. * @param array $options The options.
  1191. * @param array $nothing The nothing option.
  1192. * @return array
  1193. */
  1194. protected function flatten_options($options, $nothing) {
  1195. $flattened = [];
  1196. foreach ($options as $value => $option) {
  1197. if (is_array($option)) {
  1198. foreach ($option as $groupname => $optoptions) {
  1199. if (!isset($flattened[$groupname])) {
  1200. $flattened[$groupname] = [
  1201. 'name' => $groupname,
  1202. 'isgroup' => true,
  1203. 'options' => []
  1204. ];
  1205. }
  1206. foreach ($optoptions as $optvalue => $optoption) {
  1207. $cleanedvalue = $this->clean_url($optvalue);
  1208. $flattened[$groupname]['options'][$cleanedvalue] = [
  1209. 'name' => $optoption,
  1210. 'value' => $cleanedvalue,
  1211. 'selected' => $this->selected == $optvalue,
  1212. ];
  1213. }
  1214. }
  1215. } else {
  1216. $cleanedvalue = $this->clean_url($value);
  1217. $flattened[$cleanedvalue] = [
  1218. 'name' => $option,
  1219. 'value' => $cleanedvalue,
  1220. 'selected' => $this->selected == $value,
  1221. ];
  1222. }
  1223. }
  1224. if (!empty($nothing)) {
  1225. $value = key($nothing);
  1226. $name = reset($nothing);
  1227. $flattened = [
  1228. $value => ['name' => $name, 'value' => $value, 'selected' => $this->selected == $value]
  1229. ] + $flattened;
  1230. }
  1231. // Make non-associative array.
  1232. foreach ($flattened as $key => $value) {
  1233. if (!empty($value['options'])) {
  1234. $flattened[$key]['options'] = array_values($value['options']);
  1235. }
  1236. }
  1237. $flattened = array_values($flattened);
  1238. return $flattened;
  1239. }
  1240. /**
  1241. * Export for template.
  1242. *
  1243. * @param renderer_base $output Renderer.
  1244. * @return stdClass
  1245. */
  1246. public function export_for_template(renderer_base $output) {
  1247. $attributes = $this->attributes;
  1248. $data = new stdClass();
  1249. $data->formid = !empty($this->formid) ? $this->formid : html_writer::random_id('url_select_f');
  1250. $data->classes = $this->class;
  1251. $data->label = $this->label;
  1252. $data->disabled = $this->disabled;
  1253. $data->title = $this->tooltip;
  1254. $data->id = !empty($attributes['id']) ? $attributes['id'] : html_writer::random_id('url_select');
  1255. $data->sesskey = sesskey();
  1256. $data->action = (new moodle_url('/course/jumpto.php'))->out(false);
  1257. // Remove attributes passed as property directly.
  1258. unset($attributes['class']);
  1259. unset($attributes['id']);
  1260. unset($attributes['name']);
  1261. unset($attributes['title']);
  1262. unset($attributes['disabled']);
  1263. $data->showbutton = $this->showbutton;
  1264. // Select options.
  1265. $nothing = false;
  1266. if (is_string($this->nothing) && $this->nothing !== '') {
  1267. $nothing = ['' => $this->nothing];
  1268. } else if (is_array($this->nothing)) {
  1269. $nothingvalue = reset($this->nothing);
  1270. if ($nothingvalue === 'choose' || $nothingvalue === 'choosedots') {
  1271. $nothing = [key($this->nothing) => get_string('choosedots')];
  1272. } else {
  1273. $nothing = $this->nothing;
  1274. }
  1275. }
  1276. $data->options = $this->flatten_options($this->urls, $nothing);
  1277. // Label attributes.
  1278. $data->labelattributes = [];
  1279. // Unset label attributes that are already in the template.
  1280. unset($this->labelattributes['for']);
  1281. // Map the label attributes.
  1282. foreach ($this->labelattributes as $key => $value) {
  1283. $data->labelattributes[] = ['name' => $key, 'value' => $value];
  1284. }
  1285. // Help icon.
  1286. $data->helpicon = !empty($this->helpicon) ? $this->helpicon->export_for_template($output) : false;
  1287. // Finally all the remaining attributes.
  1288. $data->attributes = [];
  1289. foreach ($attributes as $key => $value) {
  1290. $data->attributes[] = ['name' => $key, 'value' => $value];
  1291. }
  1292. return $data;
  1293. }
  1294. }
  1295. /**
  1296. * Data structure describing html link with special action attached.
  1297. *
  1298. * @copyright 2010 Petr Skoda
  1299. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1300. * @since Moodle 2.0
  1301. * @package core
  1302. * @category output
  1303. */
  1304. class action_link implements renderable {
  1305. /**
  1306. * @var moodle_url Href url
  1307. */
  1308. public $url;
  1309. /**
  1310. * @var string Link text HTML fragment
  1311. */
  1312. public $text;
  1313. /**
  1314. * @var array HTML attributes
  1315. */
  1316. public $attributes;
  1317. /**
  1318. * @var array List of actions attached to link
  1319. */
  1320. public $actions;
  1321. /**
  1322. * @var pix_icon Optional pix icon to render with the link
  1323. */
  1324. public $icon;
  1325. /**
  1326. * Constructor
  1327. * @param moodle_url $url
  1328. * @param string $text HTML fragment
  1329. * @param component_action $action
  1330. * @param array $attributes associative array of html link attributes + disabled
  1331. * @param pix_icon $icon optional pix_icon to render with the link text
  1332. */
  1333. public function __construct(moodle_url $url,
  1334. $text,
  1335. component_action $action=null,
  1336. array $attributes=null,
  1337. pix_icon $icon=null) {
  1338. $this->url = clone($url);
  1339. $this->text = $text;
  1340. $this->attributes = (array)$attributes;
  1341. if ($action) {
  1342. $this->add_action($action);
  1343. }
  1344. $this->icon = $icon;
  1345. }
  1346. /**
  1347. * Add action to the link.
  1348. *
  1349. * @param component_action $action
  1350. */
  1351. public function add_action(component_action $action) {
  1352. $this->actions[] = $action;
  1353. }
  1354. /**
  1355. * Adds a CSS class to this action link object
  1356. * @param string $class
  1357. */
  1358. public function add_class($class) {
  1359. if (empty($this->attributes['class'])) {
  1360. $this->attributes['class'] = $class;
  1361. } else {
  1362. $this->attributes['class'] .= ' ' . $class;
  1363. }
  1364. }
  1365. /**
  1366. * Returns true if the specified class has been added to this link.
  1367. * @param string $class
  1368. * @return bool
  1369. */
  1370. public function has_class($class) {
  1371. return strpos(' ' . $this->attributes['class'] . ' ', ' ' . $class . ' ') !== false;
  1372. }
  1373. /**
  1374. * Return the rendered HTML for the icon. Useful for rendering action links in a template.
  1375. * @return string
  1376. */
  1377. public function get_icon_html() {
  1378. global $OUTPUT;
  1379. if (!$this->icon) {
  1380. return '';
  1381. }
  1382. return $OUTPUT->render($this->icon);
  1383. }
  1384. /**
  1385. * Export for template.
  1386. *
  1387. * @param rendereā€¦

Large files files are truncated, but you can click here to view the full file