/lib/outputrenderers.php
PHP | 4036 lines | 2040 code | 409 blank | 1587 comment | 444 complexity | 4862e94af8a96ce6044417edac40dbff MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- // This file is part of Moodle - http://moodle.org/
- //
- // Moodle is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // Moodle is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
- /**
- * Classes for rendering HTML output for Moodle.
- *
- * Please see {@link http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML}
- * for an overview.
- *
- * Included in this file are the primary renderer classes:
- * - renderer_base: The renderer outline class that all renderers
- * should inherit from.
- * - core_renderer: The standard HTML renderer.
- * - core_renderer_cli: An adaption of the standard renderer for CLI scripts.
- * - core_renderer_ajax: An adaption of the standard renderer for AJAX scripts.
- * - plugin_renderer_base: A renderer class that should be extended by all
- * plugin renderers.
- *
- * @package core
- * @category output
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
- defined('MOODLE_INTERNAL') || die();
- /**
- * Simple base class for Moodle renderers.
- *
- * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
- *
- * Also has methods to facilitate generating HTML output.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 2.0
- * @package core
- * @category output
- */
- class renderer_base {
- /**
- * @var xhtml_container_stack The xhtml_container_stack to use.
- */
- protected $opencontainers;
- /**
- * @var moodle_page The Moodle page the renderer has been created to assist with.
- */
- protected $page;
- /**
- * @var string The requested rendering target.
- */
- protected $target;
- /**
- * Constructor
- *
- * The constructor takes two arguments. The first is the page that the renderer
- * has been created to assist with, and the second is the target.
- * The target is an additional identifier that can be used to load different
- * renderers for different options.
- *
- * @param moodle_page $page the page we are doing output for.
- * @param string $target one of rendering target constants
- */
- public function __construct(moodle_page $page, $target) {
- $this->opencontainers = $page->opencontainers;
- $this->page = $page;
- $this->target = $target;
- }
- /**
- * Returns rendered widget.
- *
- * The provided widget needs to be an object that extends the renderable
- * interface.
- * If will then be rendered by a method based upon the classname for the widget.
- * For instance a widget of class `crazywidget` will be rendered by a protected
- * render_crazywidget method of this renderer.
- *
- * @param renderable $widget instance with renderable interface
- * @return string
- */
- public function render(renderable $widget) {
- $rendermethod = 'render_'.get_class($widget);
- if (method_exists($this, $rendermethod)) {
- return $this->$rendermethod($widget);
- }
- throw new coding_exception('Can not render widget, renderer method ('.$rendermethod.') not found.');
- }
- /**
- * Adds a JS action for the element with the provided id.
- *
- * This method adds a JS event for the provided component action to the page
- * and then returns the id that the event has been attached to.
- * If no id has been provided then a new ID is generated by {@link html_writer::random_id()}
- *
- * @param component_action $action
- * @param string $id
- * @return string id of element, either original submitted or random new if not supplied
- */
- public function add_action_handler(component_action $action, $id = null) {
- if (!$id) {
- $id = html_writer::random_id($action->event);
- }
- $this->page->requires->event_handler("#$id", $action->event, $action->jsfunction, $action->jsfunctionargs);
- return $id;
- }
- /**
- * Returns true is output has already started, and false if not.
- *
- * @return boolean true if the header has been printed.
- */
- public function has_started() {
- return $this->page->state >= moodle_page::STATE_IN_BODY;
- }
- /**
- * Given an array or space-separated list of classes, prepares and returns the HTML class attribute value
- *
- * @param mixed $classes Space-separated string or array of classes
- * @return string HTML class attribute value
- */
- public static function prepare_classes($classes) {
- if (is_array($classes)) {
- return implode(' ', array_unique($classes));
- }
- return $classes;
- }
- /**
- * Return the moodle_url for an image.
- *
- * The exact image location and extension is determined
- * automatically by searching for gif|png|jpg|jpeg, please
- * note there can not be diferent images with the different
- * extension. The imagename is for historical reasons
- * a relative path name, it may be changed later for core
- * images. It is recommended to not use subdirectories
- * in plugin and theme pix directories.
- *
- * There are three types of images:
- * 1/ theme images - stored in theme/mytheme/pix/,
- * use component 'theme'
- * 2/ core images - stored in /pix/,
- * overridden via theme/mytheme/pix_core/
- * 3/ plugin images - stored in mod/mymodule/pix,
- * overridden via theme/mytheme/pix_plugins/mod/mymodule/,
- * example: pix_url('comment', 'mod_glossary')
- *
- * @param string $imagename the pathname of the image
- * @param string $component full plugin name (aka component) or 'theme'
- * @return moodle_url
- */
- public function pix_url($imagename, $component = 'moodle') {
- return $this->page->theme->pix_url($imagename, $component);
- }
- }
- /**
- * Basis for all plugin renderers.
- *
- * @copyright Petr Skoda (skodak)
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 2.0
- * @package core
- * @category output
- */
- class plugin_renderer_base extends renderer_base {
- /**
- * @var renderer_base|core_renderer A reference to the current renderer.
- * The renderer provided here will be determined by the page but will in 90%
- * of cases by the {@link core_renderer}
- */
- protected $output;
- /**
- * Constructor method, calls the parent constructor
- *
- * @param moodle_page $page
- * @param string $target one of rendering target constants
- */
- public function __construct(moodle_page $page, $target) {
- if (empty($target) && $page->pagelayout === 'maintenance') {
- // If the page is using the maintenance layout then we're going to force the target to maintenance.
- // This way we'll get a special maintenance renderer that is designed to block access to API's that are likely
- // unavailable for this page layout.
- $target = RENDERER_TARGET_MAINTENANCE;
- }
- $this->output = $page->get_renderer('core', null, $target);
- parent::__construct($page, $target);
- }
- /**
- * Renders the provided widget and returns the HTML to display it.
- *
- * @param renderable $widget instance with renderable interface
- * @return string
- */
- public function render(renderable $widget) {
- $rendermethod = 'render_'.get_class($widget);
- if (method_exists($this, $rendermethod)) {
- return $this->$rendermethod($widget);
- }
- // pass to core renderer if method not found here
- return $this->output->render($widget);
- }
- /**
- * Magic method used to pass calls otherwise meant for the standard renderer
- * to it to ensure we don't go causing unnecessary grief.
- *
- * @param string $method
- * @param array $arguments
- * @return mixed
- */
- public function __call($method, $arguments) {
- if (method_exists('renderer_base', $method)) {
- throw new coding_exception('Protected method called against '.get_class($this).' :: '.$method);
- }
- if (method_exists($this->output, $method)) {
- return call_user_func_array(array($this->output, $method), $arguments);
- } else {
- throw new coding_exception('Unknown method called against '.get_class($this).' :: '.$method);
- }
- }
- }
- /**
- * The standard implementation of the core_renderer interface.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 2.0
- * @package core
- * @category output
- */
- class core_renderer extends renderer_base {
- /**
- * Do NOT use, please use <?php echo $OUTPUT->main_content() ?>
- * in layout files instead.
- * @deprecated
- * @var string used in {@link core_renderer::header()}.
- */
- const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
- /**
- * @var string Used to pass information from {@link core_renderer::doctype()} to
- * {@link core_renderer::standard_head_html()}.
- */
- protected $contenttype;
- /**
- * @var string Used by {@link core_renderer::redirect_message()} method to communicate
- * with {@link core_renderer::header()}.
- */
- protected $metarefreshtag = '';
- /**
- * @var string Unique token for the closing HTML
- */
- protected $unique_end_html_token;
- /**
- * @var string Unique token for performance information
- */
- protected $unique_performance_info_token;
- /**
- * @var string Unique token for the main content.
- */
- protected $unique_main_content_token;
- /**
- * Constructor
- *
- * @param moodle_page $page the page we are doing output for.
- * @param string $target one of rendering target constants
- */
- public function __construct(moodle_page $page, $target) {
- $this->opencontainers = $page->opencontainers;
- $this->page = $page;
- $this->target = $target;
- $this->unique_end_html_token = '%%ENDHTML-'.sesskey().'%%';
- $this->unique_performance_info_token = '%%PERFORMANCEINFO-'.sesskey().'%%';
- $this->unique_main_content_token = '[MAIN CONTENT GOES HERE - '.sesskey().']';
- }
- /**
- * Get the DOCTYPE declaration that should be used with this page. Designed to
- * be called in theme layout.php files.
- *
- * @return string the DOCTYPE declaration that should be used.
- */
- public function doctype() {
- if ($this->page->theme->doctype === 'html5') {
- $this->contenttype = 'text/html; charset=utf-8';
- return "<!DOCTYPE html>\n";
- } else if ($this->page->theme->doctype === 'xhtml5') {
- $this->contenttype = 'application/xhtml+xml; charset=utf-8';
- return "<!DOCTYPE html>\n";
- } else {
- // legacy xhtml 1.0
- $this->contenttype = 'text/html; charset=utf-8';
- return ('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n");
- }
- }
- /**
- * The attributes that should be added to the <html> tag. Designed to
- * be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function htmlattributes() {
- $return = get_html_lang(true);
- if ($this->page->theme->doctype !== 'html5') {
- $return .= ' xmlns="http://www.w3.org/1999/xhtml"';
- }
- return $return;
- }
- /**
- * The standard tags (meta tags, links to stylesheets and JavaScript, etc.)
- * that should be included in the <head> tag. Designed to be called in theme
- * layout.php files.
- *
- * @return string HTML fragment.
- */
- public function standard_head_html() {
- global $CFG, $SESSION;
- // Before we output any content, we need to ensure that certain
- // page components are set up.
- // Blocks must be set up early as they may require javascript which
- // has to be included in the page header before output is created.
- foreach ($this->page->blocks->get_regions() as $region) {
- $this->page->blocks->ensure_content_created($region, $this);
- }
- $output = '';
- $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
- $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
- if (!$this->page->cacheable) {
- $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
- $output .= '<meta http-equiv="expires" content="0" />' . "\n";
- }
- // This is only set by the {@link redirect()} method
- $output .= $this->metarefreshtag;
- // Check if a periodic refresh delay has been set and make sure we arn't
- // already meta refreshing
- if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
- $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />';
- }
- // flow player embedding support
- $this->page->requires->js_function_call('M.util.load_flowplayer');
- // Set up help link popups for all links with the helptooltip class
- $this->page->requires->js_init_call('M.util.help_popups.setup');
- // Setup help icon overlays.
- $this->page->requires->yui_module('moodle-core-popuphelp', 'M.core.init_popuphelp');
- $this->page->requires->strings_for_js(array(
- 'morehelp',
- 'loadinghelp',
- ), 'moodle');
- $this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));
- $focus = $this->page->focuscontrol;
- if (!empty($focus)) {
- if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
- // This is a horrifically bad way to handle focus but it is passed in
- // through messy formslib::moodleform
- $this->page->requires->js_function_call('old_onload_focus', array($matches[1], $matches[2]));
- } else if (strpos($focus, '.')!==false) {
- // Old style of focus, bad way to do it
- debugging('This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.', DEBUG_DEVELOPER);
- $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
- } else {
- // Focus element with given id
- $this->page->requires->js_function_call('focuscontrol', array($focus));
- }
- }
- // Get the theme stylesheet - this has to be always first CSS, this loads also styles.css from all plugins;
- // any other custom CSS can not be overridden via themes and is highly discouraged
- $urls = $this->page->theme->css_urls($this->page);
- foreach ($urls as $url) {
- $this->page->requires->css_theme($url);
- }
- // Get the theme javascript head and footer
- if ($jsurl = $this->page->theme->javascript_url(true)) {
- $this->page->requires->js($jsurl, true);
- }
- if ($jsurl = $this->page->theme->javascript_url(false)) {
- $this->page->requires->js($jsurl);
- }
- // Get any HTML from the page_requirements_manager.
- $output .= $this->page->requires->get_head_code($this->page, $this);
- // List alternate versions.
- foreach ($this->page->alternateversions as $type => $alt) {
- $output .= html_writer::empty_tag('link', array('rel' => 'alternate',
- 'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
- }
- if (!empty($CFG->additionalhtmlhead)) {
- $output .= "\n".$CFG->additionalhtmlhead;
- }
- return $output;
- }
- /**
- * The standard tags (typically skip links) that should be output just inside
- * the start of the <body> tag. Designed to be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function standard_top_of_body_html() {
- global $CFG;
- $output = $this->page->requires->get_top_of_body_code();
- if (!empty($CFG->additionalhtmltopofbody)) {
- $output .= "\n".$CFG->additionalhtmltopofbody;
- }
- $output .= $this->maintenance_warning();
- return $output;
- }
- /**
- * Scheduled maintenance warning message.
- *
- * Note: This is a nasty hack to display maintenance notice, this should be moved
- * to some general notification area once we have it.
- *
- * @return string
- */
- public function maintenance_warning() {
- global $CFG;
- $output = '';
- if (isset($CFG->maintenance_later) and $CFG->maintenance_later > time()) {
- $output .= $this->box_start('errorbox maintenancewarning');
- $output .= get_string('maintenancemodeisscheduled', 'admin', (int)(($CFG->maintenance_later-time())/60));
- $output .= $this->box_end();
- }
- return $output;
- }
- /**
- * The standard tags (typically performance information and validation links,
- * if we are in developer debug mode) that should be output in the footer area
- * of the page. Designed to be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function standard_footer_html() {
- global $CFG, $SCRIPT;
- if (during_initial_install()) {
- // Debugging info can not work before install is finished,
- // in any case we do not want any links during installation!
- return '';
- }
- // This function is normally called from a layout.php file in {@link core_renderer::header()}
- // but some of the content won't be known until later, so we return a placeholder
- // for now. This will be replaced with the real content in {@link core_renderer::footer()}.
- $output = $this->unique_performance_info_token;
- if ($this->page->devicetypeinuse == 'legacy') {
- // The legacy theme is in use print the notification
- $output .= html_writer::tag('div', get_string('legacythemeinuse'), array('class'=>'legacythemeinuse'));
- }
- // Get links to switch device types (only shown for users not on a default device)
- $output .= $this->theme_switch_links();
- if (!empty($CFG->debugpageinfo)) {
- $output .= '<div class="performanceinfo pageinfo">This page is: ' . $this->page->debug_summary() . '</div>';
- }
- if (debugging(null, DEBUG_DEVELOPER) and has_capability('moodle/site:config', context_system::instance())) { // Only in developer mode
- // Add link to profiling report if necessary
- if (function_exists('profiling_is_running') && profiling_is_running()) {
- $txt = get_string('profiledscript', 'admin');
- $title = get_string('profiledscriptview', 'admin');
- $url = $CFG->wwwroot . '/admin/tool/profiling/index.php?script=' . urlencode($SCRIPT);
- $link= '<a title="' . $title . '" href="' . $url . '">' . $txt . '</a>';
- $output .= '<div class="profilingfooter">' . $link . '</div>';
- }
- $purgeurl = new moodle_url('/admin/purgecaches.php', array('confirm' => 1,
- 'sesskey' => sesskey(), 'returnurl' => $this->page->url->out_as_local_url(false)));
- $output .= '<div class="purgecaches">' .
- html_writer::link($purgeurl, get_string('purgecaches', 'admin')) . '</div>';
- }
- if (!empty($CFG->debugvalidators)) {
- // NOTE: this is not a nice hack, $PAGE->url is not always accurate and $FULLME neither, it is not a bug if it fails. --skodak
- $output .= '<div class="validators"><ul>
- <li><a href="http://validator.w3.org/check?verbose=1&ss=1&uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
- <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
- <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=0&warnp2n3e=1&url1=' . urlencode(qualified_me()) . '">WCAG 1 (2,3) Check</a></li>
- </ul></div>';
- }
- return $output;
- }
- /**
- * Returns standard main content placeholder.
- * Designed to be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function main_content() {
- // This is here because it is the only place we can inject the "main" role over the entire main content area
- // without requiring all theme's to manually do it, and without creating yet another thing people need to
- // remember in the theme.
- // This is an unfortunate hack. DO NO EVER add anything more here.
- // DO NOT add classes.
- // DO NOT add an id.
- return '<div role="main">'.$this->unique_main_content_token.'</div>';
- }
- /**
- * The standard tags (typically script tags that are not needed earlier) that
- * should be output after everything else. Designed to be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function standard_end_of_body_html() {
- global $CFG;
- // This function is normally called from a layout.php file in {@link core_renderer::header()}
- // but some of the content won't be known until later, so we return a placeholder
- // for now. This will be replaced with the real content in {@link core_renderer::footer()}.
- $output = '';
- if (!empty($CFG->additionalhtmlfooter)) {
- $output .= "\n".$CFG->additionalhtmlfooter;
- }
- $output .= $this->unique_end_html_token;
- return $output;
- }
- /**
- * Return the standard string that says whether you are logged in (and switched
- * roles/logged in as another user).
- * @param bool $withlinks if false, then don't include any links in the HTML produced.
- * If not set, the default is the nologinlinks option from the theme config.php file,
- * and if that is not set, then links are included.
- * @return string HTML fragment.
- */
- public function login_info($withlinks = null) {
- global $USER, $CFG, $DB, $SESSION;
- if (during_initial_install()) {
- return '';
- }
- if (is_null($withlinks)) {
- $withlinks = empty($this->page->layout_options['nologinlinks']);
- }
- $loginpage = ((string)$this->page->url === get_login_url());
- $course = $this->page->course;
- if (\core\session\manager::is_loggedinas()) {
- $realuser = \core\session\manager::get_realuser();
- $fullname = fullname($realuser, true);
- if ($withlinks) {
- $loginastitle = get_string('loginas');
- $realuserinfo = " [<a href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&sesskey=".sesskey()."\"";
- $realuserinfo .= "title =\"".$loginastitle."\">$fullname</a>] ";
- } else {
- $realuserinfo = " [$fullname] ";
- }
- } else {
- $realuserinfo = '';
- }
- $loginurl = get_login_url();
- if (empty($course->id)) {
- // $course->id is not defined during installation
- return '';
- } else if (isloggedin()) {
- $context = context_course::instance($course->id);
- $fullname = fullname($USER, true);
- // Since Moodle 2.0 this link always goes to the public profile page (not the course profile page)
- if ($withlinks) {
- $linktitle = get_string('viewprofile');
- $username = "<a href=\"$CFG->wwwroot/user/profile.php?id=$USER->id\" title=\"$linktitle\">$fullname</a>";
- } else {
- $username = $fullname;
- }
- if (is_mnet_remote_user($USER) and $idprovider = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid))) {
- if ($withlinks) {
- $username .= " from <a href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
- } else {
- $username .= " from {$idprovider->name}";
- }
- }
- if (isguestuser()) {
- $loggedinas = $realuserinfo.get_string('loggedinasguest');
- if (!$loginpage && $withlinks) {
- $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
- }
- } else if (is_role_switched($course->id)) { // Has switched roles
- $rolename = '';
- if ($role = $DB->get_record('role', array('id'=>$USER->access['rsw'][$context->path]))) {
- $rolename = ': '.role_get_name($role, $context);
- }
- $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename;
- if ($withlinks) {
- $url = new moodle_url('/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>0, 'returnurl'=>$this->page->url->out_as_local_url(false)));
- $loggedinas .= ' ('.html_writer::tag('a', get_string('switchrolereturn'), array('href' => $url)).')';
- }
- } else {
- $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username);
- if ($withlinks) {
- $loggedinas .= " (<a href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
- }
- }
- } else {
- $loggedinas = get_string('loggedinnot', 'moodle');
- if (!$loginpage && $withlinks) {
- $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
- }
- }
- $loggedinas = '<div class="logininfo">'.$loggedinas.'</div>';
- if (isset($SESSION->justloggedin)) {
- unset($SESSION->justloggedin);
- if (!empty($CFG->displayloginfailures)) {
- if (!isguestuser()) {
- if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
- $loggedinas .= ' <div class="loginfailures">';
- if (empty($count->accounts)) {
- $loggedinas .= get_string('failedloginattempts', '', $count);
- } else {
- $loggedinas .= get_string('failedloginattemptsall', '', $count);
- }
- if (file_exists("$CFG->dirroot/report/log/index.php") and has_capability('report/log:view', context_system::instance())) {
- $loggedinas .= ' (<a href="'.$CFG->wwwroot.'/report/log/index.php'.
- '?chooselog=1&id=1&modid=site_errors">'.get_string('logs').'</a>)';
- }
- $loggedinas .= '</div>';
- }
- }
- }
- }
- return $loggedinas;
- }
- /**
- * Return the 'back' link that normally appears in the footer.
- *
- * @return string HTML fragment.
- */
- public function home_link() {
- global $CFG, $SITE;
- if ($this->page->pagetype == 'site-index') {
- // Special case for site home page - please do not remove
- return '<div class="sitelink">' .
- '<a title="Moodle" href="http://moodle.org/">' .
- '<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
- } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
- // Special case for during install/upgrade.
- return '<div class="sitelink">'.
- '<a title="Moodle" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
- '<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
- } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
- return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
- get_string('home') . '</a></div>';
- } else {
- return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
- format_string($this->page->course->shortname, true, array('context' => $this->page->context)) . '</a></div>';
- }
- }
- /**
- * Redirects the user by any means possible given the current state
- *
- * This function should not be called directly, it should always be called using
- * the redirect function in lib/weblib.php
- *
- * The redirect function should really only be called before page output has started
- * however it will allow itself to be called during the state STATE_IN_BODY
- *
- * @param string $encodedurl The URL to send to encoded if required
- * @param string $message The message to display to the user if any
- * @param int $delay The delay before redirecting a user, if $message has been
- * set this is a requirement and defaults to 3, set to 0 no delay
- * @param boolean $debugdisableredirect this redirect has been disabled for
- * debugging purposes. Display a message that explains, and don't
- * trigger the redirect.
- * @return string The HTML to display to the user before dying, may contain
- * meta refresh, javascript refresh, and may have set header redirects
- */
- public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
- global $CFG;
- $url = str_replace('&', '&', $encodedurl);
- switch ($this->page->state) {
- case moodle_page::STATE_BEFORE_HEADER :
- // No output yet it is safe to delivery the full arsenal of redirect methods
- if (!$debugdisableredirect) {
- // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
- $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
- $this->page->requires->js_function_call('document.location.replace', array($url), false, ($delay + 3));
- }
- $output = $this->header();
- break;
- case moodle_page::STATE_PRINTING_HEADER :
- // We should hopefully never get here
- throw new coding_exception('You cannot redirect while printing the page header');
- break;
- case moodle_page::STATE_IN_BODY :
- // We really shouldn't be here but we can deal with this
- debugging("You should really redirect before you start page output");
- if (!$debugdisableredirect) {
- $this->page->requires->js_function_call('document.location.replace', array($url), false, $delay);
- }
- $output = $this->opencontainers->pop_all_but_last();
- break;
- case moodle_page::STATE_DONE :
- // Too late to be calling redirect now
- throw new coding_exception('You cannot redirect after the entire page has been generated');
- break;
- }
- $output .= $this->notification($message, 'redirectmessage');
- $output .= '<div class="continuebutton">(<a href="'. $encodedurl .'">'. get_string('continue') .'</a>)</div>';
- if ($debugdisableredirect) {
- $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>';
- }
- $output .= $this->footer();
- return $output;
- }
- /**
- * Start output by sending the HTTP headers, and printing the HTML <head>
- * and the start of the <body>.
- *
- * To control what is printed, you should set properties on $PAGE. If you
- * are familiar with the old {@link print_header()} function from Moodle 1.9
- * you will find that there are properties on $PAGE that correspond to most
- * of the old parameters to could be passed to print_header.
- *
- * Not that, in due course, the remaining $navigation, $menu parameters here
- * will be replaced by more properties of $PAGE, but that is still to do.
- *
- * @return string HTML that you must output this, preferably immediately.
- */
- public function header() {
- global $USER, $CFG;
- if (\core\session\manager::is_loggedinas()) {
- $this->page->add_body_class('userloggedinas');
- }
- // Give themes a chance to init/alter the page object.
- $this->page->theme->init_page($this->page);
- $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
- // Find the appropriate page layout file, based on $this->page->pagelayout.
- $layoutfile = $this->page->theme->layout_file($this->page->pagelayout);
- // Render the layout using the layout file.
- $rendered = $this->render_page_layout($layoutfile);
- // Slice the rendered output into header and footer.
- $cutpos = strpos($rendered, $this->unique_main_content_token);
- if ($cutpos === false) {
- $cutpos = strpos($rendered, self::MAIN_CONTENT_TOKEN);
- $token = self::MAIN_CONTENT_TOKEN;
- } else {
- $token = $this->unique_main_content_token;
- }
- if ($cutpos === false) {
- throw new coding_exception('page layout file ' . $layoutfile . ' does not contain the main content placeholder, please include "<?php echo $OUTPUT->main_content() ?>" in theme layout file.');
- }
- $header = substr($rendered, 0, $cutpos);
- $footer = substr($rendered, $cutpos + strlen($token));
- if (empty($this->contenttype)) {
- debugging('The page layout file did not call $OUTPUT->doctype()');
- $header = $this->doctype() . $header;
- }
- // If this theme version is below 2.4 release and this is a course view page
- if ((!isset($this->page->theme->settings->version) || $this->page->theme->settings->version < 2012101500) &&
- $this->page->pagelayout === 'course' && $this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
- // check if course content header/footer have not been output during render of theme layout
- $coursecontentheader = $this->course_content_header(true);
- $coursecontentfooter = $this->course_content_footer(true);
- if (!empty($coursecontentheader)) {
- // display debug message and add header and footer right above and below main content
- // Please note that course header and footer (to be displayed above and below the whole page)
- // are not displayed in this case at all.
- // Besides the content header and footer are not displayed on any other course page
- debugging('The current theme is not optimised for 2.4, the course-specific header and footer defined in course format will not be output', DEBUG_DEVELOPER);
- $header .= $coursecontentheader;
- $footer = $coursecontentfooter. $footer;
- }
- }
- send_headers($this->contenttype, $this->page->cacheable);
- $this->opencontainers->push('header/footer', $footer);
- $this->page->set_state(moodle_page::STATE_IN_BODY);
- return $header . $this->skip_link_target('maincontent');
- }
- /**
- * Renders and outputs the page layout file.
- *
- * This is done by preparing the normal globals available to a script, and
- * then including the layout file provided by the current theme for the
- * requested layout.
- *
- * @param string $layoutfile The name of the layout file
- * @return string HTML code
- */
- protected function render_page_layout($layoutfile) {
- global $CFG, $SITE, $USER;
- // The next lines are a bit tricky. The point is, here we are in a method
- // of a renderer class, and this object may, or may not, be the same as
- // the global $OUTPUT object. When rendering the page layout file, we want to use
- // this object. However, people writing Moodle code expect the current
- // renderer to be called $OUTPUT, not $this, so define a variable called
- // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
- $OUTPUT = $this;
- $PAGE = $this->page;
- $COURSE = $this->page->course;
- ob_start();
- include($layoutfile);
- $rendered = ob_get_contents();
- ob_end_clean();
- return $rendered;
- }
- /**
- * Outputs the page's footer
- *
- * @return string HTML fragment
- */
- public function footer() {
- global $CFG, $DB;
- $output = $this->container_end_all(true);
- $footer = $this->opencontainers->pop('header/footer');
- if (debugging() and $DB and $DB->is_transaction_started()) {
- // TODO: MDL-20625 print warning - transaction will be rolled back
- }
- // Provide some performance info if required
- $performanceinfo = '';
- if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
- $perf = get_performance_info();
- if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
- $performanceinfo = $perf['html'];
- }
- }
- // We always want performance data when running a performance test, even if the user is redirected to another page.
- if (MDL_PERF_TEST && strpos($footer, $this->unique_performance_info_token) === false) {
- $footer = $this->unique_performance_info_token . $footer;
- }
- $footer = str_replace($this->unique_performance_info_token, $performanceinfo, $footer);
- $footer = str_replace($this->unique_end_html_token, $this->page->requires->get_end_code(), $footer);
- $this->page->set_state(moodle_page::STATE_DONE);
- return $output . $footer;
- }
- /**
- * Close all but the last open container. This is useful in places like error
- * handling, where you want to close all the open containers (apart from <body>)
- * before outputting the error message.
- *
- * @param bool $shouldbenone assert that the stack should be empty now - causes a
- * developer debug warning if it isn't.
- * @return string the HTML required to close any open containers inside <body>.
- */
- public function container_end_all($shouldbenone = false) {
- return $this->opencontainers->pop_all_but_last($shouldbenone);
- }
- /**
- * Returns course-specific information to be output immediately above content on any course page
- * (for the current course)
- *
- * @param bool $onlyifnotcalledbefore output content only if it has not been output before
- * @return string
- */
- public function course_content_header($onlyifnotcalledbefore = false) {
- global $CFG;
- if ($this->page->course->id == SITEID) {
- // return immediately and do not include /course/lib.php if not necessary
- return '';
- }
- static $functioncalled = false;
- if ($functioncalled && $onlyifnotcalledbefore) {
- // we have already output the content header
- return '';
- }
- require_once($CFG->dirroot.'/course/lib.php');
- $functioncalled = true;
- $courseformat = course_get_format($this->page->course);
- if (($obj = $courseformat->course_content_header()) !== null) {
- return html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-header');
- }
- return '';
- }
- /**
- * Returns course-specific information to be output immediately below content on any course page
- * (for the current course)
- *
- * @param bool $onlyifnotcalledbefore output content only if it has not been output before
- * @return string
- */
- public function course_content_footer($onlyifnotcalledbefore = false) {
- global $CFG;
- if ($this->page->course->id == SITEID) {
- // return immediately and do not include /course/lib.php if not necessary
- return '';
- }
- static $functioncalled = false;
- if ($functioncalled && $onlyifnotcalledbefore) {
- // we have already output the content footer
- return '';
- }
- $functioncalled = true;
- require_once($CFG->dirroot.'/course/lib.php');
- $courseformat = course_get_format($this->page->course);
- if (($obj = $courseformat->course_content_footer()) !== null) {
- return html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-footer');
- }
- return '';
- }
- /**
- * Returns course-specific information to be output on any course page in the header area
- * (for the current course)
- *
- * @return string
- */
- public function course_header() {
- global $CFG;
- if ($this->page->course->id == SITEID) {
- // return immediately and do not include /course/lib.php if not necessary
- return '';
- }
- require_once($CFG->dirroot.'/course/lib.php');
- $courseformat = course_get_format($this->page->course);
- if (($obj = $courseformat->course_header()) !== null) {
- return $courseformat->get_renderer($this->page)->render($obj);
- }
- return '';
- }
- /**
- * Returns course-specific information to be output on any course page in the footer area
- * (for the current course)
- *
- * @return string
- */
- public function course_footer() {
- global $CFG;
- if ($this->page->course->id == SITEID) {
- // return immediately and do not include /course/lib.php if not necessary
- return '';
- }
- require_once($CFG->dirroot.'/course/lib.php');
- $courseformat = course_get_format($this->page->course);
- if (($obj = $courseformat->course_footer()) !== null) {
- return $courseformat->get_renderer($this->page)->render($obj);
- }
- return '';
- }
- /**
- * Returns lang menu or '', this method also checks forcing of languages in courses.
- *
- * This function calls {@link core_renderer::render_single_select()} to actually display the language menu.
- *
- * @return string The lang menu HTML or empty string
- */
- public function lang_menu() {
- global $CFG;
- if (empty($CFG->langmenu)) {
- return '';
- }
- if ($this->page->course != SITEID and !empty($this->page->course->lang)) {
- // do not show lang menu if language forced
- return '';
- }
- $currlang = current_language();
- $langs = get_string_manager()->get_list_of_translations();
- if (count($langs) < 2) {
- return '';
- }
- $s = new single_select($this->page->url, 'lang', $langs, $currlang, null);
- $s->label = get_accesshide(get_string('language'));
- $s->class = 'langmenu';
- return $this->render($s);
- }
- /**
- * Output the row of editing icons for a block, as defined by the controls array.
- *
- * @param array $controls an array like {@link block_contents::$controls}.
- * @param string $blockid The ID given to the block.
- * @return string HTML fragment.
- */
- public function block_controls($actions, $blockid = null) {
- global $CFG;
- if (empty($actions)) {
- return '';
- }
- $menu = new action_menu($actions);
- if ($blockid !== null) {
- $menu->set_owner_selector('#'.$blockid);
- }
- $menu->set_constraint('.block-region');
- $menu->attributes['class'] .= ' block-control-actions commands';
- if (isset($CFG->blockeditingmenu) && !$CFG->blockeditingmenu) {
- $menu->do_not_enhance();
- }
- return $this->render($menu);
- }
- /**
- * Renders an action menu component.
- *
- * ARIA references:
- * - http://www.w3.org/WAI/GL/wiki/Using_ARIA_menus
- * - http://stackoverflow.com/questions/12279113/recommended-wai-aria-implementation-for-navigation-bar-menu
- *
- * @param action_menu $menu
- * @return string HTML
- */
- public function render_action_menu(action_menu $menu) {
- $menu->initialise_js($this->page);
- $output = html_writer::start_tag('div', $menu->attributes);
- $output .= html_writer::start_tag('ul', $menu->attributesprimary);
- foreach ($menu->get_primary_actions($this) as $action) {
- if ($action instanceof renderable) {
- $content = $this->render($action);
- } else {
- $content = $action;
- }
- $output .= html_writer::tag('li', $content, array('role' => 'presentation'));
- }
- $output .= html_writer::end_tag('ul');
- $output .= html_writer::start_tag('ul', $menu->attributessecondary);
- foreach ($menu->get_secondary_actions() as $action) {
- if ($action instanceof renderable) {
- $content = $this->render($action);
- } else {
- $content = $action;
- }
- $output .= html_writer::tag('li', $content, array('role' => 'presentation'));
- }
- $output .= html_writer::end_tag('ul');
- $output .= html_writer::end_tag('div');
- return $output;
- }
- /**
- * Renders an action_menu_link item.
- *
- * @param action_menu_link $action
- * @return string HTML fragment
- */
- protected function render_action_menu_link(action_menu_link $action) {
- static $actioncount = 0;
- $actioncount++;
- $comparetoalt = '';
- $text = '';
- if (!$action->icon || $action->primary === false) {
- $text .= html_writer::start_tag('span', array('class'=>'menu-action-text', 'id' => 'actionmenuaction-'.$actioncount));
- if ($action->text instanceof renderable) {
- $text .= $this->render($action->text);
- } else {
- $text .= $action->text;
- $comparetoalt = (string)$action->text;
- }
- $text .= html_writer::end_tag('span');
- }
- $icon = '';
- if ($action->icon) {
- $icon = $action->icon;
- if ($action->primary || !$action->actionmenu->will_be_enhanced()) {
- $action->attributes['title'] = $action->text;
- }
- if (!$action->primary && $action->actionmenu->will_be_enhanced()) {
- if ((string)$icon->attributes['alt'] === $comparetoalt) {
- $icon->attributes['alt'] = '';
- }
- if (isset($icon->attributes['title']) && (string)$icon->attributes['title'] === $comparetoalt) {
- unset($icon->attributes['title']);
- }
- }
- $icon = $this->render($icon);
- }
- // A disabled link is rendered as formatted text.
- if (!empty($action->attributes['disabled'])) {
- // Do not use div here due to nesting restriction in xhtml strict.
- return html_writer::tag('span', $icon.$text, array('class'=>'currentlink', 'role' => 'menuitem'));
- }
- $attributes = $action->attributes;
- unset($action->attributes['disabled']);
- $attributes['href'] = $action->url;
- if ($text !== '') {
- $attributes['aria-labelledby'] = 'actionmenuaction-'.$actioncount;
- }
- return html_writer::tag('a', $icon.$text, $attributes);
- }
- /**
- * Renders a primary action_menu_filler item.
- *
- * @param action_menu_link_filler $action
- * @return string HTML fragment
- */
- protected function render_action_menu_filler(action_menu_filler $action) {
- return html_writer::span(' ', 'filler');
- }
- /**
- * Renders a primary action_menu_link item.
- *
- * @param action_menu_link_primary $action
- * @return string HTML fragment
- */
- protected function render_action_menu_link_primary(action_menu_link_primary $action) {
- return $this->render_action_menu_link($action);
- }
- /**
- * Renders a secondary action_menu_link item.
- *
- * @param action_menu_link_secondary $action
- * @return string HTML fragment
- */
- protected function render_action_menu_link_secondary(action_menu_link_secondary $action) {
- return $this->render_action_menu_link($action);
- }
- /**
- * Prints a nice side block with an optional header.
- *
- * The content is described
- * by a {@link cor…
Large files files are truncated, but you can click here to view the full file