PageRenderTime 49ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/outputcomponents.php

https://github.com/thepurpleblob/gumoodle
PHP | 2808 lines | 1169 code | 313 blank | 1326 comment | 216 complexity | 23038f1f9232942a092bfc4d9bc8f82a MD5 | raw file
Possible License(s): Apache-2.0, GPL-3.0, BSD-3-Clause, LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-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. * Data structure representing a file picker.
  40. *
  41. * @copyright 2010 Dongsheng Cai
  42. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43. * @since Moodle 2.0
  44. * @package core
  45. * @category output
  46. */
  47. class file_picker implements renderable {
  48. /**
  49. * @var stdClass An object containing options for the file picker
  50. */
  51. public $options;
  52. /**
  53. * Constructs a file picker object.
  54. *
  55. * The following are possible options for the filepicker:
  56. * - accepted_types (*)
  57. * - return_types (FILE_INTERNAL)
  58. * - env (filepicker)
  59. * - client_id (uniqid)
  60. * - itemid (0)
  61. * - maxbytes (-1)
  62. * - maxfiles (1)
  63. * - buttonname (false)
  64. *
  65. * @param stdClass $options An object containing options for the file picker.
  66. */
  67. public function __construct(stdClass $options) {
  68. global $CFG, $USER, $PAGE;
  69. require_once($CFG->dirroot. '/repository/lib.php');
  70. $defaults = array(
  71. 'accepted_types'=>'*',
  72. 'return_types'=>FILE_INTERNAL,
  73. 'env' => 'filepicker',
  74. 'client_id' => uniqid(),
  75. 'itemid' => 0,
  76. 'maxbytes'=>-1,
  77. 'maxfiles'=>1,
  78. 'buttonname'=>false
  79. );
  80. foreach ($defaults as $key=>$value) {
  81. if (empty($options->$key)) {
  82. $options->$key = $value;
  83. }
  84. }
  85. $options->currentfile = '';
  86. if (!empty($options->itemid)) {
  87. $fs = get_file_storage();
  88. $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
  89. if (empty($options->filename)) {
  90. if ($files = $fs->get_area_files($usercontext->id, 'user', 'draft', $options->itemid, 'id DESC', false)) {
  91. $file = reset($files);
  92. }
  93. } else {
  94. $file = $fs->get_file($usercontext->id, 'user', 'draft', $options->itemid, $options->filepath, $options->filename);
  95. }
  96. if (!empty($file)) {
  97. $options->currentfile = html_writer::link(moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()), $file->get_filename());
  98. }
  99. }
  100. // initilise options, getting files in root path
  101. $this->options = initialise_filepicker($options);
  102. // copying other options
  103. foreach ($options as $name=>$value) {
  104. if (!isset($this->options->$name)) {
  105. $this->options->$name = $value;
  106. }
  107. }
  108. }
  109. }
  110. /**
  111. * Data structure representing a user picture.
  112. *
  113. * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
  114. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  115. * @since Modle 2.0
  116. * @package core
  117. * @category output
  118. */
  119. class user_picture implements renderable {
  120. /**
  121. * @var array List of mandatory fields in user record here. (do not include
  122. * TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE)
  123. */
  124. protected static $fields = array('id', 'picture', 'firstname', 'lastname', 'imagealt', 'email');
  125. /**
  126. * @var stdClass A user object with at least fields all columns specified
  127. * in $fields array constant set.
  128. */
  129. public $user;
  130. /**
  131. * @var int The course id. Used when constructing the link to the user's
  132. * profile, page course id used if not specified.
  133. */
  134. public $courseid;
  135. /**
  136. * @var bool Add course profile link to image
  137. */
  138. public $link = true;
  139. /**
  140. * @var int Size in pixels. Special values are (true/1 = 100px) and
  141. * (false/0 = 35px)
  142. * for backward compatibility.
  143. */
  144. public $size = 35;
  145. /**
  146. * @var bool Add non-blank alt-text to the image.
  147. * Default true, set to false when image alt just duplicates text in screenreaders.
  148. */
  149. public $alttext = true;
  150. /**
  151. * @var bool Whether or not to open the link in a popup window.
  152. */
  153. public $popup = false;
  154. /**
  155. * @var string Image class attribute
  156. */
  157. public $class = 'userpicture';
  158. /**
  159. * User picture constructor.
  160. *
  161. * @param stdClass $user user record with at least id, picture, imagealt, firstname and lastname set.
  162. * It is recommended to add also contextid of the user for performance reasons.
  163. */
  164. public function __construct(stdClass $user) {
  165. global $DB;
  166. if (empty($user->id)) {
  167. throw new coding_exception('User id is required when printing user avatar image.');
  168. }
  169. // only touch the DB if we are missing data and complain loudly...
  170. $needrec = false;
  171. foreach (self::$fields as $field) {
  172. if (!array_key_exists($field, $user)) {
  173. $needrec = true;
  174. debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
  175. .'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER);
  176. break;
  177. }
  178. }
  179. if ($needrec) {
  180. $this->user = $DB->get_record('user', array('id'=>$user->id), self::fields(), MUST_EXIST);
  181. } else {
  182. $this->user = clone($user);
  183. }
  184. }
  185. /**
  186. * Returns a list of required user fields, useful when fetching required user info from db.
  187. *
  188. * In some cases we have to fetch the user data together with some other information,
  189. * the idalias is useful there because the id would otherwise override the main
  190. * id of the result record. Please note it has to be converted back to id before rendering.
  191. *
  192. * @param string $tableprefix name of database table prefix in query
  193. * @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)
  194. * @param string $idalias alias of id field
  195. * @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id'
  196. * @return string
  197. */
  198. public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') {
  199. if (!$tableprefix and !$extrafields and !$idalias) {
  200. return implode(',', self::$fields);
  201. }
  202. if ($tableprefix) {
  203. $tableprefix .= '.';
  204. }
  205. $fields = array();
  206. foreach (self::$fields as $field) {
  207. if ($field === 'id' and $idalias and $idalias !== 'id') {
  208. $fields[$field] = "$tableprefix$field AS $idalias";
  209. } else {
  210. if ($fieldprefix and $field !== 'id') {
  211. $fields[$field] = "$tableprefix$field AS $fieldprefix$field";
  212. } else {
  213. $fields[$field] = "$tableprefix$field";
  214. }
  215. }
  216. }
  217. // add extra fields if not already there
  218. if ($extrafields) {
  219. foreach ($extrafields as $e) {
  220. if ($e === 'id' or isset($fields[$e])) {
  221. continue;
  222. }
  223. if ($fieldprefix) {
  224. $fields[$e] = "$tableprefix$e AS $fieldprefix$e";
  225. } else {
  226. $fields[$e] = "$tableprefix$e";
  227. }
  228. }
  229. }
  230. return implode(',', $fields);
  231. }
  232. /**
  233. * Extract the aliased user fields from a given record
  234. *
  235. * Given a record that was previously obtained using {@link self::fields()} with aliases,
  236. * this method extracts user related unaliased fields.
  237. *
  238. * @param stdClass $record containing user picture fields
  239. * @param array $extrafields extra fields included in the $record
  240. * @param string $idalias alias of the id field
  241. * @param string $fieldprefix prefix added to all columns in their aliases, does not apply to 'id'
  242. * @return stdClass object with unaliased user fields
  243. */
  244. public static function unalias(stdClass $record, array $extrafields = null, $idalias = 'id', $fieldprefix = '') {
  245. if (empty($idalias)) {
  246. $idalias = 'id';
  247. }
  248. $return = new stdClass();
  249. foreach (self::$fields as $field) {
  250. if ($field === 'id') {
  251. if (property_exists($record, $idalias)) {
  252. $return->id = $record->{$idalias};
  253. }
  254. } else {
  255. if (property_exists($record, $fieldprefix.$field)) {
  256. $return->{$field} = $record->{$fieldprefix.$field};
  257. }
  258. }
  259. }
  260. // add extra fields if not already there
  261. if ($extrafields) {
  262. foreach ($extrafields as $e) {
  263. if ($e === 'id' or property_exists($return, $e)) {
  264. continue;
  265. }
  266. $return->{$e} = $record->{$fieldprefix.$e};
  267. }
  268. }
  269. return $return;
  270. }
  271. /**
  272. * Works out the URL for the users picture.
  273. *
  274. * This method is recommended as it avoids costly redirects of user pictures
  275. * if requests are made for non-existent files etc.
  276. *
  277. * @param moodle_page $page
  278. * @param renderer_base $renderer
  279. * @return moodle_url
  280. */
  281. public function get_url(moodle_page $page, renderer_base $renderer = null) {
  282. global $CFG;
  283. if (is_null($renderer)) {
  284. $renderer = $page->get_renderer('core');
  285. }
  286. // Sort out the filename and size. Size is only required for the gravatar
  287. // implementation presently.
  288. if (empty($this->size)) {
  289. $filename = 'f2';
  290. $size = 35;
  291. } else if ($this->size === true or $this->size == 1) {
  292. $filename = 'f1';
  293. $size = 100;
  294. } else if ($this->size > 100) {
  295. $filename = 'f3';
  296. $size = (int)$this->size;
  297. } else if ($this->size >= 50) {
  298. $filename = 'f1';
  299. $size = (int)$this->size;
  300. } else {
  301. $filename = 'f2';
  302. $size = (int)$this->size;
  303. }
  304. $defaulturl = $renderer->pix_url('u/'.$filename); // default image
  305. if ((!empty($CFG->forcelogin) and !isloggedin()) ||
  306. (!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) {
  307. // Protect images if login required and not logged in;
  308. // also if login is required for profile images and is not logged in or guest
  309. // do not use require_login() because it is expensive and not suitable here anyway.
  310. return $defaulturl;
  311. }
  312. // First try to detect deleted users - but do not read from database for performance reasons!
  313. if (!empty($this->user->deleted) or strpos($this->user->email, '@') === false) {
  314. // All deleted users should have email replaced by md5 hash,
  315. // all active users are expected to have valid email.
  316. return $defaulturl;
  317. }
  318. // Did the user upload a picture?
  319. if ($this->user->picture > 0) {
  320. if (!empty($this->user->contextid)) {
  321. $contextid = $this->user->contextid;
  322. } else {
  323. $context = context_user::instance($this->user->id, IGNORE_MISSING);
  324. if (!$context) {
  325. // This must be an incorrectly deleted user, all other users have context.
  326. return $defaulturl;
  327. }
  328. $contextid = $context->id;
  329. }
  330. $path = '/';
  331. if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) {
  332. // We append the theme name to the file path if we have it so that
  333. // in the circumstance that the profile picture is not available
  334. // when the user actually requests it they still get the profile
  335. // picture for the correct theme.
  336. $path .= $page->theme->name.'/';
  337. }
  338. // Set the image URL to the URL for the uploaded file and return.
  339. $url = moodle_url::make_pluginfile_url($contextid, 'user', 'icon', NULL, $path, $filename);
  340. $url->param('rev', $this->user->picture);
  341. return $url;
  342. }
  343. if ($this->user->picture == 0 and !empty($CFG->enablegravatar)) {
  344. // Normalise the size variable to acceptable bounds
  345. if ($size < 1 || $size > 512) {
  346. $size = 35;
  347. }
  348. // Hash the users email address
  349. $md5 = md5(strtolower(trim($this->user->email)));
  350. // Build a gravatar URL with what we know.
  351. // If the currently requested page is https then we'll return an
  352. // https gravatar page.
  353. if (strpos($CFG->httpswwwroot, 'https:') === 0) {
  354. return new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $defaulturl->out(false)));
  355. } else {
  356. return new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $defaulturl->out(false)));
  357. }
  358. }
  359. return $defaulturl;
  360. }
  361. }
  362. /**
  363. * Data structure representing a help icon.
  364. *
  365. * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
  366. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  367. * @since Moodle 2.0
  368. * @package core
  369. * @category output
  370. */
  371. class old_help_icon implements renderable {
  372. /**
  373. * @var string Lang pack identifier
  374. */
  375. public $helpidentifier;
  376. /**
  377. * @var string A descriptive text for title tooltip
  378. */
  379. public $title = null;
  380. /**
  381. * @var string Component name, the same as in get_string()
  382. */
  383. public $component = 'moodle';
  384. /**
  385. * @var string Extra descriptive text next to the icon
  386. */
  387. public $linktext = null;
  388. /**
  389. * Constructor: sets up the other components in case they are needed
  390. *
  391. * @param string $helpidentifier The keyword that defines a help page
  392. * @param string $title A descriptive text for accessibility only
  393. * @param string $component
  394. */
  395. public function __construct($helpidentifier, $title, $component = 'moodle') {
  396. if (empty($title)) {
  397. throw new coding_exception('A help_icon object requires a $text parameter');
  398. }
  399. if (empty($helpidentifier)) {
  400. throw new coding_exception('A help_icon object requires a $helpidentifier parameter');
  401. }
  402. $this->helpidentifier = $helpidentifier;
  403. $this->title = $title;
  404. $this->component = $component;
  405. }
  406. }
  407. /**
  408. * Data structure representing a help icon.
  409. *
  410. * @copyright 2010 Petr Skoda (info@skodak.org)
  411. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  412. * @since Moodle 2.0
  413. * @package core
  414. * @category output
  415. */
  416. class help_icon implements renderable {
  417. /**
  418. * @var string lang pack identifier (without the "_help" suffix),
  419. * both get_string($identifier, $component) and get_string($identifier.'_help', $component)
  420. * must exist.
  421. */
  422. public $identifier;
  423. /**
  424. * @var string Component name, the same as in get_string()
  425. */
  426. public $component;
  427. /**
  428. * @var string Extra descriptive text next to the icon
  429. */
  430. public $linktext = null;
  431. /**
  432. * Constructor
  433. *
  434. * @param string $identifier string for help page title,
  435. * string with _help suffix is used for the actual help text.
  436. * string with _link suffix is used to create a link to further info (if it exists)
  437. * @param string $component
  438. */
  439. public function __construct($identifier, $component) {
  440. $this->identifier = $identifier;
  441. $this->component = $component;
  442. }
  443. /**
  444. * Verifies that both help strings exists, shows debug warnings if not
  445. */
  446. public function diag_strings() {
  447. $sm = get_string_manager();
  448. if (!$sm->string_exists($this->identifier, $this->component)) {
  449. debugging("Help title string does not exist: [$this->identifier, $this->component]");
  450. }
  451. if (!$sm->string_exists($this->identifier.'_help', $this->component)) {
  452. debugging("Help contents string does not exist: [{$this->identifier}_help, $this->component]");
  453. }
  454. }
  455. }
  456. /**
  457. * Data structure representing an icon.
  458. *
  459. * @copyright 2010 Petr Skoda
  460. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  461. * @since Moodle 2.0
  462. * @package core
  463. * @category output
  464. */
  465. class pix_icon implements renderable {
  466. /**
  467. * @var string The icon name
  468. */
  469. var $pix;
  470. /**
  471. * @var string The component the icon belongs to.
  472. */
  473. var $component;
  474. /**
  475. * @var array An array of attributes to use on the icon
  476. */
  477. var $attributes = array();
  478. /**
  479. * Constructor
  480. *
  481. * @param string $pix short icon name
  482. * @param string $alt The alt text to use for the icon
  483. * @param string $component component name
  484. * @param array $attributes html attributes
  485. */
  486. public function __construct($pix, $alt, $component='moodle', array $attributes = null) {
  487. $this->pix = $pix;
  488. $this->component = $component;
  489. $this->attributes = (array)$attributes;
  490. $this->attributes['alt'] = $alt;
  491. if (empty($this->attributes['class'])) {
  492. $this->attributes['class'] = 'smallicon';
  493. }
  494. if (!isset($this->attributes['title'])) {
  495. $this->attributes['title'] = $this->attributes['alt'];
  496. }
  497. }
  498. }
  499. /**
  500. * Data structure representing an emoticon image
  501. *
  502. * @copyright 2010 David Mudrak
  503. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  504. * @since Moodle 2.0
  505. * @package core
  506. * @category output
  507. */
  508. class pix_emoticon extends pix_icon implements renderable {
  509. /**
  510. * Constructor
  511. * @param string $pix short icon name
  512. * @param string $alt alternative text
  513. * @param string $component emoticon image provider
  514. * @param array $attributes explicit HTML attributes
  515. */
  516. public function __construct($pix, $alt, $component = 'moodle', array $attributes = array()) {
  517. if (empty($attributes['class'])) {
  518. $attributes['class'] = 'emoticon';
  519. }
  520. parent::__construct($pix, $alt, $component, $attributes);
  521. }
  522. }
  523. /**
  524. * Data structure representing a simple form with only one button.
  525. *
  526. * @copyright 2009 Petr Skoda
  527. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  528. * @since Moodle 2.0
  529. * @package core
  530. * @category output
  531. */
  532. class single_button implements renderable {
  533. /**
  534. * @var moodle_url Target url
  535. */
  536. var $url;
  537. /**
  538. * @var string Button label
  539. */
  540. var $label;
  541. /**
  542. * @var string Form submit method post or get
  543. */
  544. var $method = 'post';
  545. /**
  546. * @var string Wrapping div class
  547. */
  548. var $class = 'singlebutton';
  549. /**
  550. * @var bool True if button disabled, false if normal
  551. */
  552. var $disabled = false;
  553. /**
  554. * @var string Button tooltip
  555. */
  556. var $tooltip = null;
  557. /**
  558. * @var string Form id
  559. */
  560. var $formid;
  561. /**
  562. * @var array List of attached actions
  563. */
  564. var $actions = array();
  565. /**
  566. * Constructor
  567. * @param moodle_url $url
  568. * @param string $label button text
  569. * @param string $method get or post submit method
  570. */
  571. public function __construct(moodle_url $url, $label, $method='post') {
  572. $this->url = clone($url);
  573. $this->label = $label;
  574. $this->method = $method;
  575. }
  576. /**
  577. * Shortcut for adding a JS confirm dialog when the button is clicked.
  578. * The message must be a yes/no question.
  579. *
  580. * @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
  581. */
  582. public function add_confirm_action($confirmmessage) {
  583. $this->add_action(new confirm_action($confirmmessage));
  584. }
  585. /**
  586. * Add action to the button.
  587. * @param component_action $action
  588. */
  589. public function add_action(component_action $action) {
  590. $this->actions[] = $action;
  591. }
  592. }
  593. /**
  594. * Simple form with just one select field that gets submitted automatically.
  595. *
  596. * If JS not enabled small go button is printed too.
  597. *
  598. * @copyright 2009 Petr Skoda
  599. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  600. * @since Moodle 2.0
  601. * @package core
  602. * @category output
  603. */
  604. class single_select implements renderable {
  605. /**
  606. * @var moodle_url Target url - includes hidden fields
  607. */
  608. var $url;
  609. /**
  610. * @var string Name of the select element.
  611. */
  612. var $name;
  613. /**
  614. * @var array $options associative array value=>label ex.: array(1=>'One, 2=>Two)
  615. * it is also possible to specify optgroup as complex label array ex.:
  616. * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
  617. * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
  618. */
  619. var $options;
  620. /**
  621. * @var string Selected option
  622. */
  623. var $selected;
  624. /**
  625. * @var array Nothing selected
  626. */
  627. var $nothing;
  628. /**
  629. * @var array Extra select field attributes
  630. */
  631. var $attributes = array();
  632. /**
  633. * @var string Button label
  634. */
  635. var $label = '';
  636. /**
  637. * @var array Button label's attributes
  638. */
  639. var $labelattributes = array();
  640. /**
  641. * @var string Form submit method post or get
  642. */
  643. var $method = 'get';
  644. /**
  645. * @var string Wrapping div class
  646. */
  647. var $class = 'singleselect';
  648. /**
  649. * @var bool True if button disabled, false if normal
  650. */
  651. var $disabled = false;
  652. /**
  653. * @var string Button tooltip
  654. */
  655. var $tooltip = null;
  656. /**
  657. * @var string Form id
  658. */
  659. var $formid = null;
  660. /**
  661. * @var array List of attached actions
  662. */
  663. var $helpicon = null;
  664. /**
  665. * Constructor
  666. * @param moodle_url $url form action target, includes hidden fields
  667. * @param string $name name of selection field - the changing parameter in url
  668. * @param array $options list of options
  669. * @param string $selected selected element
  670. * @param array $nothing
  671. * @param string $formid
  672. */
  673. public function __construct(moodle_url $url, $name, array $options, $selected = '', $nothing = array('' => 'choosedots'), $formid = null) {
  674. $this->url = $url;
  675. $this->name = $name;
  676. $this->options = $options;
  677. $this->selected = $selected;
  678. $this->nothing = $nothing;
  679. $this->formid = $formid;
  680. }
  681. /**
  682. * Shortcut for adding a JS confirm dialog when the button is clicked.
  683. * The message must be a yes/no question.
  684. *
  685. * @param string $confirmmessage The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
  686. */
  687. public function add_confirm_action($confirmmessage) {
  688. $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
  689. }
  690. /**
  691. * Add action to the button.
  692. *
  693. * @param component_action $action
  694. */
  695. public function add_action(component_action $action) {
  696. $this->actions[] = $action;
  697. }
  698. /**
  699. * Adds help icon.
  700. *
  701. * @param string $helppage The keyword that defines a help page
  702. * @param string $title A descriptive text for accessibility only
  703. * @param string $component
  704. */
  705. public function set_old_help_icon($helppage, $title, $component = 'moodle') {
  706. $this->helpicon = new old_help_icon($helppage, $title, $component);
  707. }
  708. /**
  709. * Adds help icon.
  710. *
  711. * @param string $identifier The keyword that defines a help page
  712. * @param string $component
  713. */
  714. public function set_help_icon($identifier, $component = 'moodle') {
  715. $this->helpicon = new help_icon($identifier, $component);
  716. }
  717. /**
  718. * Sets select's label
  719. *
  720. * @param string $label
  721. * @param array $attributes (optional)
  722. */
  723. public function set_label($label, $attributes = array()) {
  724. $this->label = $label;
  725. $this->labelattributes = $attributes;
  726. }
  727. }
  728. /**
  729. * Simple URL selection widget description.
  730. *
  731. * @copyright 2009 Petr Skoda
  732. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  733. * @since Moodle 2.0
  734. * @package core
  735. * @category output
  736. */
  737. class url_select implements renderable {
  738. /**
  739. * @var array $urls associative array value=>label ex.: array(1=>'One, 2=>Two)
  740. * it is also possible to specify optgroup as complex label array ex.:
  741. * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
  742. * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
  743. */
  744. var $urls;
  745. /**
  746. * @var string Selected option
  747. */
  748. var $selected;
  749. /**
  750. * @var array Nothing selected
  751. */
  752. var $nothing;
  753. /**
  754. * @var array Extra select field attributes
  755. */
  756. var $attributes = array();
  757. /**
  758. * @var string Button label
  759. */
  760. var $label = '';
  761. /**
  762. * @var array Button label's attributes
  763. */
  764. var $labelattributes = array();
  765. /**
  766. * @var string Wrapping div class
  767. */
  768. var $class = 'urlselect';
  769. /**
  770. * @var bool True if button disabled, false if normal
  771. */
  772. var $disabled = false;
  773. /**
  774. * @var string Button tooltip
  775. */
  776. var $tooltip = null;
  777. /**
  778. * @var string Form id
  779. */
  780. var $formid = null;
  781. /**
  782. * @var array List of attached actions
  783. */
  784. var $helpicon = null;
  785. /**
  786. * @var string If set, makes button visible with given name for button
  787. */
  788. var $showbutton = null;
  789. /**
  790. * Constructor
  791. * @param array $urls list of options
  792. * @param string $selected selected element
  793. * @param array $nothing
  794. * @param string $formid
  795. * @param string $showbutton Set to text of button if it should be visible
  796. * or null if it should be hidden (hidden version always has text 'go')
  797. */
  798. public function __construct(array $urls, $selected = '', $nothing = array('' => 'choosedots'), $formid = null, $showbutton = null) {
  799. $this->urls = $urls;
  800. $this->selected = $selected;
  801. $this->nothing = $nothing;
  802. $this->formid = $formid;
  803. $this->showbutton = $showbutton;
  804. }
  805. /**
  806. * Adds help icon.
  807. *
  808. * @param string $helppage The keyword that defines a help page
  809. * @param string $title A descriptive text for accessibility only
  810. * @param string $component
  811. */
  812. public function set_old_help_icon($helppage, $title, $component = 'moodle') {
  813. $this->helpicon = new old_help_icon($helppage, $title, $component);
  814. }
  815. /**
  816. * Adds help icon.
  817. *
  818. * @param string $identifier The keyword that defines a help page
  819. * @param string $component
  820. */
  821. public function set_help_icon($identifier, $component = 'moodle') {
  822. $this->helpicon = new help_icon($identifier, $component);
  823. }
  824. /**
  825. * Sets select's label
  826. *
  827. * @param string $label
  828. * @param array $attributes (optional)
  829. */
  830. public function set_label($label, $attributes = array()) {
  831. $this->label = $label;
  832. $this->labelattributes = $attributes;
  833. }
  834. }
  835. /**
  836. * Data structure describing html link with special action attached.
  837. *
  838. * @copyright 2010 Petr Skoda
  839. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  840. * @since Moodle 2.0
  841. * @package core
  842. * @category output
  843. */
  844. class action_link implements renderable {
  845. /**
  846. * @var moodle_url Href url
  847. */
  848. var $url;
  849. /**
  850. * @var string Link text HTML fragment
  851. */
  852. var $text;
  853. /**
  854. * @var array HTML attributes
  855. */
  856. var $attributes;
  857. /**
  858. * @var array List of actions attached to link
  859. */
  860. var $actions;
  861. /**
  862. * Constructor
  863. * @param moodle_url $url
  864. * @param string $text HTML fragment
  865. * @param component_action $action
  866. * @param array $attributes associative array of html link attributes + disabled
  867. */
  868. public function __construct(moodle_url $url, $text, component_action $action = null, array $attributes = null) {
  869. $this->url = clone($url);
  870. $this->text = $text;
  871. $this->attributes = (array)$attributes;
  872. if ($action) {
  873. $this->add_action($action);
  874. }
  875. }
  876. /**
  877. * Add action to the link.
  878. *
  879. * @param component_action $action
  880. */
  881. public function add_action(component_action $action) {
  882. $this->actions[] = $action;
  883. }
  884. /**
  885. * Adds a CSS class to this action link object
  886. * @param string $class
  887. */
  888. public function add_class($class) {
  889. if (empty($this->attributes['class'])) {
  890. $this->attributes['class'] = $class;
  891. } else {
  892. $this->attributes['class'] .= ' ' . $class;
  893. }
  894. }
  895. }
  896. /**
  897. * Simple html output class
  898. *
  899. * @copyright 2009 Tim Hunt, 2010 Petr Skoda
  900. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  901. * @since Moodle 2.0
  902. * @package core
  903. * @category output
  904. */
  905. class html_writer {
  906. /**
  907. * Outputs a tag with attributes and contents
  908. *
  909. * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
  910. * @param string $contents What goes between the opening and closing tags
  911. * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
  912. * @return string HTML fragment
  913. */
  914. public static function tag($tagname, $contents, array $attributes = null) {
  915. return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname);
  916. }
  917. /**
  918. * Outputs an opening tag with attributes
  919. *
  920. * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
  921. * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
  922. * @return string HTML fragment
  923. */
  924. public static function start_tag($tagname, array $attributes = null) {
  925. return '<' . $tagname . self::attributes($attributes) . '>';
  926. }
  927. /**
  928. * Outputs a closing tag
  929. *
  930. * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
  931. * @return string HTML fragment
  932. */
  933. public static function end_tag($tagname) {
  934. return '</' . $tagname . '>';
  935. }
  936. /**
  937. * Outputs an empty tag with attributes
  938. *
  939. * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
  940. * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
  941. * @return string HTML fragment
  942. */
  943. public static function empty_tag($tagname, array $attributes = null) {
  944. return '<' . $tagname . self::attributes($attributes) . ' />';
  945. }
  946. /**
  947. * Outputs a tag, but only if the contents are not empty
  948. *
  949. * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
  950. * @param string $contents What goes between the opening and closing tags
  951. * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
  952. * @return string HTML fragment
  953. */
  954. public static function nonempty_tag($tagname, $contents, array $attributes = null) {
  955. if ($contents === '' || is_null($contents)) {
  956. return '';
  957. }
  958. return self::tag($tagname, $contents, $attributes);
  959. }
  960. /**
  961. * Outputs a HTML attribute and value
  962. *
  963. * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
  964. * @param string $value The value of the attribute. The value will be escaped with {@link s()}
  965. * @return string HTML fragment
  966. */
  967. public static function attribute($name, $value) {
  968. if (is_array($value)) {
  969. debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
  970. }
  971. if ($value instanceof moodle_url) {
  972. return ' ' . $name . '="' . $value->out() . '"';
  973. }
  974. // special case, we do not want these in output
  975. if ($value === null) {
  976. return '';
  977. }
  978. // no sloppy trimming here!
  979. return ' ' . $name . '="' . s($value) . '"';
  980. }
  981. /**
  982. * Outputs a list of HTML attributes and values
  983. *
  984. * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
  985. * The values will be escaped with {@link s()}
  986. * @return string HTML fragment
  987. */
  988. public static function attributes(array $attributes = null) {
  989. $attributes = (array)$attributes;
  990. $output = '';
  991. foreach ($attributes as $name => $value) {
  992. $output .= self::attribute($name, $value);
  993. }
  994. return $output;
  995. }
  996. /**
  997. * Generates random html element id.
  998. *
  999. * @staticvar int $counter
  1000. * @staticvar type $uniq
  1001. * @param string $base A string fragment that will be included in the random ID.
  1002. * @return string A unique ID
  1003. */
  1004. public static function random_id($base='random') {
  1005. static $counter = 0;
  1006. static $uniq;
  1007. if (!isset($uniq)) {
  1008. $uniq = uniqid();
  1009. }
  1010. $counter++;
  1011. return $base.$uniq.$counter;
  1012. }
  1013. /**
  1014. * Generates a simple html link
  1015. *
  1016. * @param string|moodle_url $url The URL
  1017. * @param string $text The text
  1018. * @param array $attributes HTML attributes
  1019. * @return string HTML fragment
  1020. */
  1021. public static function link($url, $text, array $attributes = null) {
  1022. $attributes = (array)$attributes;
  1023. $attributes['href'] = $url;
  1024. return self::tag('a', $text, $attributes);
  1025. }
  1026. /**
  1027. * Generates a simple checkbox with optional label
  1028. *
  1029. * @param string $name The name of the checkbox
  1030. * @param string $value The value of the checkbox
  1031. * @param bool $checked Whether the checkbox is checked
  1032. * @param string $label The label for the checkbox
  1033. * @param array $attributes Any attributes to apply to the checkbox
  1034. * @return string html fragment
  1035. */
  1036. public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) {
  1037. $attributes = (array)$attributes;
  1038. $output = '';
  1039. if ($label !== '' and !is_null($label)) {
  1040. if (empty($attributes['id'])) {
  1041. $attributes['id'] = self::random_id('checkbox_');
  1042. }
  1043. }
  1044. $attributes['type'] = 'checkbox';
  1045. $attributes['value'] = $value;
  1046. $attributes['name'] = $name;
  1047. $attributes['checked'] = $checked ? 'checked' : null;
  1048. $output .= self::empty_tag('input', $attributes);
  1049. if ($label !== '' and !is_null($label)) {
  1050. $output .= self::tag('label', $label, array('for'=>$attributes['id']));
  1051. }
  1052. return $output;
  1053. }
  1054. /**
  1055. * Generates a simple select yes/no form field
  1056. *
  1057. * @param string $name name of select element
  1058. * @param bool $selected
  1059. * @param array $attributes - html select element attributes
  1060. * @return string HTML fragment
  1061. */
  1062. public static function select_yes_no($name, $selected=true, array $attributes = null) {
  1063. $options = array('1'=>get_string('yes'), '0'=>get_string('no'));
  1064. return self::select($options, $name, $selected, null, $attributes);
  1065. }
  1066. /**
  1067. * Generates a simple select form field
  1068. *
  1069. * @param array $options associative array value=>label ex.:
  1070. * array(1=>'One, 2=>Two)
  1071. * it is also possible to specify optgroup as complex label array ex.:
  1072. * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
  1073. * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
  1074. * @param string $name name of select element
  1075. * @param string|array $selected value or array of values depending on multiple attribute
  1076. * @param array|bool $nothing add nothing selected option, or false of not added
  1077. * @param array $attributes html select element attributes
  1078. * @return string HTML fragment
  1079. */
  1080. public static function select(array $options, $name, $selected = '', $nothing = array('' => 'choosedots'), array $attributes = null) {
  1081. $attributes = (array)$attributes;
  1082. if (is_array($nothing)) {
  1083. foreach ($nothing as $k=>$v) {
  1084. if ($v === 'choose' or $v === 'choosedots') {
  1085. $nothing[$k] = get_string('choosedots');
  1086. }
  1087. }
  1088. $options = $nothing + $options; // keep keys, do not override
  1089. } else if (is_string($nothing) and $nothing !== '') {
  1090. // BC
  1091. $options = array(''=>$nothing) + $options;
  1092. }
  1093. // we may accept more values if multiple attribute specified
  1094. $selected = (array)$selected;
  1095. foreach ($selected as $k=>$v) {
  1096. $selected[$k] = (string)$v;
  1097. }
  1098. if (!isset($attributes['id'])) {
  1099. $id = 'menu'.$name;
  1100. // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
  1101. $id = str_replace('[', '', $id);
  1102. $id = str_replace(']', '', $id);
  1103. $attributes['id'] = $id;
  1104. }
  1105. if (!isset($attributes['class'])) {
  1106. $class = 'menu'.$name;
  1107. // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
  1108. $class = str_replace('[', '', $class);
  1109. $class = str_replace(']', '', $class);
  1110. $attributes['class'] = $class;
  1111. }
  1112. $attributes['class'] = 'select ' . $attributes['class']; // Add 'select' selector always
  1113. $attributes['name'] = $name;
  1114. if (!empty($attributes['disabled'])) {
  1115. $attributes['disabled'] = 'disabled';
  1116. } else {
  1117. unset($attributes['disabled']);
  1118. }
  1119. $output = '';
  1120. foreach ($options as $value=>$label) {
  1121. if (is_array($label)) {
  1122. // ignore key, it just has to be unique
  1123. $output .= self::select_optgroup(key($label), current($label), $selected);
  1124. } else {
  1125. $output .= self::select_option($label, $value, $selected);
  1126. }
  1127. }
  1128. return self::tag('select', $output, $attributes);
  1129. }
  1130. /**
  1131. * Returns HTML to display a select box option.
  1132. *
  1133. * @param string $label The label to display as the option.
  1134. * @param string|int $value The value the option represents
  1135. * @param array $selected An array of selected options
  1136. * @return string HTML fragment
  1137. */
  1138. private static function select_option($label, $value, array $selected) {
  1139. $attributes = array();
  1140. $value = (string)$value;
  1141. if (in_array($value, $selected, true)) {
  1142. $attributes['selected'] = 'selected';
  1143. }
  1144. $attributes['value'] = $value;
  1145. return self::tag('option', $label, $attributes);
  1146. }
  1147. /**
  1148. * Returns HTML to display a select box option group.
  1149. *
  1150. * @param string $groupname The label to use for the group
  1151. * @param array $options The options in the group
  1152. * @param array $selected An array of selected values.
  1153. * @return string HTML fragment.
  1154. */
  1155. private static function select_optgroup($groupname, $options, array $selected) {
  1156. if (empty($options)) {
  1157. return '';
  1158. }
  1159. $attributes = array('label'=>$groupname);
  1160. $output = '';
  1161. foreach ($options as $value=>$label) {
  1162. $output .= self::select_option($label, $value, $selected);
  1163. }
  1164. return self::tag('optgroup', $output, $attributes);
  1165. }
  1166. /**
  1167. * This is a shortcut for making an hour selector menu.
  1168. *
  1169. * @param string $type The type of selector (years, months, days, hours, minutes)
  1170. * @param string $name fieldname
  1171. * @param int $currenttime A default timestamp in GMT
  1172. * @param int $step minute spacing
  1173. * @param array $attributes - html select element attributes
  1174. * @return HTML fragment
  1175. */
  1176. public static function select_time($type, $name, $currenttime = 0, $step = 5, array $attributes = null) {
  1177. if (!$currenttime) {
  1178. $currenttime = time();
  1179. }
  1180. $currentdate = usergetdate($currenttime);
  1181. $userdatetype = $type;
  1182. $timeunits = array();
  1183. switch ($type) {
  1184. case 'years':
  1185. for ($i=1970; $i<=2020; $i++) {
  1186. $timeunits[$i] = $i;
  1187. }
  1188. $userdatetype = 'year';
  1189. break;
  1190. case 'months':
  1191. for ($i=1; $i<=12; $i++) {
  1192. $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
  1193. }
  1194. $userdatetype = 'month';
  1195. $currentdate['month'] = (int)$currentdate['mon'];
  1196. break;
  1197. case 'days':
  1198. for ($i=1; $i<=31; $i++) {
  1199. $timeunits[$i] = $i;
  1200. }
  1201. $userdatetype = 'mday';
  1202. break;
  1203. case 'hours':
  1204. for ($i=0; $i<=23; $i++) {
  1205. $timeunits[$i] = sprintf("%02d",$i);
  1206. }
  1207. break;
  1208. case 'minutes':
  1209. if ($step != 1) {
  1210. $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
  1211. }
  1212. for ($i=0; $i<=59; $i+=$step) {
  1213. $timeunits[$i] = sprintf("%02d",$i);
  1214. }
  1215. break;
  1216. default:
  1217. throw new coding_exception("Time type $type is not supported by html_writer::select_time().");
  1218. }
  1219. if (empty($attributes['id'])) {
  1220. $attributes['id'] = self::random_id('ts_');
  1221. }
  1222. $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id']));
  1223. $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide'));
  1224. return $label.$timerselector;
  1225. }
  1226. /**
  1227. * Shortcut for quick making of lists
  1228. *
  1229. * Note: 'list' is a reserved keyword ;-)
  1230. *
  1231. * @param array $items
  1232. * @param array $attributes
  1233. * @param string $tag ul or ol
  1234. * @return string
  1235. */
  1236. public static function alist(array $items, array $attributes = null, $tag = 'ul') {
  1237. $output = '';
  1238. foreach ($items as $item) {
  1239. $output .= html_writer::start_tag('li') . "\n";
  1240. $output .= $item . "\n";
  1241. $output .= html_writer::end_tag('li') . "\n";
  1242. }
  1243. return html_writer::tag($tag, $output, $attributes);
  1244. }
  1245. /**
  1246. * Returns hidden input fields created from url parameters.
  1247. *
  1248. * @param moodle_url $url
  1249. * @param array $exclude list of excluded parameters
  1250. * @return string HTML fragment
  1251. */
  1252. public static function input_hidden_params(moodle_url $url, array $exclude = null) {
  1253. $exclude = (array)$exclude;
  1254. $params = $url->params();
  1255. foreach ($exclude as $key) {
  1256. unset($params[$key]);
  1257. }
  1258. $output = '';
  1259. foreach ($params as $key => $value) {
  1260. $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
  1261. $output .= self::empty_tag('input', $attributes)."\n";
  1262. }
  1263. return $output;
  1264. }
  1265. /**
  1266. * Generate a script tag containing the the specified code.
  1267. *
  1268. * @param string $jscode the JavaScript code
  1269. * @param moodle_url|string $url optional url of the external script, $code ignored if specified
  1270. * @return string HTML, the code wrapped in <script> tags.
  1271. */
  1272. public static function script($jscode, $url=null) {
  1273. if ($jscode) {
  1274. $attributes = array('type'=>'text/javascript');
  1275. return self::tag('script', "\n//<![CDATA[\n$jscode\n//]]>\n", $attributes) . "\n";
  1276. } else if ($url) {
  1277. $attributes = array('type'=>'text/javascript', 'src'=>$url);
  1278. return self::tag('script', '', $attributes) . "\n";
  1279. } else {
  1280. return '';
  1281. }
  1282. }
  1283. /**
  1284. * Renders HTML table
  1285. *
  1286. * This method may modify the passed instance by adding some default properties if they are not set yet.
  1287. * If this is not what you want, you should make a full clone of your data before passing them to this
  1288. * method. In most cases this is not an issue at all so we do not clone by default for performance
  1289. * and memory consumption reasons.
  1290. *
  1291. * @param html_table $table data to be rendered
  1292. * @return string HTML code
  1293. */
  1294. public static function table(html_table $table) {
  1295. // prepare table data and populate missing properties with reasonable defaults
  1296. if (!empty($table->align)) {
  1297. foreach ($table->align as $key => $aa) {
  1298. if ($aa) {
  1299. $table->align[$key] = 'text-align:'. fix_align_rtl($aa) .';'; // Fix for RTL languages
  1300. } else {
  1301. $table->align[$key] = null;
  1302. }
  1303. }
  1304. }
  1305. if (!empty($table->size)) {
  1306. foreach ($table->size as $key => $ss) {
  1307. if ($ss) {
  1308. $table->size[$key] = 'width:'. $ss .';';
  1309. } else {
  1310. $table->size[$key] = null;
  1311. }
  1312. }
  1313. }
  1314. if (!empty($table->wrap)) {
  1315. foreach ($table->wrap as $key => $ww) {
  1316. if ($ww) {
  1317. $table->wrap[$key] = 'white-space:nowrap;';
  1318. } else {
  1319. $table->wrap[$key] = '';
  1320. }
  1321. }
  1322. }
  1323. if (!empty($table->head)) {
  1324. foreach ($table->head as $key => $val) {
  1325. if (!isset($table->align[$key])) {
  1326. $table->align[$key] = null;
  1327. }
  1328. if (!isset($table->size[$key])) {
  1329. $table->size[$key] = null;
  1330. }
  1331. if (!isset($table->wrap[$key])) {
  1332. $table->wrap[$key] = null;
  1333. }
  1334. }
  1335. }
  1336. if (empty($table->attributes['class'])) {
  1337. $table->attributes['class'] = 'generaltable';
  1338. }
  1339. if (!empty($table->tablealign)) {
  1340. $table->attributes['class'] .= ' boxalign' . $table->tablealign;
  1341. }
  1342. // explicitly assigned properties override those defined via $table->attributes
  1343. $table->attributes['class'] = trim($table->attributes['class']);
  1344. $attributes = array_merge($table->attributes, array(
  1345. 'id' => $table->id,
  1346. 'width' => $table->width,
  1347. 'summary' => $table->summary,
  1348. 'cellpadding' => $table->cellpadding,
  1349. 'cellspacing' => $table->cellspacing,
  1350. ));
  1351. $output = html_writer::start_tag('table', $attributes) . "\n";
  1352. $countcols = 0;
  1353. if (!empty($table->head)) {
  1354. $countcols = count($table->head);
  1355. $output .= html_writer::start_tag('thead', array()) . "\n";
  1356. $output .= html_writer::start_tag('tr', array()) . "\n";
  1357. $keys = array_keys($table->head);
  1358. $lastkey = end($keys);
  1359. foreach ($table->head as $key => $heading) {
  1360. // Convert plain string headings into html_table_cell objects
  1361. if (!($heading instanceof html_table_cell)) {
  1362. $headingtext = $heading;
  1363. $heading = new html_table_cell();
  1364. $heading->text = $headingtext;
  1365. $heading->header = true;
  1366. }
  1367. if ($heading->header !== false) {
  1368. $heading->header = true;
  1369. }
  1370. if ($heading->header && empty($heading->scope)) {
  1371. $heading->scope = 'col';
  1372. }
  1373. $heading->attributes['class'] .= ' header c' . $key;
  1374. if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
  1375. $heading->colspan = $table->headspan[$ke

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