/lib/outputrenderers.php
PHP | 5233 lines | 2800 code | 543 blank | 1890 comment | 574 complexity | c161263a7273691547007b21380ca2c9 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause
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;
- /**
- * @var Mustache_Engine $mustache The mustache template compiler
- */
- private $mustache;
- /**
- * Return an instance of the mustache class.
- *
- * @since 2.9
- * @return Mustache_Engine
- */
- protected function get_mustache() {
- global $CFG;
- if ($this->mustache === null) {
- require_once("{$CFG->libdir}/filelib.php");
- $themename = $this->page->theme->name;
- $themerev = theme_get_revision();
- // Create new localcache directory.
- $cachedir = make_localcache_directory("mustache/$themerev/$themename");
- // Remove old localcache directories.
- $mustachecachedirs = glob("{$CFG->localcachedir}/mustache/*", GLOB_ONLYDIR);
- foreach ($mustachecachedirs as $localcachedir) {
- $cachedrev = [];
- preg_match("/\/mustache\/([0-9]+)$/", $localcachedir, $cachedrev);
- $cachedrev = isset($cachedrev[1]) ? intval($cachedrev[1]) : 0;
- if ($cachedrev > 0 && $cachedrev < $themerev) {
- fulldelete($localcachedir);
- }
- }
- $loader = new \core\output\mustache_filesystem_loader();
- $stringhelper = new \core\output\mustache_string_helper();
- $quotehelper = new \core\output\mustache_quote_helper();
- $jshelper = new \core\output\mustache_javascript_helper($this->page);
- $pixhelper = new \core\output\mustache_pix_helper($this);
- $shortentexthelper = new \core\output\mustache_shorten_text_helper();
- $userdatehelper = new \core\output\mustache_user_date_helper();
- // We only expose the variables that are exposed to JS templates.
- $safeconfig = $this->page->requires->get_config_for_javascript($this->page, $this);
- $helpers = array('config' => $safeconfig,
- 'str' => array($stringhelper, 'str'),
- 'quote' => array($quotehelper, 'quote'),
- 'js' => array($jshelper, 'help'),
- 'pix' => array($pixhelper, 'pix'),
- 'shortentext' => array($shortentexthelper, 'shorten'),
- 'userdate' => array($userdatehelper, 'transform'),
- );
- $this->mustache = new \core\output\mustache_engine(array(
- 'cache' => $cachedir,
- 'escape' => 's',
- 'loader' => $loader,
- 'helpers' => $helpers,
- 'pragmas' => [Mustache_Engine::PRAGMA_BLOCKS],
- // Don't allow the JavaScript helper to be executed from within another
- // helper. If it's allowed it can be used by users to inject malicious
- // JS into the page.
- 'blacklistednestedhelpers' => ['js']));
- }
- return $this->mustache;
- }
- /**
- * 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;
- }
- /**
- * Renders a template by name with the given context.
- *
- * The provided data needs to be array/stdClass made up of only simple types.
- * Simple types are array,stdClass,bool,int,float,string
- *
- * @since 2.9
- * @param array|stdClass $context Context containing data for the template.
- * @return string|boolean
- */
- public function render_from_template($templatename, $context) {
- static $templatecache = array();
- $mustache = $this->get_mustache();
- try {
- // Grab a copy of the existing helper to be restored later.
- $uniqidhelper = $mustache->getHelper('uniqid');
- } catch (Mustache_Exception_UnknownHelperException $e) {
- // Helper doesn't exist.
- $uniqidhelper = null;
- }
- // Provide 1 random value that will not change within a template
- // but will be different from template to template. This is useful for
- // e.g. aria attributes that only work with id attributes and must be
- // unique in a page.
- $mustache->addHelper('uniqid', new \core\output\mustache_uniqid_helper());
- if (isset($templatecache[$templatename])) {
- $template = $templatecache[$templatename];
- } else {
- try {
- $template = $mustache->loadTemplate($templatename);
- $templatecache[$templatename] = $template;
- } catch (Mustache_Exception_UnknownTemplateException $e) {
- throw new moodle_exception('Unknown template: ' . $templatename);
- }
- }
- $renderedtemplate = trim($template->render($context));
- // If we had an existing uniqid helper then we need to restore it to allow
- // handle nested calls of render_from_template.
- if ($uniqidhelper) {
- $mustache->addHelper('uniqid', $uniqidhelper);
- }
- return $renderedtemplate;
- }
- /**
- * 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.
- * If no render_crazywidget method exists and crazywidget implements templatable,
- * look for the 'crazywidget' template in the same component and render that.
- *
- * @param renderable $widget instance with renderable interface
- * @return string
- */
- public function render(renderable $widget) {
- $classparts = explode('\\', get_class($widget));
- // Strip namespaces.
- $classname = array_pop($classparts);
- // Remove _renderable suffixes
- $classname = preg_replace('/_renderable$/', '', $classname);
- $rendermethod = 'render_'.$classname;
- if (method_exists($this, $rendermethod)) {
- return $this->$rendermethod($widget);
- }
- if ($widget instanceof templatable) {
- $component = array_shift($classparts);
- if (!$component) {
- $component = 'core';
- }
- $template = $component . '/' . $classname;
- $context = $widget->export_for_template($this);
- return $this->render_from_template($template, $context);
- }
- 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 direct URL for an image from the pix folder.
- *
- * Use this function sparingly and never for icons. For icons use pix_icon or the pix helper in a mustache template.
- *
- * @deprecated since Moodle 3.3
- * @param string $imagename the name of the icon.
- * @param string $component specification of one plugin like in get_string()
- * @return moodle_url
- */
- public function pix_url($imagename, $component = 'moodle') {
- debugging('pix_url is deprecated. Use image_url for images and pix_icon for icons.', DEBUG_DEVELOPER);
- return $this->page->theme->image_url($imagename, $component);
- }
- /**
- * 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: image_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 image_url($imagename, $component = 'moodle') {
- return $this->page->theme->image_url($imagename, $component);
- }
- /**
- * Return the site's logo URL, if any.
- *
- * @param int $maxwidth The maximum width, or null when the maximum width does not matter.
- * @param int $maxheight The maximum height, or null when the maximum height does not matter.
- * @return moodle_url|false
- */
- public function get_logo_url($maxwidth = null, $maxheight = 200) {
- global $CFG;
- $logo = get_config('core_admin', 'logo');
- if (empty($logo)) {
- return false;
- }
- // 200px high is the default image size which should be displayed at 100px in the page to account for retina displays.
- // It's not worth the overhead of detecting and serving 2 different images based on the device.
- // Hide the requested size in the file path.
- $filepath = ((int) $maxwidth . 'x' . (int) $maxheight) . '/';
- // Use $CFG->themerev to prevent browser caching when the file changes.
- return moodle_url::make_pluginfile_url(context_system::instance()->id, 'core_admin', 'logo', $filepath,
- theme_get_revision(), $logo);
- }
- /**
- * Return the site's compact logo URL, if any.
- *
- * @param int $maxwidth The maximum width, or null when the maximum width does not matter.
- * @param int $maxheight The maximum height, or null when the maximum height does not matter.
- * @return moodle_url|false
- */
- public function get_compact_logo_url($maxwidth = 100, $maxheight = 100) {
- global $CFG;
- $logo = get_config('core_admin', 'logocompact');
- if (empty($logo)) {
- return false;
- }
- // Hide the requested size in the file path.
- $filepath = ((int) $maxwidth . 'x' . (int) $maxheight) . '/';
- // Use $CFG->themerev to prevent browser caching when the file changes.
- return moodle_url::make_pluginfile_url(context_system::instance()->id, 'core_admin', 'logocompact', $filepath,
- theme_get_revision(), $logo);
- }
- /**
- * Whether we should display the logo in the navbar.
- *
- * We will when there are no main logos, and we have compact logo.
- *
- * @return bool
- */
- public function should_display_navbar_logo() {
- $logo = $this->get_compact_logo_url();
- return !empty($logo) && !$this->should_display_main_logo();
- }
- /**
- * Whether we should display the main logo.
- *
- * @param int $headinglevel The heading level we want to check against.
- * @return bool
- */
- public function should_display_main_logo($headinglevel = 1) {
- // Only render the logo if we're on the front page or login page and the we have a logo.
- $logo = $this->get_logo_url();
- if ($headinglevel == 1 && !empty($logo)) {
- if ($this->page->pagelayout == 'frontpage' || $this->page->pagelayout == 'login') {
- return true;
- }
- }
- return false;
- }
- }
- /**
- * 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) {
- $classname = get_class($widget);
- // Strip namespaces.
- $classname = preg_replace('/^.*\\\/', '', $classname);
- // Keep a copy at this point, we may need to look for a deprecated method.
- $deprecatedmethod = 'render_'.$classname;
- // Remove _renderable suffixes
- $classname = preg_replace('/_renderable$/', '', $classname);
- $rendermethod = 'render_'.$classname;
- if (method_exists($this, $rendermethod)) {
- return $this->$rendermethod($widget);
- }
- if ($rendermethod !== $deprecatedmethod && method_exists($this, $deprecatedmethod)) {
- // This is exactly where we don't want to be.
- // If you have arrived here you have a renderable component within your plugin that has the name
- // blah_renderable, and you have a render method render_blah_renderable on your plugin.
- // In 2.8 we revamped output, as part of this change we changed slightly how renderables got rendered
- // and the _renderable suffix now gets removed when looking for a render method.
- // You need to change your renderers render_blah_renderable to render_blah.
- // Until you do this it will not be possible for a theme to override the renderer to override your method.
- // Please do it ASAP.
- static $debugged = array();
- if (!isset($debugged[$deprecatedmethod])) {
- debugging(sprintf('Deprecated call. Please rename your renderables render method from %s to %s.',
- $deprecatedmethod, $rendermethod), DEBUG_DEVELOPER);
- $debugged[$deprecatedmethod] = true;
- }
- return $this->$deprecatedmethod($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;
- /** @var custom_menu_item language The language menu if created */
- protected $language = null;
- /**
- * 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);
- $attributes = array();
- if ($this->page->theme->doctype !== 'html5') {
- $attributes['xmlns'] = 'http://www.w3.org/1999/xhtml';
- }
- // Give plugins an opportunity to add things like xml namespaces to the html element.
- // This function should return an array of html attribute names => values.
- $pluginswithfunction = get_plugins_with_function('add_htmlattributes', 'lib.php');
- foreach ($pluginswithfunction as $plugins) {
- foreach ($plugins as $function) {
- $newattrs = $function();
- unset($newattrs['dir']);
- unset($newattrs['lang']);
- unset($newattrs['xmlns']);
- unset($newattrs['xml:lang']);
- $attributes += $newattrs;
- }
- }
- foreach ($attributes as $key => $val) {
- $val = s($val);
- $return .= " $key=\"$val\"";
- }
- 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, $SITE;
- // 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 = '';
- // Give plugins an opportunity to add any head elements. The callback
- // must always return a string containing valid html head content.
- $pluginswithfunction = get_plugins_with_function('before_standard_html_head', 'lib.php');
- foreach ($pluginswithfunction as $plugins) {
- foreach ($plugins as $function) {
- $output .= $function();
- }
- }
- // Allow a url_rewrite plugin to setup any dynamic head content.
- if (isset($CFG->urlrewriteclass) && !isset($CFG->upgraderunning)) {
- $class = $CFG->urlrewriteclass;
- $output .= $class::html_head_setup();
- }
- $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
- $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\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().'" />';
- }
- // Set up help link popups for all links with the helptooltip class
- $this->page->requires->js_init_call('M.util.help_popups.setup');
- $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));
- }
- // Add noindex tag if relevant page and setting applied.
- $allowindexing = isset($CFG->allowindexing) ? $CFG->allowindexing : 0;
- $loginpages = array('login-index', 'login-signup');
- if ($allowindexing == 2 || ($allowindexing == 0 && in_array($this->page->pagetype, $loginpages))) {
- if (!isset($CFG->additionalhtmlhead)) {
- $CFG->additionalhtmlhead = '';
- }
- $CFG->additionalhtmlhead .= '<meta name="robots" content="noindex" />';
- }
- if (!empty($CFG->additionalhtmlhead)) {
- $output .= "\n".$CFG->additionalhtmlhead;
- }
- if ($this->page->pagelayout == 'frontpage') {
- $summary = s(strip_tags(format_text($SITE->summary, FORMAT_HTML)));
- if (!empty($summary)) {
- $output .= "<meta name=\"description\" content=\"$summary\" />\n";
- }
- }
- 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($this);
- if ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmltopofbody)) {
- $output .= "\n".$CFG->additionalhtmltopofbody;
- }
- // Give subsystems an opportunity to inject extra html content. The callback
- // must always return a string containing valid html.
- foreach (\core_component::get_core_subsystems() as $name => $path) {
- if ($path) {
- $output .= component_callback($name, 'before_standard_top_of_body_html', [], '');
- }
- }
- // Give plugins an opportunity to inject extra html content. The callback
- // must always return a string containing valid html.
- $pluginswithfunction = get_plugins_with_function('before_standard_top_of_body_html', 'lib.php');
- foreach ($pluginswithfunction as $plugins) {
- foreach ($plugins as $function) {
- $output .= $function();
- }
- }
- $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()) {
- $timeleft = $CFG->maintenance_later - time();
- // If timeleft less than 30 sec, set the class on block to error to highlight.
- $errorclass = ($timeleft < 30) ? 'alert-error alert-danger' : 'alert-warning';
- $output .= $this->box_start($errorclass . ' moodle-has-zindex maintenancewarning m-a-1 alert');
- $a = new stdClass();
- $a->hour = (int)($timeleft / 3600);
- $a->min = (int)(($timeleft / 60) % 60);
- $a->sec = (int)($timeleft % 60);
- if ($a->hour > 0) {
- $output .= get_string('maintenancemodeisscheduledlong', 'admin', $a);
- } else {
- $output .= get_string('maintenancemodeisscheduled', 'admin', $a);
- }
- $output .= $this->box_end();
- $this->page->requires->yui_module('moodle-core-maintenancemodetimer', 'M.core.maintenancemodetimer',
- array(array('timeleftinsec' => $timeleft)));
- $this->page->requires->strings_for_js(
- array('maintenancemodeisscheduled', 'maintenancemodeisscheduledlong', 'sitemaintenance'),
- 'admin');
- }
- 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;
- $output = '';
- 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 $output;
- }
- // Give plugins an opportunity to add any footer elements.
- // The callback must always return a string containing valid html footer content.
- $pluginswithfunction = get_plugins_with_function('standard_footer_html', 'lib.php');
- foreach ($pluginswithfunction as $plugins) {
- foreach ($plugins as $function) {
- $output .= $function();
- }
- }
- // 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">' . get_string('pageinfodebugsummary', 'core_admin',
- $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, $this->page->url is not always accurate and
- // $FULLME neither, it is not a bug if it fails. --skodak.
- $output .= '<div class="validators"><ul class="list-unstyled ml-1">
- <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>';
- }
- /**
- * Returns standard navigation between activities in a course.
- *
- * @return string the navigation HTML.
- */
- public function activity_navigation() {
- // First we should check if we want to add navigation.
- $context = $this->page->context;
- if (($this->page->pagelayout !== 'incourse' && $this->page->pagelayout !== 'frametop')
- || $context->contextlevel != CONTEXT_MODULE) {
- return '';
- }
- // If the activity is in stealth mode, show no links.
- if ($this->page->cm->is_stealth()) {
- return '';
- }
- // Get a list of all the activities in the course.
- $course = $this->page->cm->get_course();
- $modules = get_fast_modinfo($course->id)->get_cms();
- // Put the modules into an array in order by the position they are shown in the course.
- $mods = [];
- $activitylist = [];
- foreach ($modules as $module) {
- // Only add activities the user can access, aren't in stealth mode and have a url (eg. mod_label does not).
- if (!$module->uservisible || $module->is_stealth() || empty($module->url)) {
- continue;
- }
- $mods[$module->id] = $module;
- // No need to add the current module to the list for the activity dropdown menu.
- if ($module->id == $this->page->cm->id) {
- continue;
- }
- // Module name.
- $modname = $module->get_formatted_name();
- // Display the hidden text if necessary.
- if (!$module->visible) {
- $modname .= ' ' . get_string('hiddenwithbrackets');
- }
- // Module URL.
- $linkurl = new moodle_url($module->url, array('forceview' => 1));
- // Add module URL (as key) and name (as value) to the activity list array.
- $activitylist[$linkurl->out(false)] = $modname;
- }
- $nummods = count($mods);
- // If there is only one mod then do nothing.
- if ($nummods == 1) {
- return '';
- }
- // Get an array of just the course module ids used to get the cmid value based on their position in the course.
- $modids = array_keys($mods);
- // Get the position in the array of the course module we are viewing.
- $position = array_search($this->page->cm->id, $modids);
- $prevmod = null;
- $nextmod = null;
- // Check if we have a previous mod to show.
- if ($position > 0) {
- $prevmod = $mods[$modids[$position - 1]];
- }
- // Check if we have a next mod to show.
- if ($position < ($nummods - 1)) {
- $nextmod = $mods[$modids[$position + 1]];
- }
- $activitynav = new \core_course\output\activity_navigation($prevmod, $nextmod, $activitylist);
- $renderer = $this->page->get_renderer('core', 'course');
- return $renderer->render($activitynav);
- }
- /**
- * 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 ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmlfooter)) {
- $output .= "\n".$CFG->additionalhtmlfooter;
- }
- $output .= $this->unique_end_html_token;
- return $output;
- }
- /**
- * The standard HTML that should be output just before the <footer> tag.
- * Designed to be called in theme layout.php files.
- *
- * @return string HTML fragment.
- */
- public function standard_after_main_region_html() {
- global $CFG;
- $output = '';
- if ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmlbottomofbody)) {
- $output .= "\n".$CFG->additionalhtmlbottomofbody;
- }
- // Give subsystems an opportunity to inject extra html content. The callback
- // must always return a string containing valid html.
- foreach (\core_component::get_core_subsystems() as $name => $path) {
- if ($path) {
- $output .= component_callback($name, 'standard_after_main_region_html', [], '');
- }
- }
- // Give plugins an opportunity to inject extra html content. The callback
- // must always return a string containing valid html.
- $pluginswithfunction = get_plugins_with_function('standard_after_main_region_html', 'lib.php');
- foreach ($pluginswithfunction as $plugins) {
- foreach ($plugins as $function) {
- $output .= $function();
- }
- }
- 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']);
- }
- $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 = '';
- }
- $loginpage = $this->is_login_page();
- $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()) {
- // Include this file only when required.
- require_once($CFG->dirroot . '/user/lib.php');
- if ($count = user_count_login_failures($USER)) {
- $loggedinas .= '<div class="loginfailures">';
- $a = new stdClass();
- $a->attempts = $count;
- $loggedinas .= get_string('failedloginattempts', '', $a);
- if (file_exists("$CFG->dirroot/report/log/index.php") and has_capability('report/log:view', context_system::instance())) {
- $loggedinas .= ' ('.html_writer::link(new moodle_url('/report/log/index.php', array('chooselog' => 1,
- 'id' => 0 , 'modid' => 'site_errors')), get_string('logs')).')';
- }
- $loggedinas .= '</div>';
- }
- }
- }
- }
- return $loggedinas;
- }
- /**
- * Check whether the current page is a login page.
- *
- * @since Moodle 2.9
- * @return bool
- */
- protected function is_login_page() {
- // This is a real bit of a hack, but its a rarety that we need to do something like this.
- // In fact the login pages should be only these two pages and as exposing this as an option for all pages
- // could lead to abuse (or at least unneedingly complex code) the hack is the way to go.
- return in_array(
- $this->page->url->out_as_local_url(false, array()),
- array(
- '/login/index.php',
- '/login/forgot_password.php',
- )
- );
- }
- /**
- * 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 src="' . $this->image_url('moodlelogo_grayhat') . '" alt="'.get_string('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 src="' . $this->image_url('moodlelogo_grayhat') . '" alt="'.get_string('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 …
Large files files are truncated, but you can click here to view the full file