| 1 | efrain | 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 | /**
 | 
        
           |  |  | 18 |  * Classes for rendering HTML output for Moodle.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * Please see {@link http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML}
 | 
        
           |  |  | 21 |  * for an overview.
 | 
        
           |  |  | 22 |  *
 | 
        
           |  |  | 23 |  * Included in this file are the primary renderer classes:
 | 
        
           |  |  | 24 |  *     - renderer_base:         The renderer outline class that all renderers
 | 
        
           |  |  | 25 |  *                              should inherit from.
 | 
        
           |  |  | 26 |  *     - core_renderer:         The standard HTML renderer.
 | 
        
           |  |  | 27 |  *     - core_renderer_cli:     An adaption of the standard renderer for CLI scripts.
 | 
        
           |  |  | 28 |  *     - core_renderer_ajax:    An adaption of the standard renderer for AJAX scripts.
 | 
        
           |  |  | 29 |  *     - plugin_renderer_base:  A renderer class that should be extended by all
 | 
        
           |  |  | 30 |  *                              plugin renderers.
 | 
        
           |  |  | 31 |  *
 | 
        
           |  |  | 32 |  * @package core
 | 
        
           |  |  | 33 |  * @category output
 | 
        
           |  |  | 34 |  * @copyright  2009 Tim Hunt
 | 
        
           |  |  | 35 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 36 |  */
 | 
        
           |  |  | 37 |   | 
        
           |  |  | 38 | use core\di;
 | 
        
           |  |  | 39 | use core\hook\manager as hook_manager;
 | 
        
           |  |  | 40 | use core\hook\output\after_standard_main_region_html_generation;
 | 
        
           |  |  | 41 | use core\hook\output\before_footer_html_generation;
 | 
        
           |  |  | 42 | use core\hook\output\before_html_attributes;
 | 
        
           |  |  | 43 | use core\hook\output\before_http_headers;
 | 
        
           |  |  | 44 | use core\hook\output\before_standard_footer_html_generation;
 | 
        
           |  |  | 45 | use core\hook\output\before_standard_top_of_body_html_generation;
 | 
        
           |  |  | 46 | use core\output\named_templatable;
 | 
        
           |  |  | 47 | use core_completion\cm_completion_details;
 | 
        
           |  |  | 48 | use core_course\output\activity_information;
 | 
        
           |  |  | 49 |   | 
        
           |  |  | 50 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 | /**
 | 
        
           |  |  | 53 |  * Simple base class for Moodle renderers.
 | 
        
           |  |  | 54 |  *
 | 
        
           |  |  | 55 |  * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
 | 
        
           |  |  | 56 |  *
 | 
        
           |  |  | 57 |  * Also has methods to facilitate generating HTML output.
 | 
        
           |  |  | 58 |  *
 | 
        
           |  |  | 59 |  * @copyright 2009 Tim Hunt
 | 
        
           |  |  | 60 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 61 |  * @since Moodle 2.0
 | 
        
           |  |  | 62 |  * @package core
 | 
        
           |  |  | 63 |  * @category output
 | 
        
           |  |  | 64 |  */
 | 
        
           |  |  | 65 | class renderer_base {
 | 
        
           |  |  | 66 |     /**
 | 
        
           |  |  | 67 |      * @var xhtml_container_stack The xhtml_container_stack to use.
 | 
        
           |  |  | 68 |      */
 | 
        
           |  |  | 69 |     protected $opencontainers;
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |     /**
 | 
        
           |  |  | 72 |      * @var moodle_page The Moodle page the renderer has been created to assist with.
 | 
        
           |  |  | 73 |      */
 | 
        
           |  |  | 74 |     protected $page;
 | 
        
           |  |  | 75 |   | 
        
           |  |  | 76 |     /**
 | 
        
           |  |  | 77 |      * @var string The requested rendering target.
 | 
        
           |  |  | 78 |      */
 | 
        
           |  |  | 79 |     protected $target;
 | 
        
           |  |  | 80 |   | 
        
           |  |  | 81 |     /**
 | 
        
           |  |  | 82 |      * @var Mustache_Engine $mustache The mustache template compiler
 | 
        
           |  |  | 83 |      */
 | 
        
           |  |  | 84 |     private $mustache;
 | 
        
           |  |  | 85 |   | 
        
           |  |  | 86 |     /**
 | 
        
           |  |  | 87 |      * @var array $templatecache The mustache template cache.
 | 
        
           |  |  | 88 |      */
 | 
        
           |  |  | 89 |     protected $templatecache = [];
 | 
        
           |  |  | 90 |   | 
        
           |  |  | 91 |     /**
 | 
        
           |  |  | 92 |      * Return an instance of the mustache class.
 | 
        
           |  |  | 93 |      *
 | 
        
           |  |  | 94 |      * @since 2.9
 | 
        
           |  |  | 95 |      * @return Mustache_Engine
 | 
        
           |  |  | 96 |      */
 | 
        
           |  |  | 97 |     protected function get_mustache() {
 | 
        
           |  |  | 98 |         global $CFG;
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 |         if ($this->mustache === null) {
 | 
        
           |  |  | 101 |             require_once("{$CFG->libdir}/filelib.php");
 | 
        
           |  |  | 102 |   | 
        
           |  |  | 103 |             $themename = $this->page->theme->name;
 | 
        
           |  |  | 104 |             $themerev = theme_get_revision();
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |             // Create new localcache directory.
 | 
        
           |  |  | 107 |             $cachedir = make_localcache_directory("mustache/$themerev/$themename");
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |             // Remove old localcache directories.
 | 
        
           |  |  | 110 |             $mustachecachedirs = glob("{$CFG->localcachedir}/mustache/*", GLOB_ONLYDIR);
 | 
        
           |  |  | 111 |             foreach ($mustachecachedirs as $localcachedir) {
 | 
        
           |  |  | 112 |                 $cachedrev = [];
 | 
        
           |  |  | 113 |                 preg_match("/\/mustache\/([0-9]+)$/", $localcachedir, $cachedrev);
 | 
        
           |  |  | 114 |                 $cachedrev = isset($cachedrev[1]) ? intval($cachedrev[1]) : 0;
 | 
        
           |  |  | 115 |                 if ($cachedrev > 0 && $cachedrev < $themerev) {
 | 
        
           |  |  | 116 |                     fulldelete($localcachedir);
 | 
        
           |  |  | 117 |                 }
 | 
        
           |  |  | 118 |             }
 | 
        
           |  |  | 119 |   | 
        
           |  |  | 120 |             $loader = new \core\output\mustache_filesystem_loader();
 | 
        
           |  |  | 121 |             $stringhelper = new \core\output\mustache_string_helper();
 | 
        
           |  |  | 122 |             $cleanstringhelper = new \core\output\mustache_clean_string_helper();
 | 
        
           |  |  | 123 |             $quotehelper = new \core\output\mustache_quote_helper();
 | 
        
           |  |  | 124 |             $jshelper = new \core\output\mustache_javascript_helper($this->page);
 | 
        
           |  |  | 125 |             $pixhelper = new \core\output\mustache_pix_helper($this);
 | 
        
           |  |  | 126 |             $shortentexthelper = new \core\output\mustache_shorten_text_helper();
 | 
        
           |  |  | 127 |             $userdatehelper = new \core\output\mustache_user_date_helper();
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |             // We only expose the variables that are exposed to JS templates.
 | 
        
           |  |  | 130 |             $safeconfig = $this->page->requires->get_config_for_javascript($this->page, $this);
 | 
        
           |  |  | 131 |   | 
        
           |  |  | 132 |             $helpers = array('config' => $safeconfig,
 | 
        
           |  |  | 133 |                              'str' => array($stringhelper, 'str'),
 | 
        
           |  |  | 134 |                              'cleanstr' => array($cleanstringhelper, 'cleanstr'),
 | 
        
           |  |  | 135 |                              'quote' => array($quotehelper, 'quote'),
 | 
        
           |  |  | 136 |                              'js' => array($jshelper, 'help'),
 | 
        
           |  |  | 137 |                              'pix' => array($pixhelper, 'pix'),
 | 
        
           |  |  | 138 |                              'shortentext' => array($shortentexthelper, 'shorten'),
 | 
        
           |  |  | 139 |                              'userdate' => array($userdatehelper, 'transform'),
 | 
        
           |  |  | 140 |                          );
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |             $this->mustache = new \core\output\mustache_engine(array(
 | 
        
           |  |  | 143 |                 'cache' => $cachedir,
 | 
        
           |  |  | 144 |                 'escape' => 's',
 | 
        
           |  |  | 145 |                 'loader' => $loader,
 | 
        
           |  |  | 146 |                 'helpers' => $helpers,
 | 
        
           |  |  | 147 |                 'pragmas' => [Mustache_Engine::PRAGMA_BLOCKS],
 | 
        
           |  |  | 148 |                 // Don't allow the JavaScript helper to be executed from within another
 | 
        
           |  |  | 149 |                 // helper. If it's allowed it can be used by users to inject malicious
 | 
        
           |  |  | 150 |                 // JS into the page.
 | 
        
           |  |  | 151 |                 'disallowednestedhelpers' => ['js'],
 | 
        
           |  |  | 152 |                 // Disable lambda rendering - content in helpers is already rendered, no need to render it again.
 | 
        
           |  |  | 153 |                 'disable_lambda_rendering' => true,
 | 
        
           |  |  | 154 |             ));
 | 
        
           |  |  | 155 |   | 
        
           |  |  | 156 |         }
 | 
        
           |  |  | 157 |   | 
        
           |  |  | 158 |         return $this->mustache;
 | 
        
           |  |  | 159 |     }
 | 
        
           |  |  | 160 |   | 
        
           |  |  | 161 |   | 
        
           |  |  | 162 |     /**
 | 
        
           |  |  | 163 |      * Constructor
 | 
        
           |  |  | 164 |      *
 | 
        
           |  |  | 165 |      * The constructor takes two arguments. The first is the page that the renderer
 | 
        
           |  |  | 166 |      * has been created to assist with, and the second is the target.
 | 
        
           |  |  | 167 |      * The target is an additional identifier that can be used to load different
 | 
        
           |  |  | 168 |      * renderers for different options.
 | 
        
           |  |  | 169 |      *
 | 
        
           |  |  | 170 |      * @param moodle_page $page the page we are doing output for.
 | 
        
           |  |  | 171 |      * @param string $target one of rendering target constants
 | 
        
           |  |  | 172 |      */
 | 
        
           |  |  | 173 |     public function __construct(moodle_page $page, $target) {
 | 
        
           |  |  | 174 |         $this->opencontainers = $page->opencontainers;
 | 
        
           |  |  | 175 |         $this->page = $page;
 | 
        
           |  |  | 176 |         $this->target = $target;
 | 
        
           |  |  | 177 |     }
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |     /**
 | 
        
           |  |  | 180 |      * Renders a template by name with the given context.
 | 
        
           |  |  | 181 |      *
 | 
        
           |  |  | 182 |      * The provided data needs to be array/stdClass made up of only simple types.
 | 
        
           |  |  | 183 |      * Simple types are array,stdClass,bool,int,float,string
 | 
        
           |  |  | 184 |      *
 | 
        
           |  |  | 185 |      * @since 2.9
 | 
        
           |  |  | 186 |      * @param array|stdClass $context Context containing data for the template.
 | 
        
           |  |  | 187 |      * @return string|boolean
 | 
        
           |  |  | 188 |      */
 | 
        
           |  |  | 189 |     public function render_from_template($templatename, $context) {
 | 
        
           |  |  | 190 |         $mustache = $this->get_mustache();
 | 
        
           |  |  | 191 |   | 
        
           |  |  | 192 |         if ($mustache->hasHelper('uniqid')) {
 | 
        
           |  |  | 193 |             // Grab a copy of the existing helper to be restored later.
 | 
        
           |  |  | 194 |             $uniqidhelper = $mustache->getHelper('uniqid');
 | 
        
           |  |  | 195 |         } else {
 | 
        
           |  |  | 196 |             // Helper doesn't exist.
 | 
        
           |  |  | 197 |             $uniqidhelper = null;
 | 
        
           |  |  | 198 |         }
 | 
        
           |  |  | 199 |   | 
        
           |  |  | 200 |         // Provide 1 random value that will not change within a template
 | 
        
           |  |  | 201 |         // but will be different from template to template. This is useful for
 | 
        
           |  |  | 202 |         // e.g. aria attributes that only work with id attributes and must be
 | 
        
           |  |  | 203 |         // unique in a page.
 | 
        
           |  |  | 204 |         $mustache->addHelper('uniqid', new \core\output\mustache_uniqid_helper());
 | 
        
           |  |  | 205 |         if (isset($this->templatecache[$templatename])) {
 | 
        
           |  |  | 206 |             $template = $this->templatecache[$templatename];
 | 
        
           |  |  | 207 |         } else {
 | 
        
           |  |  | 208 |             try {
 | 
        
           |  |  | 209 |                 $template = $mustache->loadTemplate($templatename);
 | 
        
           |  |  | 210 |                 $this->templatecache[$templatename] = $template;
 | 
        
           |  |  | 211 |             } catch (Mustache_Exception_UnknownTemplateException $e) {
 | 
        
           |  |  | 212 |                 throw new moodle_exception('Unknown template: ' . $templatename);
 | 
        
           |  |  | 213 |             }
 | 
        
           |  |  | 214 |         }
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 |         $renderedtemplate = trim($template->render($context));
 | 
        
           |  |  | 217 |   | 
        
           |  |  | 218 |         // If we had an existing uniqid helper then we need to restore it to allow
 | 
        
           |  |  | 219 |         // handle nested calls of render_from_template.
 | 
        
           |  |  | 220 |         if ($uniqidhelper) {
 | 
        
           |  |  | 221 |             $mustache->addHelper('uniqid', $uniqidhelper);
 | 
        
           |  |  | 222 |         }
 | 
        
           |  |  | 223 |   | 
        
           |  |  | 224 |         return $renderedtemplate;
 | 
        
           |  |  | 225 |     }
 | 
        
           |  |  | 226 |   | 
        
           |  |  | 227 |   | 
        
           |  |  | 228 |     /**
 | 
        
           |  |  | 229 |      * Returns rendered widget.
 | 
        
           |  |  | 230 |      *
 | 
        
           |  |  | 231 |      * The provided widget needs to be an object that extends the renderable
 | 
        
           |  |  | 232 |      * interface.
 | 
        
           |  |  | 233 |      * If will then be rendered by a method based upon the classname for the widget.
 | 
        
           |  |  | 234 |      * For instance a widget of class `crazywidget` will be rendered by a protected
 | 
        
           |  |  | 235 |      * render_crazywidget method of this renderer.
 | 
        
           |  |  | 236 |      * If no render_crazywidget method exists and crazywidget implements templatable,
 | 
        
           |  |  | 237 |      * look for the 'crazywidget' template in the same component and render that.
 | 
        
           |  |  | 238 |      *
 | 
        
           |  |  | 239 |      * @param renderable $widget instance with renderable interface
 | 
        
           |  |  | 240 |      * @return string
 | 
        
           |  |  | 241 |      */
 | 
        
           |  |  | 242 |     public function render(renderable $widget) {
 | 
        
           |  |  | 243 |         $classparts = explode('\\', get_class($widget));
 | 
        
           |  |  | 244 |         // Strip namespaces.
 | 
        
           |  |  | 245 |         $classname = array_pop($classparts);
 | 
        
           |  |  | 246 |         // Remove _renderable suffixes.
 | 
        
           |  |  | 247 |         $classname = preg_replace('/_renderable$/', '', $classname);
 | 
        
           |  |  | 248 |   | 
        
           |  |  | 249 |         $rendermethod = "render_{$classname}";
 | 
        
           |  |  | 250 |         if (method_exists($this, $rendermethod)) {
 | 
        
           |  |  | 251 |             // Call the render_[widget_name] function.
 | 
        
           |  |  | 252 |             // Note: This has a higher priority than the named_templatable to allow the theme to override the template.
 | 
        
           |  |  | 253 |             return $this->$rendermethod($widget);
 | 
        
           |  |  | 254 |         }
 | 
        
           |  |  | 255 |   | 
        
           |  |  | 256 |         if ($widget instanceof named_templatable) {
 | 
        
           |  |  | 257 |             // This is a named templatable.
 | 
        
           |  |  | 258 |             // Fetch the template name from the get_template_name function instead.
 | 
        
           |  |  | 259 |             // Note: This has higher priority than the guessed template name.
 | 
        
           |  |  | 260 |             return $this->render_from_template(
 | 
        
           |  |  | 261 |                 $widget->get_template_name($this),
 | 
        
           |  |  | 262 |                 $widget->export_for_template($this)
 | 
        
           |  |  | 263 |             );
 | 
        
           |  |  | 264 |         }
 | 
        
           |  |  | 265 |   | 
        
           |  |  | 266 |         if ($widget instanceof templatable) {
 | 
        
           |  |  | 267 |             // Guess the templat ename based on the class name.
 | 
        
           |  |  | 268 |             // Note: There's no benefit to moving this aboved the named_templatable and this approach is more costly.
 | 
        
           |  |  | 269 |             $component = array_shift($classparts);
 | 
        
           |  |  | 270 |             if (!$component) {
 | 
        
           |  |  | 271 |                 $component = 'core';
 | 
        
           |  |  | 272 |             }
 | 
        
           |  |  | 273 |             $template = $component . '/' . $classname;
 | 
        
           |  |  | 274 |             $context = $widget->export_for_template($this);
 | 
        
           |  |  | 275 |             return $this->render_from_template($template, $context);
 | 
        
           |  |  | 276 |         }
 | 
        
           |  |  | 277 |         throw new coding_exception("Can not render widget, renderer method ('{$rendermethod}') not found.");
 | 
        
           |  |  | 278 |     }
 | 
        
           |  |  | 279 |   | 
        
           |  |  | 280 |     /**
 | 
        
           |  |  | 281 |      * Adds a JS action for the element with the provided id.
 | 
        
           |  |  | 282 |      *
 | 
        
           |  |  | 283 |      * This method adds a JS event for the provided component action to the page
 | 
        
           |  |  | 284 |      * and then returns the id that the event has been attached to.
 | 
        
           |  |  | 285 |      * If no id has been provided then a new ID is generated by {@link html_writer::random_id()}
 | 
        
           |  |  | 286 |      *
 | 
        
           |  |  | 287 |      * @param component_action $action
 | 
        
           |  |  | 288 |      * @param string $id
 | 
        
           |  |  | 289 |      * @return string id of element, either original submitted or random new if not supplied
 | 
        
           |  |  | 290 |      */
 | 
        
           |  |  | 291 |     public function add_action_handler(component_action $action, $id = null) {
 | 
        
           |  |  | 292 |         if (!$id) {
 | 
        
           |  |  | 293 |             $id = html_writer::random_id($action->event);
 | 
        
           |  |  | 294 |         }
 | 
        
           |  |  | 295 |         $this->page->requires->event_handler("#$id", $action->event, $action->jsfunction, $action->jsfunctionargs);
 | 
        
           |  |  | 296 |         return $id;
 | 
        
           |  |  | 297 |     }
 | 
        
           |  |  | 298 |   | 
        
           |  |  | 299 |     /**
 | 
        
           |  |  | 300 |      * Returns true is output has already started, and false if not.
 | 
        
           |  |  | 301 |      *
 | 
        
           |  |  | 302 |      * @return boolean true if the header has been printed.
 | 
        
           |  |  | 303 |      */
 | 
        
           |  |  | 304 |     public function has_started() {
 | 
        
           |  |  | 305 |         return $this->page->state >= moodle_page::STATE_IN_BODY;
 | 
        
           |  |  | 306 |     }
 | 
        
           |  |  | 307 |   | 
        
           |  |  | 308 |     /**
 | 
        
           |  |  | 309 |      * Given an array or space-separated list of classes, prepares and returns the HTML class attribute value
 | 
        
           |  |  | 310 |      *
 | 
        
           |  |  | 311 |      * @param mixed $classes Space-separated string or array of classes
 | 
        
           |  |  | 312 |      * @return string HTML class attribute value
 | 
        
           |  |  | 313 |      */
 | 
        
           |  |  | 314 |     public static function prepare_classes($classes) {
 | 
        
           |  |  | 315 |         if (is_array($classes)) {
 | 
        
           |  |  | 316 |             return implode(' ', array_unique($classes));
 | 
        
           |  |  | 317 |         }
 | 
        
           |  |  | 318 |         return $classes;
 | 
        
           |  |  | 319 |     }
 | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |     /**
 | 
        
           |  |  | 322 |      * Return the direct URL for an image from the pix folder.
 | 
        
           |  |  | 323 |      *
 | 
        
           |  |  | 324 |      * Use this function sparingly and never for icons. For icons use pix_icon or the pix helper in a mustache template.
 | 
        
           |  |  | 325 |      *
 | 
        
           |  |  | 326 |      * @deprecated since Moodle 3.3
 | 
        
           |  |  | 327 |      * @param string $imagename the name of the icon.
 | 
        
           |  |  | 328 |      * @param string $component specification of one plugin like in get_string()
 | 
        
           |  |  | 329 |      * @return moodle_url
 | 
        
           |  |  | 330 |      */
 | 
        
           |  |  | 331 |     public function pix_url($imagename, $component = 'moodle') {
 | 
        
           |  |  | 332 |         debugging('pix_url is deprecated. Use image_url for images and pix_icon for icons.', DEBUG_DEVELOPER);
 | 
        
           |  |  | 333 |         return $this->page->theme->image_url($imagename, $component);
 | 
        
           |  |  | 334 |     }
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |     /**
 | 
        
           |  |  | 337 |      * Return the moodle_url for an image.
 | 
        
           |  |  | 338 |      *
 | 
        
           |  |  | 339 |      * The exact image location and extension is determined
 | 
        
           |  |  | 340 |      * automatically by searching for gif|png|jpg|jpeg, please
 | 
        
           |  |  | 341 |      * note there can not be diferent images with the different
 | 
        
           |  |  | 342 |      * extension. The imagename is for historical reasons
 | 
        
           |  |  | 343 |      * a relative path name, it may be changed later for core
 | 
        
           |  |  | 344 |      * images. It is recommended to not use subdirectories
 | 
        
           |  |  | 345 |      * in plugin and theme pix directories.
 | 
        
           |  |  | 346 |      *
 | 
        
           |  |  | 347 |      * There are three types of images:
 | 
        
           |  |  | 348 |      * 1/ theme images  - stored in theme/mytheme/pix/,
 | 
        
           |  |  | 349 |      *                    use component 'theme'
 | 
        
           |  |  | 350 |      * 2/ core images   - stored in /pix/,
 | 
        
           |  |  | 351 |      *                    overridden via theme/mytheme/pix_core/
 | 
        
           |  |  | 352 |      * 3/ plugin images - stored in mod/mymodule/pix,
 | 
        
           |  |  | 353 |      *                    overridden via theme/mytheme/pix_plugins/mod/mymodule/,
 | 
        
           |  |  | 354 |      *                    example: image_url('comment', 'mod_glossary')
 | 
        
           |  |  | 355 |      *
 | 
        
           |  |  | 356 |      * @param string $imagename the pathname of the image
 | 
        
           |  |  | 357 |      * @param string $component full plugin name (aka component) or 'theme'
 | 
        
           |  |  | 358 |      * @return moodle_url
 | 
        
           |  |  | 359 |      */
 | 
        
           |  |  | 360 |     public function image_url($imagename, $component = 'moodle') {
 | 
        
           |  |  | 361 |         return $this->page->theme->image_url($imagename, $component);
 | 
        
           |  |  | 362 |     }
 | 
        
           |  |  | 363 |   | 
        
           |  |  | 364 |     /**
 | 
        
           |  |  | 365 |      * Return the site's logo URL, if any.
 | 
        
           |  |  | 366 |      *
 | 
        
           |  |  | 367 |      * @param int $maxwidth The maximum width, or null when the maximum width does not matter.
 | 
        
           |  |  | 368 |      * @param int $maxheight The maximum height, or null when the maximum height does not matter.
 | 
        
           |  |  | 369 |      * @return moodle_url|false
 | 
        
           |  |  | 370 |      */
 | 
        
           |  |  | 371 |     public function get_logo_url($maxwidth = null, $maxheight = 200) {
 | 
        
           |  |  | 372 |         global $CFG;
 | 
        
           |  |  | 373 |         $logo = get_config('core_admin', 'logo');
 | 
        
           |  |  | 374 |         if (empty($logo)) {
 | 
        
           |  |  | 375 |             return false;
 | 
        
           |  |  | 376 |         }
 | 
        
           |  |  | 377 |   | 
        
           |  |  | 378 |         // 200px high is the default image size which should be displayed at 100px in the page to account for retina displays.
 | 
        
           |  |  | 379 |         // It's not worth the overhead of detecting and serving 2 different images based on the device.
 | 
        
           |  |  | 380 |   | 
        
           |  |  | 381 |         // Hide the requested size in the file path.
 | 
        
           |  |  | 382 |         $filepath = ((int) $maxwidth . 'x' . (int) $maxheight) . '/';
 | 
        
           |  |  | 383 |   | 
        
           |  |  | 384 |         // Use $CFG->themerev to prevent browser caching when the file changes.
 | 
        
           |  |  | 385 |         return moodle_url::make_pluginfile_url(context_system::instance()->id, 'core_admin', 'logo', $filepath,
 | 
        
           |  |  | 386 |             theme_get_revision(), $logo);
 | 
        
           |  |  | 387 |     }
 | 
        
           |  |  | 388 |   | 
        
           |  |  | 389 |     /**
 | 
        
           |  |  | 390 |      * Return the site's compact logo URL, if any.
 | 
        
           |  |  | 391 |      *
 | 
        
           |  |  | 392 |      * @param int $maxwidth The maximum width, or null when the maximum width does not matter.
 | 
        
           |  |  | 393 |      * @param int $maxheight The maximum height, or null when the maximum height does not matter.
 | 
        
           |  |  | 394 |      * @return moodle_url|false
 | 
        
           |  |  | 395 |      */
 | 
        
           |  |  | 396 |     public function get_compact_logo_url($maxwidth = 300, $maxheight = 300) {
 | 
        
           |  |  | 397 |         global $CFG;
 | 
        
           |  |  | 398 |         $logo = get_config('core_admin', 'logocompact');
 | 
        
           |  |  | 399 |         if (empty($logo)) {
 | 
        
           |  |  | 400 |             return false;
 | 
        
           |  |  | 401 |         }
 | 
        
           |  |  | 402 |   | 
        
           |  |  | 403 |         // Hide the requested size in the file path.
 | 
        
           |  |  | 404 |         $filepath = ((int) $maxwidth . 'x' . (int) $maxheight) . '/';
 | 
        
           |  |  | 405 |   | 
        
           |  |  | 406 |         // Use $CFG->themerev to prevent browser caching when the file changes.
 | 
        
           |  |  | 407 |         return moodle_url::make_pluginfile_url(context_system::instance()->id, 'core_admin', 'logocompact', $filepath,
 | 
        
           |  |  | 408 |             theme_get_revision(), $logo);
 | 
        
           |  |  | 409 |     }
 | 
        
           |  |  | 410 |   | 
        
           |  |  | 411 |     /**
 | 
        
           |  |  | 412 |      * Whether we should display the logo in the navbar.
 | 
        
           |  |  | 413 |      *
 | 
        
           |  |  | 414 |      * We will when there are no main logos, and we have compact logo.
 | 
        
           |  |  | 415 |      *
 | 
        
           |  |  | 416 |      * @return bool
 | 
        
           |  |  | 417 |      */
 | 
        
           |  |  | 418 |     public function should_display_navbar_logo() {
 | 
        
           |  |  | 419 |         $logo = $this->get_compact_logo_url();
 | 
        
           |  |  | 420 |         return !empty($logo);
 | 
        
           |  |  | 421 |     }
 | 
        
           |  |  | 422 |   | 
        
           |  |  | 423 |     /**
 | 
        
           |  |  | 424 |      * Whether we should display the main logo.
 | 
        
           |  |  | 425 |      * @deprecated since Moodle 4.0
 | 
        
           |  |  | 426 |      * @todo final deprecation. To be removed in Moodle 4.4 MDL-73165.
 | 
        
           |  |  | 427 |      * @param int $headinglevel The heading level we want to check against.
 | 
        
           |  |  | 428 |      * @return bool
 | 
        
           |  |  | 429 |      */
 | 
        
           |  |  | 430 |     public function should_display_main_logo($headinglevel = 1) {
 | 
        
           |  |  | 431 |         debugging('should_display_main_logo() is deprecated and will be removed in Moodle 4.4.', DEBUG_DEVELOPER);
 | 
        
           |  |  | 432 |         // Only render the logo if we're on the front page or login page and the we have a logo.
 | 
        
           |  |  | 433 |         $logo = $this->get_logo_url();
 | 
        
           |  |  | 434 |         if ($headinglevel == 1 && !empty($logo)) {
 | 
        
           |  |  | 435 |             if ($this->page->pagelayout == 'frontpage' || $this->page->pagelayout == 'login') {
 | 
        
           |  |  | 436 |                 return true;
 | 
        
           |  |  | 437 |             }
 | 
        
           |  |  | 438 |         }
 | 
        
           |  |  | 439 |   | 
        
           |  |  | 440 |         return false;
 | 
        
           |  |  | 441 |     }
 | 
        
           |  |  | 442 |   | 
        
           |  |  | 443 |   | 
        
           |  |  | 444 |     /**
 | 
        
           |  |  | 445 |      * Returns the moodle page object.
 | 
        
           |  |  | 446 |      *
 | 
        
           |  |  | 447 |      * @return moodle_page
 | 
        
           |  |  | 448 |      */
 | 
        
           |  |  | 449 |     public function get_page(): moodle_page {
 | 
        
           |  |  | 450 |         return $this->page;
 | 
        
           |  |  | 451 |     }
 | 
        
           |  |  | 452 | }
 | 
        
           |  |  | 453 |   | 
        
           |  |  | 454 |   | 
        
           |  |  | 455 | /**
 | 
        
           |  |  | 456 |  * Basis for all plugin renderers.
 | 
        
           |  |  | 457 |  *
 | 
        
           |  |  | 458 |  * @copyright Petr Skoda (skodak)
 | 
        
           |  |  | 459 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 460 |  * @since Moodle 2.0
 | 
        
           |  |  | 461 |  * @package core
 | 
        
           |  |  | 462 |  * @category output
 | 
        
           |  |  | 463 |  */
 | 
        
           |  |  | 464 | class plugin_renderer_base extends renderer_base {
 | 
        
           |  |  | 465 |   | 
        
           |  |  | 466 |     /**
 | 
        
           |  |  | 467 |      * @var renderer_base|core_renderer A reference to the current renderer.
 | 
        
           |  |  | 468 |      * The renderer provided here will be determined by the page but will in 90%
 | 
        
           |  |  | 469 |      * of cases by the {@link core_renderer}
 | 
        
           |  |  | 470 |      */
 | 
        
           |  |  | 471 |     protected $output;
 | 
        
           |  |  | 472 |   | 
        
           |  |  | 473 |     /**
 | 
        
           |  |  | 474 |      * Constructor method, calls the parent constructor
 | 
        
           |  |  | 475 |      *
 | 
        
           |  |  | 476 |      * @param moodle_page $page
 | 
        
           |  |  | 477 |      * @param string $target one of rendering target constants
 | 
        
           |  |  | 478 |      */
 | 
        
           |  |  | 479 |     public function __construct(moodle_page $page, $target) {
 | 
        
           |  |  | 480 |         if (empty($target) && $page->pagelayout === 'maintenance') {
 | 
        
           |  |  | 481 |             // If the page is using the maintenance layout then we're going to force the target to maintenance.
 | 
        
           |  |  | 482 |             // This way we'll get a special maintenance renderer that is designed to block access to API's that are likely
 | 
        
           |  |  | 483 |             // unavailable for this page layout.
 | 
        
           |  |  | 484 |             $target = RENDERER_TARGET_MAINTENANCE;
 | 
        
           |  |  | 485 |         }
 | 
        
           |  |  | 486 |         $this->output = $page->get_renderer('core', null, $target);
 | 
        
           |  |  | 487 |         parent::__construct($page, $target);
 | 
        
           |  |  | 488 |     }
 | 
        
           |  |  | 489 |   | 
        
           |  |  | 490 |     /**
 | 
        
           |  |  | 491 |      * Renders the provided widget and returns the HTML to display it.
 | 
        
           |  |  | 492 |      *
 | 
        
           |  |  | 493 |      * @param renderable $widget instance with renderable interface
 | 
        
           |  |  | 494 |      * @return string
 | 
        
           |  |  | 495 |      */
 | 
        
           |  |  | 496 |     public function render(renderable $widget) {
 | 
        
           |  |  | 497 |         $classname = get_class($widget);
 | 
        
           |  |  | 498 |   | 
        
           |  |  | 499 |         // Strip namespaces.
 | 
        
           |  |  | 500 |         $classname = preg_replace('/^.*\\\/', '', $classname);
 | 
        
           |  |  | 501 |   | 
        
           |  |  | 502 |         // Keep a copy at this point, we may need to look for a deprecated method.
 | 
        
           |  |  | 503 |         $deprecatedmethod = "render_{$classname}";
 | 
        
           |  |  | 504 |   | 
        
           |  |  | 505 |         // Remove _renderable suffixes.
 | 
        
           |  |  | 506 |         $classname = preg_replace('/_renderable$/', '', $classname);
 | 
        
           |  |  | 507 |         $rendermethod = "render_{$classname}";
 | 
        
           |  |  | 508 |   | 
        
           |  |  | 509 |         if (method_exists($this, $rendermethod)) {
 | 
        
           |  |  | 510 |             // Call the render_[widget_name] function.
 | 
        
           |  |  | 511 |             // Note: This has a higher priority than the named_templatable to allow the theme to override the template.
 | 
        
           |  |  | 512 |             return $this->$rendermethod($widget);
 | 
        
           |  |  | 513 |         }
 | 
        
           |  |  | 514 |   | 
        
           |  |  | 515 |         if ($widget instanceof named_templatable) {
 | 
        
           |  |  | 516 |             // This is a named templatable.
 | 
        
           |  |  | 517 |             // Fetch the template name from the get_template_name function instead.
 | 
        
           |  |  | 518 |             // Note: This has higher priority than the deprecated method which is not overridable by themes anyway.
 | 
        
           |  |  | 519 |             return $this->render_from_template(
 | 
        
           |  |  | 520 |                 $widget->get_template_name($this),
 | 
        
           |  |  | 521 |                 $widget->export_for_template($this)
 | 
        
           |  |  | 522 |             );
 | 
        
           |  |  | 523 |         }
 | 
        
           |  |  | 524 |   | 
        
           |  |  | 525 |         if ($rendermethod !== $deprecatedmethod && method_exists($this, $deprecatedmethod)) {
 | 
        
           |  |  | 526 |             // This is exactly where we don't want to be.
 | 
        
           |  |  | 527 |             // If you have arrived here you have a renderable component within your plugin that has the name
 | 
        
           |  |  | 528 |             // blah_renderable, and you have a render method render_blah_renderable on your plugin.
 | 
        
           |  |  | 529 |             // In 2.8 we revamped output, as part of this change we changed slightly how renderables got rendered
 | 
        
           |  |  | 530 |             // and the _renderable suffix now gets removed when looking for a render method.
 | 
        
           |  |  | 531 |             // You need to change your renderers render_blah_renderable to render_blah.
 | 
        
           |  |  | 532 |             // Until you do this it will not be possible for a theme to override the renderer to override your method.
 | 
        
           |  |  | 533 |             // Please do it ASAP.
 | 
        
           |  |  | 534 |             static $debugged = [];
 | 
        
           |  |  | 535 |             if (!isset($debugged[$deprecatedmethod])) {
 | 
        
           |  |  | 536 |                 debugging(sprintf(
 | 
        
           |  |  | 537 |                     'Deprecated call. Please rename your renderables render method from %s to %s.',
 | 
        
           |  |  | 538 |                     $deprecatedmethod,
 | 
        
           |  |  | 539 |                     $rendermethod
 | 
        
           |  |  | 540 |                 ), DEBUG_DEVELOPER);
 | 
        
           |  |  | 541 |                 $debugged[$deprecatedmethod] = true;
 | 
        
           |  |  | 542 |             }
 | 
        
           |  |  | 543 |             return $this->$deprecatedmethod($widget);
 | 
        
           |  |  | 544 |         }
 | 
        
           |  |  | 545 |   | 
        
           |  |  | 546 |         // Pass to core renderer if method not found here.
 | 
        
           |  |  | 547 |         // Note: this is not a parent. This is _new_ renderer which respects the requested format, and output type.
 | 
        
           |  |  | 548 |         return $this->output->render($widget);
 | 
        
           |  |  | 549 |     }
 | 
        
           |  |  | 550 |   | 
        
           |  |  | 551 |     /**
 | 
        
           |  |  | 552 |      * Magic method used to pass calls otherwise meant for the standard renderer
 | 
        
           |  |  | 553 |      * to it to ensure we don't go causing unnecessary grief.
 | 
        
           |  |  | 554 |      *
 | 
        
           |  |  | 555 |      * @param string $method
 | 
        
           |  |  | 556 |      * @param array $arguments
 | 
        
           |  |  | 557 |      * @return mixed
 | 
        
           |  |  | 558 |      */
 | 
        
           |  |  | 559 |     public function __call($method, $arguments) {
 | 
        
           |  |  | 560 |         if (method_exists('renderer_base', $method)) {
 | 
        
           |  |  | 561 |             throw new coding_exception('Protected method called against '.get_class($this).' :: '.$method);
 | 
        
           |  |  | 562 |         }
 | 
        
           |  |  | 563 |         if (method_exists($this->output, $method)) {
 | 
        
           |  |  | 564 |             return call_user_func_array(array($this->output, $method), $arguments);
 | 
        
           |  |  | 565 |         } else {
 | 
        
           |  |  | 566 |             throw new coding_exception('Unknown method called against '.get_class($this).' :: '.$method);
 | 
        
           |  |  | 567 |         }
 | 
        
           |  |  | 568 |     }
 | 
        
           |  |  | 569 | }
 | 
        
           |  |  | 570 |   | 
        
           |  |  | 571 |   | 
        
           |  |  | 572 | /**
 | 
        
           |  |  | 573 |  * The standard implementation of the core_renderer interface.
 | 
        
           |  |  | 574 |  *
 | 
        
           |  |  | 575 |  * @copyright 2009 Tim Hunt
 | 
        
           |  |  | 576 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 577 |  * @since Moodle 2.0
 | 
        
           |  |  | 578 |  * @package core
 | 
        
           |  |  | 579 |  * @category output
 | 
        
           |  |  | 580 |  */
 | 
        
           |  |  | 581 | class core_renderer extends renderer_base {
 | 
        
           |  |  | 582 |     /**
 | 
        
           |  |  | 583 |      * Do NOT use, please use <?php echo $OUTPUT->main_content() ?>
 | 
        
           |  |  | 584 |      * in layout files instead.
 | 
        
           |  |  | 585 |      * @deprecated
 | 
        
           |  |  | 586 |      * @var string used in {@link core_renderer::header()}.
 | 
        
           |  |  | 587 |      */
 | 
        
           |  |  | 588 |     const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
 | 
        
           |  |  | 589 |   | 
        
           |  |  | 590 |     /**
 | 
        
           |  |  | 591 |      * @var string Used to pass information from {@link core_renderer::doctype()} to
 | 
        
           |  |  | 592 |      * {@link core_renderer::standard_head_html()}.
 | 
        
           |  |  | 593 |      */
 | 
        
           |  |  | 594 |     protected $contenttype;
 | 
        
           |  |  | 595 |   | 
        
           |  |  | 596 |     /**
 | 
        
           |  |  | 597 |      * @var string Used by {@link core_renderer::redirect_message()} method to communicate
 | 
        
           |  |  | 598 |      * with {@link core_renderer::header()}.
 | 
        
           |  |  | 599 |      */
 | 
        
           |  |  | 600 |     protected $metarefreshtag = '';
 | 
        
           |  |  | 601 |   | 
        
           |  |  | 602 |     /**
 | 
        
           |  |  | 603 |      * @var string Unique token for the closing HTML
 | 
        
           |  |  | 604 |      */
 | 
        
           |  |  | 605 |     protected $unique_end_html_token;
 | 
        
           |  |  | 606 |   | 
        
           |  |  | 607 |     /**
 | 
        
           |  |  | 608 |      * @var string Unique token for performance information
 | 
        
           |  |  | 609 |      */
 | 
        
           |  |  | 610 |     protected $unique_performance_info_token;
 | 
        
           |  |  | 611 |   | 
        
           |  |  | 612 |     /**
 | 
        
           |  |  | 613 |      * @var string Unique token for the main content.
 | 
        
           |  |  | 614 |      */
 | 
        
           |  |  | 615 |     protected $unique_main_content_token;
 | 
        
           |  |  | 616 |   | 
        
           |  |  | 617 |     /** @var custom_menu_item language The language menu if created */
 | 
        
           |  |  | 618 |     protected $language = null;
 | 
        
           |  |  | 619 |   | 
        
           |  |  | 620 |     /** @var string The current selector for an element being streamed into */
 | 
        
           |  |  | 621 |     protected $currentselector = '';
 | 
        
           |  |  | 622 |   | 
        
           |  |  | 623 |     /** @var string The current element tag which is being streamed into */
 | 
        
           |  |  | 624 |     protected $currentelement = '';
 | 
        
           |  |  | 625 |   | 
        
           |  |  | 626 |     /**
 | 
        
           |  |  | 627 |      * Constructor
 | 
        
           |  |  | 628 |      *
 | 
        
           |  |  | 629 |      * @param moodle_page $page the page we are doing output for.
 | 
        
           |  |  | 630 |      * @param string $target one of rendering target constants
 | 
        
           |  |  | 631 |      */
 | 
        
           |  |  | 632 |     public function __construct(moodle_page $page, $target) {
 | 
        
           |  |  | 633 |         $this->opencontainers = $page->opencontainers;
 | 
        
           |  |  | 634 |         $this->page = $page;
 | 
        
           |  |  | 635 |         $this->target = $target;
 | 
        
           |  |  | 636 |   | 
        
           |  |  | 637 |         $this->unique_end_html_token = '%%ENDHTML-'.sesskey().'%%';
 | 
        
           |  |  | 638 |         $this->unique_performance_info_token = '%%PERFORMANCEINFO-'.sesskey().'%%';
 | 
        
           |  |  | 639 |         $this->unique_main_content_token = '[MAIN CONTENT GOES HERE - '.sesskey().']';
 | 
        
           |  |  | 640 |     }
 | 
        
           |  |  | 641 |   | 
        
           |  |  | 642 |     /**
 | 
        
           |  |  | 643 |      * Get the DOCTYPE declaration that should be used with this page. Designed to
 | 
        
           |  |  | 644 |      * be called in theme layout.php files.
 | 
        
           |  |  | 645 |      *
 | 
        
           |  |  | 646 |      * @return string the DOCTYPE declaration that should be used.
 | 
        
           |  |  | 647 |      */
 | 
        
           |  |  | 648 |     public function doctype() {
 | 
        
           |  |  | 649 |         if ($this->page->theme->doctype === 'html5') {
 | 
        
           |  |  | 650 |             $this->contenttype = 'text/html; charset=utf-8';
 | 
        
           |  |  | 651 |             return "<!DOCTYPE html>\n";
 | 
        
           |  |  | 652 |   | 
        
           |  |  | 653 |         } else if ($this->page->theme->doctype === 'xhtml5') {
 | 
        
           |  |  | 654 |             $this->contenttype = 'application/xhtml+xml; charset=utf-8';
 | 
        
           |  |  | 655 |             return "<!DOCTYPE html>\n";
 | 
        
           |  |  | 656 |   | 
        
           |  |  | 657 |         } else {
 | 
        
           |  |  | 658 |             // legacy xhtml 1.0
 | 
        
           |  |  | 659 |             $this->contenttype = 'text/html; charset=utf-8';
 | 
        
           |  |  | 660 |             return ('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n");
 | 
        
           |  |  | 661 |         }
 | 
        
           |  |  | 662 |     }
 | 
        
           |  |  | 663 |   | 
        
           |  |  | 664 |     /**
 | 
        
           |  |  | 665 |      * The attributes that should be added to the <html> tag. Designed to
 | 
        
           |  |  | 666 |      * be called in theme layout.php files.
 | 
        
           |  |  | 667 |      *
 | 
        
           |  |  | 668 |      * @return string HTML fragment.
 | 
        
           |  |  | 669 |      */
 | 
        
           |  |  | 670 |     public function htmlattributes() {
 | 
        
           |  |  | 671 |         $return = get_html_lang(true);
 | 
        
           |  |  | 672 |   | 
        
           |  |  | 673 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 674 |         // This is a critical page path.
 | 
        
           |  |  | 675 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 676 |         require_once(__DIR__ . '/classes/hook/output/before_html_attributes.php');
 | 
        
           |  |  | 677 |   | 
        
           |  |  | 678 |         $hook = new before_html_attributes($this);
 | 
        
           |  |  | 679 |   | 
        
           |  |  | 680 |         if ($this->page->theme->doctype !== 'html5') {
 | 
        
           |  |  | 681 |             $hook->add_attribute('xmlns', 'http://www.w3.org/1999/xhtml');
 | 
        
           |  |  | 682 |         }
 | 
        
           |  |  | 683 |   | 
        
           |  |  | 684 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 685 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 686 |   | 
        
           |  |  | 687 |         foreach ($hook->get_attributes() as $key => $val) {
 | 
        
           |  |  | 688 |             $val = s($val);
 | 
        
           |  |  | 689 |             $return .= " $key=\"$val\"";
 | 
        
           |  |  | 690 |         }
 | 
        
           |  |  | 691 |   | 
        
           |  |  | 692 |         return $return;
 | 
        
           |  |  | 693 |     }
 | 
        
           |  |  | 694 |   | 
        
           |  |  | 695 |     /**
 | 
        
           |  |  | 696 |      * The standard tags (meta tags, links to stylesheets and JavaScript, etc.)
 | 
        
           |  |  | 697 |      * that should be included in the <head> tag. Designed to be called in theme
 | 
        
           |  |  | 698 |      * layout.php files.
 | 
        
           |  |  | 699 |      *
 | 
        
           |  |  | 700 |      * @return string HTML fragment.
 | 
        
           |  |  | 701 |      */
 | 
        
           |  |  | 702 |     public function standard_head_html() {
 | 
        
           |  |  | 703 |         global $CFG, $SESSION, $SITE;
 | 
        
           |  |  | 704 |   | 
        
           |  |  | 705 |         // Before we output any content, we need to ensure that certain
 | 
        
           |  |  | 706 |         // page components are set up.
 | 
        
           |  |  | 707 |   | 
        
           |  |  | 708 |         // Blocks must be set up early as they may require javascript which
 | 
        
           |  |  | 709 |         // has to be included in the page header before output is created.
 | 
        
           |  |  | 710 |         foreach ($this->page->blocks->get_regions() as $region) {
 | 
        
           |  |  | 711 |             $this->page->blocks->ensure_content_created($region, $this);
 | 
        
           |  |  | 712 |         }
 | 
        
           |  |  | 713 |   | 
        
           |  |  | 714 |         // Give plugins an opportunity to add any head elements. The callback
 | 
        
           |  |  | 715 |         // must always return a string containing valid html head content.
 | 
        
           |  |  | 716 |   | 
        
           |  |  | 717 |         $hook = new \core\hook\output\before_standard_head_html_generation($this);
 | 
        
           |  |  | 718 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 719 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 720 |   | 
        
           |  |  | 721 |         // Allow a url_rewrite plugin to setup any dynamic head content.
 | 
        
           |  |  | 722 |         if (isset($CFG->urlrewriteclass) && !isset($CFG->upgraderunning)) {
 | 
        
           |  |  | 723 |             $class = $CFG->urlrewriteclass;
 | 
        
           |  |  | 724 |             $hook->add_html($class::html_head_setup());
 | 
        
           |  |  | 725 |         }
 | 
        
           |  |  | 726 |   | 
        
           |  |  | 727 |         $hook->add_html('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n");
 | 
        
           |  |  | 728 |         $hook->add_html('<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n");
 | 
        
           |  |  | 729 |         // This is only set by the {@link redirect()} method
 | 
        
           |  |  | 730 |         $hook->add_html($this->metarefreshtag);
 | 
        
           |  |  | 731 |   | 
        
           |  |  | 732 |         // Check if a periodic refresh delay has been set and make sure we arn't
 | 
        
           |  |  | 733 |         // already meta refreshing
 | 
        
           |  |  | 734 |         if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
 | 
        
           |  |  | 735 |             $hook->add_html(
 | 
        
           |  |  | 736 |                 html_writer::empty_tag('meta', [
 | 
        
           |  |  | 737 |                     'http-equiv' => 'refresh',
 | 
        
           |  |  | 738 |                     'content' => $this->page->periodicrefreshdelay . ';url='.$this->page->url->out(),
 | 
        
           |  |  | 739 |                 ]),
 | 
        
           |  |  | 740 |             );
 | 
        
           |  |  | 741 |         }
 | 
        
           |  |  | 742 |   | 
        
           |  |  | 743 |         $output = $hook->get_output();
 | 
        
           |  |  | 744 |   | 
        
           |  |  | 745 |         // Set up help link popups for all links with the helptooltip class
 | 
        
           |  |  | 746 |         $this->page->requires->js_init_call('M.util.help_popups.setup');
 | 
        
           |  |  | 747 |   | 
        
           |  |  | 748 |         $focus = $this->page->focuscontrol;
 | 
        
           |  |  | 749 |         if (!empty($focus)) {
 | 
        
           |  |  | 750 |             if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
 | 
        
           |  |  | 751 |                 // This is a horrifically bad way to handle focus but it is passed in
 | 
        
           |  |  | 752 |                 // through messy formslib::moodleform
 | 
        
           |  |  | 753 |                 $this->page->requires->js_function_call('old_onload_focus', array($matches[1], $matches[2]));
 | 
        
           |  |  | 754 |             } else if (strpos($focus, '.')!==false) {
 | 
        
           |  |  | 755 |                 // Old style of focus, bad way to do it
 | 
        
           |  |  | 756 |                 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);
 | 
        
           |  |  | 757 |                 $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
 | 
        
           |  |  | 758 |             } else {
 | 
        
           |  |  | 759 |                 // Focus element with given id
 | 
        
           |  |  | 760 |                 $this->page->requires->js_function_call('focuscontrol', array($focus));
 | 
        
           |  |  | 761 |             }
 | 
        
           |  |  | 762 |         }
 | 
        
           |  |  | 763 |   | 
        
           |  |  | 764 |         // Get the theme stylesheet - this has to be always first CSS, this loads also styles.css from all plugins;
 | 
        
           |  |  | 765 |         // any other custom CSS can not be overridden via themes and is highly discouraged
 | 
        
           |  |  | 766 |         $urls = $this->page->theme->css_urls($this->page);
 | 
        
           |  |  | 767 |         foreach ($urls as $url) {
 | 
        
           |  |  | 768 |             $this->page->requires->css_theme($url);
 | 
        
           |  |  | 769 |         }
 | 
        
           |  |  | 770 |   | 
        
           |  |  | 771 |         // Get the theme javascript head and footer
 | 
        
           |  |  | 772 |         if ($jsurl = $this->page->theme->javascript_url(true)) {
 | 
        
           |  |  | 773 |             $this->page->requires->js($jsurl, true);
 | 
        
           |  |  | 774 |         }
 | 
        
           |  |  | 775 |         if ($jsurl = $this->page->theme->javascript_url(false)) {
 | 
        
           |  |  | 776 |             $this->page->requires->js($jsurl);
 | 
        
           |  |  | 777 |         }
 | 
        
           |  |  | 778 |   | 
        
           |  |  | 779 |         // Get any HTML from the page_requirements_manager.
 | 
        
           |  |  | 780 |         $output .= $this->page->requires->get_head_code($this->page, $this);
 | 
        
           |  |  | 781 |   | 
        
           |  |  | 782 |         // List alternate versions.
 | 
        
           |  |  | 783 |         foreach ($this->page->alternateversions as $type => $alt) {
 | 
        
           |  |  | 784 |             $output .= html_writer::empty_tag('link', array('rel' => 'alternate',
 | 
        
           |  |  | 785 |                     'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
 | 
        
           |  |  | 786 |         }
 | 
        
           |  |  | 787 |   | 
        
           |  |  | 788 |         // Add noindex tag if relevant page and setting applied.
 | 
        
           |  |  | 789 |         $allowindexing = isset($CFG->allowindexing) ? $CFG->allowindexing : 0;
 | 
        
           |  |  | 790 |         $loginpages = array('login-index', 'login-signup');
 | 
        
           |  |  | 791 |         if ($allowindexing == 2 || ($allowindexing == 0 && in_array($this->page->pagetype, $loginpages))) {
 | 
        
           |  |  | 792 |             if (!isset($CFG->additionalhtmlhead)) {
 | 
        
           |  |  | 793 |                 $CFG->additionalhtmlhead = '';
 | 
        
           |  |  | 794 |             }
 | 
        
           |  |  | 795 |             $CFG->additionalhtmlhead .= '<meta name="robots" content="noindex" />';
 | 
        
           |  |  | 796 |         }
 | 
        
           |  |  | 797 |   | 
        
           |  |  | 798 |         if (!empty($CFG->additionalhtmlhead)) {
 | 
        
           |  |  | 799 |             $output .= "\n".$CFG->additionalhtmlhead;
 | 
        
           |  |  | 800 |         }
 | 
        
           |  |  | 801 |   | 
        
           |  |  | 802 |         if ($this->page->pagelayout == 'frontpage') {
 | 
        
           |  |  | 803 |             $summary = s(strip_tags(format_text($SITE->summary, FORMAT_HTML)));
 | 
        
           |  |  | 804 |             if (!empty($summary)) {
 | 
        
           |  |  | 805 |                 $output .= "<meta name=\"description\" content=\"$summary\" />\n";
 | 
        
           |  |  | 806 |             }
 | 
        
           |  |  | 807 |         }
 | 
        
           |  |  | 808 |   | 
        
           |  |  | 809 |         return $output;
 | 
        
           |  |  | 810 |     }
 | 
        
           |  |  | 811 |   | 
        
           |  |  | 812 |     /**
 | 
        
           |  |  | 813 |      * The standard tags (typically skip links) that should be output just inside
 | 
        
           |  |  | 814 |      * the start of the <body> tag. Designed to be called in theme layout.php files.
 | 
        
           |  |  | 815 |      *
 | 
        
           |  |  | 816 |      * @return string HTML fragment.
 | 
        
           |  |  | 817 |      */
 | 
        
           |  |  | 818 |     public function standard_top_of_body_html() {
 | 
        
           |  |  | 819 |         global $CFG;
 | 
        
           |  |  | 820 |         $output = $this->page->requires->get_top_of_body_code($this);
 | 
        
           |  |  | 821 |         if ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmltopofbody)) {
 | 
        
           |  |  | 822 |             $output .= "\n".$CFG->additionalhtmltopofbody;
 | 
        
           |  |  | 823 |         }
 | 
        
           |  |  | 824 |   | 
        
           |  |  | 825 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 826 |         // This is a critical page path.
 | 
        
           |  |  | 827 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 828 |         require_once(__DIR__ . '/classes/hook/output/before_standard_top_of_body_html_generation.php');
 | 
        
           |  |  | 829 |   | 
        
           |  |  | 830 |         // Allow components to add content to the top of the body.
 | 
        
           |  |  | 831 |         $hook = new before_standard_top_of_body_html_generation($this, $output);
 | 
        
           |  |  | 832 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 833 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 834 |         $output = $hook->get_output();
 | 
        
           |  |  | 835 |   | 
        
           |  |  | 836 |         $output .= $this->maintenance_warning();
 | 
        
           |  |  | 837 |   | 
        
           |  |  | 838 |         return $output;
 | 
        
           |  |  | 839 |     }
 | 
        
           |  |  | 840 |   | 
        
           |  |  | 841 |     /**
 | 
        
           |  |  | 842 |      * Scheduled maintenance warning message.
 | 
        
           |  |  | 843 |      *
 | 
        
           |  |  | 844 |      * Note: This is a nasty hack to display maintenance notice, this should be moved
 | 
        
           |  |  | 845 |      *       to some general notification area once we have it.
 | 
        
           |  |  | 846 |      *
 | 
        
           |  |  | 847 |      * @return string
 | 
        
           |  |  | 848 |      */
 | 
        
           |  |  | 849 |     public function maintenance_warning() {
 | 
        
           |  |  | 850 |         global $CFG;
 | 
        
           |  |  | 851 |   | 
        
           |  |  | 852 |         $output = '';
 | 
        
           |  |  | 853 |         if (isset($CFG->maintenance_later) and $CFG->maintenance_later > time()) {
 | 
        
           |  |  | 854 |             $timeleft = $CFG->maintenance_later - time();
 | 
        
           |  |  | 855 |             // If timeleft less than 30 sec, set the class on block to error to highlight.
 | 
        
           |  |  | 856 |             $errorclass = ($timeleft < 30) ? 'alert-error alert-danger' : 'alert-warning';
 | 
        
           |  |  | 857 |             $output .= $this->box_start($errorclass . ' moodle-has-zindex maintenancewarning m-3 alert');
 | 
        
           |  |  | 858 |             $a = new stdClass();
 | 
        
           |  |  | 859 |             $a->hour = (int)($timeleft / 3600);
 | 
        
           |  |  | 860 |             $a->min = (int)(floor($timeleft / 60) % 60);
 | 
        
           |  |  | 861 |             $a->sec = (int)($timeleft % 60);
 | 
        
           |  |  | 862 |             if ($a->hour > 0) {
 | 
        
           |  |  | 863 |                 $output .= get_string('maintenancemodeisscheduledlong', 'admin', $a);
 | 
        
           |  |  | 864 |             } else {
 | 
        
           |  |  | 865 |                 $output .= get_string('maintenancemodeisscheduled', 'admin', $a);
 | 
        
           |  |  | 866 |             }
 | 
        
           |  |  | 867 |   | 
        
           |  |  | 868 |             $output .= $this->box_end();
 | 
        
           |  |  | 869 |             $this->page->requires->yui_module('moodle-core-maintenancemodetimer', 'M.core.maintenancemodetimer',
 | 
        
           |  |  | 870 |                     array(array('timeleftinsec' => $timeleft)));
 | 
        
           |  |  | 871 |             $this->page->requires->strings_for_js(
 | 
        
           |  |  | 872 |                     array('maintenancemodeisscheduled', 'maintenancemodeisscheduledlong', 'sitemaintenance'),
 | 
        
           |  |  | 873 |                     'admin');
 | 
        
           |  |  | 874 |         }
 | 
        
           |  |  | 875 |         return $output;
 | 
        
           |  |  | 876 |     }
 | 
        
           |  |  | 877 |   | 
        
           |  |  | 878 |     /**
 | 
        
           |  |  | 879 |      * content that should be output in the footer area
 | 
        
           |  |  | 880 |      * of the page. Designed to be called in theme layout.php files.
 | 
        
           |  |  | 881 |      *
 | 
        
           |  |  | 882 |      * @return string HTML fragment.
 | 
        
           |  |  | 883 |      */
 | 
        
           |  |  | 884 |     public function standard_footer_html() {
 | 
        
           |  |  | 885 |         if (during_initial_install()) {
 | 
        
           |  |  | 886 |             // Debugging info can not work before install is finished,
 | 
        
           |  |  | 887 |             // in any case we do not want any links during installation!
 | 
        
           |  |  | 888 |             return '';
 | 
        
           |  |  | 889 |         }
 | 
        
           |  |  | 890 |   | 
        
           |  |  | 891 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 892 |         // This is a critical page path.
 | 
        
           |  |  | 893 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 894 |         require_once(__DIR__ . '/classes/hook/output/before_standard_footer_html_generation.php');
 | 
        
           |  |  | 895 |   | 
        
           |  |  | 896 |         $hook = new before_standard_footer_html_generation($this);
 | 
        
           |  |  | 897 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 898 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 899 |         $output = $hook->get_output();
 | 
        
           |  |  | 900 |   | 
        
           |  |  | 901 |         if ($this->page->devicetypeinuse == 'legacy') {
 | 
        
           |  |  | 902 |             // The legacy theme is in use print the notification
 | 
        
           |  |  | 903 |             $output .= html_writer::tag('div', get_string('legacythemeinuse'), array('class'=>'legacythemeinuse'));
 | 
        
           |  |  | 904 |         }
 | 
        
           |  |  | 905 |   | 
        
           |  |  | 906 |         // Get links to switch device types (only shown for users not on a default device)
 | 
        
           |  |  | 907 |         $output .= $this->theme_switch_links();
 | 
        
           |  |  | 908 |   | 
        
           |  |  | 909 |         return $output;
 | 
        
           |  |  | 910 |     }
 | 
        
           |  |  | 911 |   | 
        
           |  |  | 912 |     /**
 | 
        
           |  |  | 913 |      * Performance information and validation links for debugging.
 | 
        
           |  |  | 914 |      *
 | 
        
           |  |  | 915 |      * @return string HTML fragment.
 | 
        
           |  |  | 916 |      */
 | 
        
           |  |  | 917 |     public function debug_footer_html() {
 | 
        
           |  |  | 918 |         global $CFG, $SCRIPT;
 | 
        
           |  |  | 919 |         $output = '';
 | 
        
           |  |  | 920 |   | 
        
           |  |  | 921 |         if (during_initial_install()) {
 | 
        
           |  |  | 922 |             // Debugging info can not work before install is finished.
 | 
        
           |  |  | 923 |             return $output;
 | 
        
           |  |  | 924 |         }
 | 
        
           |  |  | 925 |   | 
        
           |  |  | 926 |         // This function is normally called from a layout.php file
 | 
        
           |  |  | 927 |         // but some of the content won't be known until later, so we return a placeholder
 | 
        
           |  |  | 928 |         // for now. This will be replaced with the real content in the footer.
 | 
        
           |  |  | 929 |         $output .= $this->unique_performance_info_token;
 | 
        
           |  |  | 930 |   | 
        
           |  |  | 931 |         if (!empty($CFG->debugpageinfo)) {
 | 
        
           |  |  | 932 |             $output .= '<div class="performanceinfo pageinfo">' . get_string('pageinfodebugsummary', 'core_admin',
 | 
        
           |  |  | 933 |                 $this->page->debug_summary()) . '</div>';
 | 
        
           |  |  | 934 |         }
 | 
        
           |  |  | 935 |         if (debugging(null, DEBUG_DEVELOPER) and has_capability('moodle/site:config', context_system::instance())) {  // Only in developer mode
 | 
        
           |  |  | 936 |   | 
        
           |  |  | 937 |             // Add link to profiling report if necessary
 | 
        
           |  |  | 938 |             if (function_exists('profiling_is_running') && profiling_is_running()) {
 | 
        
           |  |  | 939 |                 $txt = get_string('profiledscript', 'admin');
 | 
        
           |  |  | 940 |                 $title = get_string('profiledscriptview', 'admin');
 | 
        
           |  |  | 941 |                 $url = $CFG->wwwroot . '/admin/tool/profiling/index.php?script=' . urlencode($SCRIPT);
 | 
        
           |  |  | 942 |                 $link= '<a title="' . $title . '" href="' . $url . '">' . $txt . '</a>';
 | 
        
           |  |  | 943 |                 $output .= '<div class="profilingfooter">' . $link . '</div>';
 | 
        
           |  |  | 944 |             }
 | 
        
           |  |  | 945 |             $purgeurl = new moodle_url('/admin/purgecaches.php', array('confirm' => 1,
 | 
        
           |  |  | 946 |                 'sesskey' => sesskey(), 'returnurl' => $this->page->url->out_as_local_url(false)));
 | 
        
           |  |  | 947 |             $output .= '<div class="purgecaches">' .
 | 
        
           |  |  | 948 |                     html_writer::link($purgeurl, get_string('purgecaches', 'admin')) . '</div>';
 | 
        
           |  |  | 949 |   | 
        
           |  |  | 950 |             // Reactive module debug panel.
 | 
        
           |  |  | 951 |             $output .= $this->render_from_template('core/local/reactive/debugpanel', []);
 | 
        
           |  |  | 952 |         }
 | 
        
           |  |  | 953 |         if (!empty($CFG->debugvalidators)) {
 | 
        
           |  |  | 954 |             $siteurl = qualified_me();
 | 
        
           |  |  | 955 |             $nuurl = new moodle_url('https://validator.w3.org/nu/', ['doc' => $siteurl, 'showsource' => 'yes']);
 | 
        
           |  |  | 956 |             $waveurl = new moodle_url('https://wave.webaim.org/report#/' . urlencode($siteurl));
 | 
        
           |  |  | 957 |             $validatorlinks = [
 | 
        
           |  |  | 958 |                 html_writer::link($nuurl, get_string('validatehtml')),
 | 
        
           |  |  | 959 |                 html_writer::link($waveurl, get_string('wcagcheck'))
 | 
        
           |  |  | 960 |             ];
 | 
        
           |  |  | 961 |             $validatorlinkslist = html_writer::alist($validatorlinks, ['class' => 'list-unstyled ml-1']);
 | 
        
           |  |  | 962 |             $output .= html_writer::div($validatorlinkslist, 'validators');
 | 
        
           |  |  | 963 |         }
 | 
        
           |  |  | 964 |         return $output;
 | 
        
           |  |  | 965 |     }
 | 
        
           |  |  | 966 |   | 
        
           |  |  | 967 |     /**
 | 
        
           |  |  | 968 |      * Returns standard main content placeholder.
 | 
        
           |  |  | 969 |      * Designed to be called in theme layout.php files.
 | 
        
           |  |  | 970 |      *
 | 
        
           |  |  | 971 |      * @return string HTML fragment.
 | 
        
           |  |  | 972 |      */
 | 
        
           |  |  | 973 |     public function main_content() {
 | 
        
           |  |  | 974 |         // This is here because it is the only place we can inject the "main" role over the entire main content area
 | 
        
           |  |  | 975 |         // without requiring all theme's to manually do it, and without creating yet another thing people need to
 | 
        
           |  |  | 976 |         // remember in the theme.
 | 
        
           |  |  | 977 |         // This is an unfortunate hack. DO NO EVER add anything more here.
 | 
        
           |  |  | 978 |         // DO NOT add classes.
 | 
        
           |  |  | 979 |         // DO NOT add an id.
 | 
        
           |  |  | 980 |         return '<div role="main">'.$this->unique_main_content_token.'</div>';
 | 
        
           |  |  | 981 |     }
 | 
        
           |  |  | 982 |   | 
        
           |  |  | 983 |     /**
 | 
        
           |  |  | 984 |      * Returns information about an activity.
 | 
        
           |  |  | 985 |      *
 | 
        
           |  |  | 986 |      * @deprecated since Moodle 4.3 MDL-78744
 | 
        
           |  |  | 987 |      * @todo MDL-78926 This method will be deleted in Moodle 4.7
 | 
        
           |  |  | 988 |      * @param cm_info $cminfo The course module information.
 | 
        
           |  |  | 989 |      * @param cm_completion_details $completiondetails The completion details for this activity module.
 | 
        
           |  |  | 990 |      * @param array $activitydates The dates for this activity module.
 | 
        
           |  |  | 991 |      * @return string the activity information HTML.
 | 
        
           |  |  | 992 |      * @throws coding_exception
 | 
        
           |  |  | 993 |      */
 | 
        
           |  |  | 994 |     public function activity_information(cm_info $cminfo, cm_completion_details $completiondetails, array $activitydates): string {
 | 
        
           |  |  | 995 |         debugging('activity_information method is deprecated.', DEBUG_DEVELOPER);
 | 
        
           |  |  | 996 |         if (!$completiondetails->has_completion() && empty($activitydates)) {
 | 
        
           |  |  | 997 |             // No need to render the activity information when there's no completion info and activity dates to show.
 | 
        
           |  |  | 998 |             return '';
 | 
        
           |  |  | 999 |         }
 | 
        
           |  |  | 1000 |         $activityinfo = new activity_information($cminfo, $completiondetails, $activitydates);
 | 
        
           |  |  | 1001 |         $renderer = $this->page->get_renderer('core', 'course');
 | 
        
           |  |  | 1002 |         return $renderer->render($activityinfo);
 | 
        
           |  |  | 1003 |     }
 | 
        
           |  |  | 1004 |   | 
        
           |  |  | 1005 |     /**
 | 
        
           |  |  | 1006 |      * Returns standard navigation between activities in a course.
 | 
        
           |  |  | 1007 |      *
 | 
        
           |  |  | 1008 |      * @return string the navigation HTML.
 | 
        
           |  |  | 1009 |      */
 | 
        
           |  |  | 1010 |     public function activity_navigation() {
 | 
        
           |  |  | 1011 |         // First we should check if we want to add navigation.
 | 
        
           |  |  | 1012 |         $context = $this->page->context;
 | 
        
           |  |  | 1013 |         if (($this->page->pagelayout !== 'incourse' && $this->page->pagelayout !== 'frametop')
 | 
        
           |  |  | 1014 |             || $context->contextlevel != CONTEXT_MODULE) {
 | 
        
           |  |  | 1015 |             return '';
 | 
        
           |  |  | 1016 |         }
 | 
        
           |  |  | 1017 |   | 
        
           |  |  | 1018 |         // If the activity is in stealth mode, show no links.
 | 
        
           |  |  | 1019 |         if ($this->page->cm->is_stealth()) {
 | 
        
           |  |  | 1020 |             return '';
 | 
        
           |  |  | 1021 |         }
 | 
        
           |  |  | 1022 |   | 
        
           |  |  | 1023 |         $course = $this->page->cm->get_course();
 | 
        
           |  |  | 1024 |         $courseformat = course_get_format($course);
 | 
        
           |  |  | 1025 |   | 
        
           |  |  | 1026 |         // If the theme implements course index and the current course format uses course index and the current
 | 
        
           |  |  | 1027 |         // page layout is not 'frametop' (this layout does not support course index), show no links.
 | 
        
           |  |  | 1028 |         if ($this->page->theme->usescourseindex && $courseformat->uses_course_index() &&
 | 
        
           |  |  | 1029 |                 $this->page->pagelayout !== 'frametop') {
 | 
        
           |  |  | 1030 |             return '';
 | 
        
           |  |  | 1031 |         }
 | 
        
           |  |  | 1032 |   | 
        
           |  |  | 1033 |         // Get a list of all the activities in the course.
 | 
        
           |  |  | 1034 |         $modules = get_fast_modinfo($course->id)->get_cms();
 | 
        
           |  |  | 1035 |   | 
        
           |  |  | 1036 |         // Put the modules into an array in order by the position they are shown in the course.
 | 
        
           |  |  | 1037 |         $mods = [];
 | 
        
           |  |  | 1038 |         $activitylist = [];
 | 
        
           |  |  | 1039 |         foreach ($modules as $module) {
 | 
        
           |  |  | 1040 |             // Only add activities the user can access, aren't in stealth mode and have a url (eg. mod_label does not).
 | 
        
           |  |  | 1041 |             if (!$module->uservisible || $module->is_stealth() || empty($module->url)) {
 | 
        
           |  |  | 1042 |                 continue;
 | 
        
           |  |  | 1043 |             }
 | 
        
           |  |  | 1044 |             $mods[$module->id] = $module;
 | 
        
           |  |  | 1045 |   | 
        
           |  |  | 1046 |             // No need to add the current module to the list for the activity dropdown menu.
 | 
        
           |  |  | 1047 |             if ($module->id == $this->page->cm->id) {
 | 
        
           |  |  | 1048 |                 continue;
 | 
        
           |  |  | 1049 |             }
 | 
        
           |  |  | 1050 |             // Module name.
 | 
        
           |  |  | 1051 |             $modname = $module->get_formatted_name();
 | 
        
           |  |  | 1052 |             // Display the hidden text if necessary.
 | 
        
           |  |  | 1053 |             if (!$module->visible) {
 | 
        
           |  |  | 1054 |                 $modname .= ' ' . get_string('hiddenwithbrackets');
 | 
        
           |  |  | 1055 |             }
 | 
        
           |  |  | 1056 |             // Module URL.
 | 
        
           |  |  | 1057 |             $linkurl = new moodle_url($module->url, array('forceview' => 1));
 | 
        
           |  |  | 1058 |             // Add module URL (as key) and name (as value) to the activity list array.
 | 
        
           |  |  | 1059 |             $activitylist[$linkurl->out(false)] = $modname;
 | 
        
           |  |  | 1060 |         }
 | 
        
           |  |  | 1061 |   | 
        
           |  |  | 1062 |         $nummods = count($mods);
 | 
        
           |  |  | 1063 |   | 
        
           |  |  | 1064 |         // If there is only one mod then do nothing.
 | 
        
           |  |  | 1065 |         if ($nummods == 1) {
 | 
        
           |  |  | 1066 |             return '';
 | 
        
           |  |  | 1067 |         }
 | 
        
           |  |  | 1068 |   | 
        
           |  |  | 1069 |         // Get an array of just the course module ids used to get the cmid value based on their position in the course.
 | 
        
           |  |  | 1070 |         $modids = array_keys($mods);
 | 
        
           |  |  | 1071 |   | 
        
           |  |  | 1072 |         // Get the position in the array of the course module we are viewing.
 | 
        
           |  |  | 1073 |         $position = array_search($this->page->cm->id, $modids);
 | 
        
           |  |  | 1074 |   | 
        
           |  |  | 1075 |         $prevmod = null;
 | 
        
           |  |  | 1076 |         $nextmod = null;
 | 
        
           |  |  | 1077 |   | 
        
           |  |  | 1078 |         // Check if we have a previous mod to show.
 | 
        
           |  |  | 1079 |         if ($position > 0) {
 | 
        
           |  |  | 1080 |             $prevmod = $mods[$modids[$position - 1]];
 | 
        
           |  |  | 1081 |         }
 | 
        
           |  |  | 1082 |   | 
        
           |  |  | 1083 |         // Check if we have a next mod to show.
 | 
        
           |  |  | 1084 |         if ($position < ($nummods - 1)) {
 | 
        
           |  |  | 1085 |             $nextmod = $mods[$modids[$position + 1]];
 | 
        
           |  |  | 1086 |         }
 | 
        
           |  |  | 1087 |   | 
        
           |  |  | 1088 |         $activitynav = new \core_course\output\activity_navigation($prevmod, $nextmod, $activitylist);
 | 
        
           |  |  | 1089 |         $renderer = $this->page->get_renderer('core', 'course');
 | 
        
           |  |  | 1090 |         return $renderer->render($activitynav);
 | 
        
           |  |  | 1091 |     }
 | 
        
           |  |  | 1092 |   | 
        
           |  |  | 1093 |     /**
 | 
        
           |  |  | 1094 |      * The standard tags (typically script tags that are not needed earlier) that
 | 
        
           |  |  | 1095 |      * should be output after everything else. Designed to be called in theme layout.php files.
 | 
        
           |  |  | 1096 |      *
 | 
        
           |  |  | 1097 |      * @return string HTML fragment.
 | 
        
           |  |  | 1098 |      */
 | 
        
           |  |  | 1099 |     public function standard_end_of_body_html() {
 | 
        
           |  |  | 1100 |         global $CFG;
 | 
        
           |  |  | 1101 |   | 
        
           |  |  | 1102 |         // This function is normally called from a layout.php file in {@link core_renderer::header()}
 | 
        
           |  |  | 1103 |         // but some of the content won't be known until later, so we return a placeholder
 | 
        
           |  |  | 1104 |         // for now. This will be replaced with the real content in {@link core_renderer::footer()}.
 | 
        
           |  |  | 1105 |         $output = '';
 | 
        
           |  |  | 1106 |         if ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmlfooter)) {
 | 
        
           |  |  | 1107 |             $output .= "\n".$CFG->additionalhtmlfooter;
 | 
        
           |  |  | 1108 |         }
 | 
        
           |  |  | 1109 |         $output .= $this->unique_end_html_token;
 | 
        
           |  |  | 1110 |         return $output;
 | 
        
           |  |  | 1111 |     }
 | 
        
           |  |  | 1112 |   | 
        
           |  |  | 1113 |     /**
 | 
        
           |  |  | 1114 |      * The standard HTML that should be output just before the <footer> tag.
 | 
        
           |  |  | 1115 |      * Designed to be called in theme layout.php files.
 | 
        
           |  |  | 1116 |      *
 | 
        
           |  |  | 1117 |      * @return string HTML fragment.
 | 
        
           |  |  | 1118 |      */
 | 
        
           |  |  | 1119 |     public function standard_after_main_region_html() {
 | 
        
           |  |  | 1120 |         global $CFG;
 | 
        
           |  |  | 1121 |   | 
        
           |  |  | 1122 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 1123 |         // This is a critical page path.
 | 
        
           |  |  | 1124 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 1125 |         require_once(__DIR__ . '/classes/hook/output/after_standard_main_region_html_generation.php');
 | 
        
           |  |  | 1126 |   | 
        
           |  |  | 1127 |         $hook = new after_standard_main_region_html_generation($this);
 | 
        
           |  |  | 1128 |   | 
        
           |  |  | 1129 |         if ($this->page->pagelayout !== 'embedded' && !empty($CFG->additionalhtmlbottomofbody)) {
 | 
        
           |  |  | 1130 |             $hook->add_html("\n");
 | 
        
           |  |  | 1131 |             $hook->add_html($CFG->additionalhtmlbottomofbody);
 | 
        
           |  |  | 1132 |         }
 | 
        
           |  |  | 1133 |   | 
        
           |  |  | 1134 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 1135 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 1136 |   | 
        
           |  |  | 1137 |         return $hook->get_output();
 | 
        
           |  |  | 1138 |     }
 | 
        
           |  |  | 1139 |   | 
        
           |  |  | 1140 |     /**
 | 
        
           |  |  | 1141 |      * Return the standard string that says whether you are logged in (and switched
 | 
        
           |  |  | 1142 |      * roles/logged in as another user).
 | 
        
           |  |  | 1143 |      * @param bool $withlinks if false, then don't include any links in the HTML produced.
 | 
        
           |  |  | 1144 |      * If not set, the default is the nologinlinks option from the theme config.php file,
 | 
        
           |  |  | 1145 |      * and if that is not set, then links are included.
 | 
        
           |  |  | 1146 |      * @return string HTML fragment.
 | 
        
           |  |  | 1147 |      */
 | 
        
           |  |  | 1148 |     public function login_info($withlinks = null) {
 | 
        
           |  |  | 1149 |         global $USER, $CFG, $DB, $SESSION;
 | 
        
           |  |  | 1150 |   | 
        
           |  |  | 1151 |         if (during_initial_install()) {
 | 
        
           |  |  | 1152 |             return '';
 | 
        
           |  |  | 1153 |         }
 | 
        
           |  |  | 1154 |   | 
        
           |  |  | 1155 |         if (is_null($withlinks)) {
 | 
        
           |  |  | 1156 |             $withlinks = empty($this->page->layout_options['nologinlinks']);
 | 
        
           |  |  | 1157 |         }
 | 
        
           |  |  | 1158 |   | 
        
           |  |  | 1159 |         $course = $this->page->course;
 | 
        
           |  |  | 1160 |         if (\core\session\manager::is_loggedinas()) {
 | 
        
           |  |  | 1161 |             $realuser = \core\session\manager::get_realuser();
 | 
        
           |  |  | 1162 |             $fullname = fullname($realuser);
 | 
        
           |  |  | 1163 |             if ($withlinks) {
 | 
        
           |  |  | 1164 |                 $loginastitle = get_string('loginas');
 | 
        
           |  |  | 1165 |                 $realuserinfo = " [<a href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&sesskey=".sesskey()."\"";
 | 
        
           |  |  | 1166 |                 $realuserinfo .= "title =\"".$loginastitle."\">$fullname</a>] ";
 | 
        
           |  |  | 1167 |             } else {
 | 
        
           |  |  | 1168 |                 $realuserinfo = " [$fullname] ";
 | 
        
           |  |  | 1169 |             }
 | 
        
           |  |  | 1170 |         } else {
 | 
        
           |  |  | 1171 |             $realuserinfo = '';
 | 
        
           |  |  | 1172 |         }
 | 
        
           |  |  | 1173 |   | 
        
           |  |  | 1174 |         $loginpage = $this->is_login_page();
 | 
        
           |  |  | 1175 |         $loginurl = get_login_url();
 | 
        
           |  |  | 1176 |   | 
        
           |  |  | 1177 |         if (empty($course->id)) {
 | 
        
           |  |  | 1178 |             // $course->id is not defined during installation
 | 
        
           |  |  | 1179 |             return '';
 | 
        
           |  |  | 1180 |         } else if (isloggedin()) {
 | 
        
           |  |  | 1181 |             $context = context_course::instance($course->id);
 | 
        
           |  |  | 1182 |   | 
        
           |  |  | 1183 |             $fullname = fullname($USER);
 | 
        
           |  |  | 1184 |             // Since Moodle 2.0 this link always goes to the public profile page (not the course profile page)
 | 
        
           |  |  | 1185 |             if ($withlinks) {
 | 
        
           |  |  | 1186 |                 $linktitle = get_string('viewprofile');
 | 
        
           |  |  | 1187 |                 $username = "<a href=\"$CFG->wwwroot/user/profile.php?id=$USER->id\" title=\"$linktitle\">$fullname</a>";
 | 
        
           |  |  | 1188 |             } else {
 | 
        
           |  |  | 1189 |                 $username = $fullname;
 | 
        
           |  |  | 1190 |             }
 | 
        
           |  |  | 1191 |             if (is_mnet_remote_user($USER) and $idprovider = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid))) {
 | 
        
           |  |  | 1192 |                 if ($withlinks) {
 | 
        
           |  |  | 1193 |                     $username .= " from <a href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
 | 
        
           |  |  | 1194 |                 } else {
 | 
        
           |  |  | 1195 |                     $username .= " from {$idprovider->name}";
 | 
        
           |  |  | 1196 |                 }
 | 
        
           |  |  | 1197 |             }
 | 
        
           |  |  | 1198 |             if (isguestuser()) {
 | 
        
           |  |  | 1199 |                 $loggedinas = $realuserinfo.get_string('loggedinasguest');
 | 
        
           |  |  | 1200 |                 if (!$loginpage && $withlinks) {
 | 
        
           |  |  | 1201 |                     $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
 | 
        
           |  |  | 1202 |                 }
 | 
        
           |  |  | 1203 |             } else if (is_role_switched($course->id)) { // Has switched roles
 | 
        
           |  |  | 1204 |                 $rolename = '';
 | 
        
           |  |  | 1205 |                 if ($role = $DB->get_record('role', array('id'=>$USER->access['rsw'][$context->path]))) {
 | 
        
           |  |  | 1206 |                     $rolename = ': '.role_get_name($role, $context);
 | 
        
           |  |  | 1207 |                 }
 | 
        
           |  |  | 1208 |                 $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename;
 | 
        
           |  |  | 1209 |                 if ($withlinks) {
 | 
        
           |  |  | 1210 |                     $url = new moodle_url('/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>0, 'returnurl'=>$this->page->url->out_as_local_url(false)));
 | 
        
           |  |  | 1211 |                     $loggedinas .= ' ('.html_writer::tag('a', get_string('switchrolereturn'), array('href' => $url)).')';
 | 
        
           |  |  | 1212 |                 }
 | 
        
           |  |  | 1213 |             } else {
 | 
        
           |  |  | 1214 |                 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username);
 | 
        
           |  |  | 1215 |                 if ($withlinks) {
 | 
        
           |  |  | 1216 |                     $loggedinas .= " (<a href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
 | 
        
           |  |  | 1217 |                 }
 | 
        
           |  |  | 1218 |             }
 | 
        
           |  |  | 1219 |         } else {
 | 
        
           |  |  | 1220 |             $loggedinas = get_string('loggedinnot', 'moodle');
 | 
        
           |  |  | 1221 |             if (!$loginpage && $withlinks) {
 | 
        
           |  |  | 1222 |                 $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
 | 
        
           |  |  | 1223 |             }
 | 
        
           |  |  | 1224 |         }
 | 
        
           |  |  | 1225 |   | 
        
           |  |  | 1226 |         $loggedinas = '<div class="logininfo">'.$loggedinas.'</div>';
 | 
        
           |  |  | 1227 |   | 
        
           |  |  | 1228 |         if (isset($SESSION->justloggedin)) {
 | 
        
           |  |  | 1229 |             unset($SESSION->justloggedin);
 | 
        
           |  |  | 1230 |             if (!isguestuser()) {
 | 
        
           |  |  | 1231 |                 // Include this file only when required.
 | 
        
           |  |  | 1232 |                 require_once($CFG->dirroot . '/user/lib.php');
 | 
        
           |  |  | 1233 |                 if (($count = user_count_login_failures($USER)) && !empty($CFG->displayloginfailures)) {
 | 
        
           |  |  | 1234 |                     $loggedinas .= '<div class="loginfailures">';
 | 
        
           |  |  | 1235 |                     $a = new stdClass();
 | 
        
           |  |  | 1236 |                     $a->attempts = $count;
 | 
        
           |  |  | 1237 |                     $loggedinas .= get_string('failedloginattempts', '', $a);
 | 
        
           |  |  | 1238 |                     if (file_exists("$CFG->dirroot/report/log/index.php") and has_capability('report/log:view', context_system::instance())) {
 | 
        
           |  |  | 1239 |                         $loggedinas .= ' ('.html_writer::link(new moodle_url('/report/log/index.php', array('chooselog' => 1,
 | 
        
           |  |  | 1240 |                                 'id' => 0 , 'modid' => 'site_errors')), get_string('logs')).')';
 | 
        
           |  |  | 1241 |                     }
 | 
        
           |  |  | 1242 |                     $loggedinas .= '</div>';
 | 
        
           |  |  | 1243 |                 }
 | 
        
           |  |  | 1244 |             }
 | 
        
           |  |  | 1245 |         }
 | 
        
           |  |  | 1246 |   | 
        
           |  |  | 1247 |         return $loggedinas;
 | 
        
           |  |  | 1248 |     }
 | 
        
           |  |  | 1249 |   | 
        
           |  |  | 1250 |     /**
 | 
        
           |  |  | 1251 |      * Check whether the current page is a login page.
 | 
        
           |  |  | 1252 |      *
 | 
        
           |  |  | 1253 |      * @since Moodle 2.9
 | 
        
           |  |  | 1254 |      * @return bool
 | 
        
           |  |  | 1255 |      */
 | 
        
           |  |  | 1256 |     protected function is_login_page() {
 | 
        
           |  |  | 1257 |         // This is a real bit of a hack, but its a rarety that we need to do something like this.
 | 
        
           |  |  | 1258 |         // In fact the login pages should be only these two pages and as exposing this as an option for all pages
 | 
        
           |  |  | 1259 |         // could lead to abuse (or at least unneedingly complex code) the hack is the way to go.
 | 
        
           |  |  | 1260 |         return in_array(
 | 
        
           |  |  | 1261 |             $this->page->url->out_as_local_url(false, array()),
 | 
        
           |  |  | 1262 |             array(
 | 
        
           |  |  | 1263 |                 '/login/index.php',
 | 
        
           |  |  | 1264 |                 '/login/forgot_password.php',
 | 
        
           |  |  | 1265 |             )
 | 
        
           |  |  | 1266 |         );
 | 
        
           |  |  | 1267 |     }
 | 
        
           |  |  | 1268 |   | 
        
           |  |  | 1269 |     /**
 | 
        
           |  |  | 1270 |      * Return the 'back' link that normally appears in the footer.
 | 
        
           |  |  | 1271 |      *
 | 
        
           |  |  | 1272 |      * @return string HTML fragment.
 | 
        
           |  |  | 1273 |      */
 | 
        
           |  |  | 1274 |     public function home_link() {
 | 
        
           |  |  | 1275 |         global $CFG, $SITE;
 | 
        
           |  |  | 1276 |   | 
        
           |  |  | 1277 |         if ($this->page->pagetype == 'site-index') {
 | 
        
           |  |  | 1278 |             // Special case for site home page - please do not remove
 | 
        
           |  |  | 1279 |             return '<div class="sitelink">' .
 | 
        
           |  |  | 1280 |                    '<a title="Moodle" class="d-inline-block aalink" href="http://moodle.org/">' .
 | 
        
           |  |  | 1281 |                    '<img src="' . $this->image_url('moodlelogo_grayhat') . '" alt="'.get_string('moodlelogo').'" /></a></div>';
 | 
        
           |  |  | 1282 |   | 
        
           |  |  | 1283 |         } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
 | 
        
           |  |  | 1284 |             // Special case for during install/upgrade.
 | 
        
           |  |  | 1285 |             return '<div class="sitelink">'.
 | 
        
           |  |  | 1286 |                    '<a title="Moodle" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
 | 
        
           |  |  | 1287 |                    '<img src="' . $this->image_url('moodlelogo_grayhat') . '" alt="'.get_string('moodlelogo').'" /></a></div>';
 | 
        
           |  |  | 1288 |   | 
        
           |  |  | 1289 |         } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
 | 
        
           |  |  | 1290 |             return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
 | 
        
           |  |  | 1291 |                     get_string('home') . '</a></div>';
 | 
        
           |  |  | 1292 |   | 
        
           |  |  | 1293 |         } else {
 | 
        
           |  |  | 1294 |             return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
 | 
        
           |  |  | 1295 |                     format_string($this->page->course->shortname, true, array('context' => $this->page->context)) . '</a></div>';
 | 
        
           |  |  | 1296 |         }
 | 
        
           |  |  | 1297 |     }
 | 
        
           |  |  | 1298 |   | 
        
           |  |  | 1299 |     /**
 | 
        
           |  |  | 1300 |      * Redirects the user by any means possible given the current state
 | 
        
           |  |  | 1301 |      *
 | 
        
           |  |  | 1302 |      * This function should not be called directly, it should always be called using
 | 
        
           |  |  | 1303 |      * the redirect function in lib/weblib.php
 | 
        
           |  |  | 1304 |      *
 | 
        
           |  |  | 1305 |      * The redirect function should really only be called before page output has started
 | 
        
           |  |  | 1306 |      * however it will allow itself to be called during the state STATE_IN_BODY
 | 
        
           |  |  | 1307 |      *
 | 
        
           |  |  | 1308 |      * @param string $encodedurl The URL to send to encoded if required
 | 
        
           |  |  | 1309 |      * @param string $message The message to display to the user if any
 | 
        
           |  |  | 1310 |      * @param int $delay The delay before redirecting a user, if $message has been
 | 
        
           |  |  | 1311 |      *         set this is a requirement and defaults to 3, set to 0 no delay
 | 
        
           |  |  | 1312 |      * @param boolean $debugdisableredirect this redirect has been disabled for
 | 
        
           |  |  | 1313 |      *         debugging purposes. Display a message that explains, and don't
 | 
        
           |  |  | 1314 |      *         trigger the redirect.
 | 
        
           |  |  | 1315 |      * @param string $messagetype The type of notification to show the message in.
 | 
        
           |  |  | 1316 |      *         See constants on \core\output\notification.
 | 
        
           |  |  | 1317 |      * @return string The HTML to display to the user before dying, may contain
 | 
        
           |  |  | 1318 |      *         meta refresh, javascript refresh, and may have set header redirects
 | 
        
           |  |  | 1319 |      */
 | 
        
           |  |  | 1320 |     public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect,
 | 
        
           |  |  | 1321 |                                      $messagetype = \core\output\notification::NOTIFY_INFO) {
 | 
        
           |  |  | 1322 |         global $CFG;
 | 
        
           |  |  | 1323 |         $url = str_replace('&', '&', $encodedurl);
 | 
        
           |  |  | 1324 |   | 
        
           |  |  | 1325 |         switch ($this->page->state) {
 | 
        
           |  |  | 1326 |             case moodle_page::STATE_BEFORE_HEADER :
 | 
        
           |  |  | 1327 |                 // No output yet it is safe to delivery the full arsenal of redirect methods
 | 
        
           |  |  | 1328 |                 if (!$debugdisableredirect) {
 | 
        
           |  |  | 1329 |                     // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
 | 
        
           |  |  | 1330 |                     $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
 | 
        
           |  |  | 1331 |                     $this->page->requires->js_function_call('document.location.replace', array($url), false, ($delay + 3));
 | 
        
           |  |  | 1332 |                 }
 | 
        
           |  |  | 1333 |                 $output = $this->header();
 | 
        
           |  |  | 1334 |                 break;
 | 
        
           |  |  | 1335 |             case moodle_page::STATE_PRINTING_HEADER :
 | 
        
           |  |  | 1336 |                 // We should hopefully never get here
 | 
        
           |  |  | 1337 |                 throw new coding_exception('You cannot redirect while printing the page header');
 | 
        
           |  |  | 1338 |                 break;
 | 
        
           |  |  | 1339 |             case moodle_page::STATE_IN_BODY :
 | 
        
           |  |  | 1340 |                 // We really shouldn't be here but we can deal with this
 | 
        
           |  |  | 1341 |                 debugging("You should really redirect before you start page output");
 | 
        
           |  |  | 1342 |                 if (!$debugdisableredirect) {
 | 
        
           |  |  | 1343 |                     $this->page->requires->js_function_call('document.location.replace', array($url), false, $delay);
 | 
        
           |  |  | 1344 |                 }
 | 
        
           |  |  | 1345 |                 $output = $this->opencontainers->pop_all_but_last();
 | 
        
           |  |  | 1346 |                 break;
 | 
        
           |  |  | 1347 |             case moodle_page::STATE_DONE :
 | 
        
           |  |  | 1348 |                 // Too late to be calling redirect now
 | 
        
           |  |  | 1349 |                 throw new coding_exception('You cannot redirect after the entire page has been generated');
 | 
        
           |  |  | 1350 |                 break;
 | 
        
           |  |  | 1351 |         }
 | 
        
           |  |  | 1352 |         $output .= $this->notification($message, $messagetype);
 | 
        
           |  |  | 1353 |         $output .= '<div class="continuebutton">(<a href="'. $encodedurl .'">'. get_string('continue') .'</a>)</div>';
 | 
        
           |  |  | 1354 |         if ($debugdisableredirect) {
 | 
        
           |  |  | 1355 |             $output .= '<p><strong>'.get_string('erroroutput', 'error').'</strong></p>';
 | 
        
           |  |  | 1356 |         }
 | 
        
           |  |  | 1357 |         $output .= $this->footer();
 | 
        
           |  |  | 1358 |         return $output;
 | 
        
           |  |  | 1359 |     }
 | 
        
           |  |  | 1360 |   | 
        
           |  |  | 1361 |     /**
 | 
        
           |  |  | 1362 |      * Start output by sending the HTTP headers, and printing the HTML <head>
 | 
        
           |  |  | 1363 |      * and the start of the <body>.
 | 
        
           |  |  | 1364 |      *
 | 
        
           |  |  | 1365 |      * To control what is printed, you should set properties on $PAGE.
 | 
        
           |  |  | 1366 |      *
 | 
        
           |  |  | 1367 |      * @return string HTML that you must output this, preferably immediately.
 | 
        
           |  |  | 1368 |      */
 | 
        
           |  |  | 1369 |     public function header() {
 | 
        
           |  |  | 1370 |         global $USER, $CFG, $SESSION;
 | 
        
           |  |  | 1371 |   | 
        
           |  |  | 1372 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 1373 |         // This is a critical page path.
 | 
        
           |  |  | 1374 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 1375 |         require_once(__DIR__ . '/classes/hook/output/before_http_headers.php');
 | 
        
           |  |  | 1376 |   | 
        
           |  |  | 1377 |         $hook = new before_http_headers($this);
 | 
        
           |  |  | 1378 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 1379 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 1380 |   | 
        
           |  |  | 1381 |         if (\core\session\manager::is_loggedinas()) {
 | 
        
           |  |  | 1382 |             $this->page->add_body_class('userloggedinas');
 | 
        
           |  |  | 1383 |         }
 | 
        
           |  |  | 1384 |   | 
        
           |  |  | 1385 |         if (isset($SESSION->justloggedin) && !empty($CFG->displayloginfailures)) {
 | 
        
           |  |  | 1386 |             require_once($CFG->dirroot . '/user/lib.php');
 | 
        
           |  |  | 1387 |             // Set second parameter to false as we do not want reset the counter, the same message appears on footer.
 | 
        
           |  |  | 1388 |             if ($count = user_count_login_failures($USER, false)) {
 | 
        
           |  |  | 1389 |                 $this->page->add_body_class('loginfailures');
 | 
        
           |  |  | 1390 |             }
 | 
        
           |  |  | 1391 |         }
 | 
        
           |  |  | 1392 |   | 
        
           |  |  | 1393 |         // If the user is logged in, and we're not in initial install,
 | 
        
           |  |  | 1394 |         // check to see if the user is role-switched and add the appropriate
 | 
        
           |  |  | 1395 |         // CSS class to the body element.
 | 
        
           |  |  | 1396 |         if (!during_initial_install() && isloggedin() && is_role_switched($this->page->course->id)) {
 | 
        
           |  |  | 1397 |             $this->page->add_body_class('userswitchedrole');
 | 
        
           |  |  | 1398 |         }
 | 
        
           |  |  | 1399 |   | 
        
           |  |  | 1400 |         // Give themes a chance to init/alter the page object.
 | 
        
           |  |  | 1401 |         $this->page->theme->init_page($this->page);
 | 
        
           |  |  | 1402 |   | 
        
           |  |  | 1403 |         $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
 | 
        
           |  |  | 1404 |   | 
        
           |  |  | 1405 |         // Find the appropriate page layout file, based on $this->page->pagelayout.
 | 
        
           |  |  | 1406 |         $layoutfile = $this->page->theme->layout_file($this->page->pagelayout);
 | 
        
           |  |  | 1407 |         // Render the layout using the layout file.
 | 
        
           |  |  | 1408 |         $rendered = $this->render_page_layout($layoutfile);
 | 
        
           |  |  | 1409 |   | 
        
           |  |  | 1410 |         // Slice the rendered output into header and footer.
 | 
        
           |  |  | 1411 |         $cutpos = strpos($rendered, $this->unique_main_content_token);
 | 
        
           |  |  | 1412 |         if ($cutpos === false) {
 | 
        
           |  |  | 1413 |             $cutpos = strpos($rendered, self::MAIN_CONTENT_TOKEN);
 | 
        
           |  |  | 1414 |             $token = self::MAIN_CONTENT_TOKEN;
 | 
        
           |  |  | 1415 |         } else {
 | 
        
           |  |  | 1416 |             $token = $this->unique_main_content_token;
 | 
        
           |  |  | 1417 |         }
 | 
        
           |  |  | 1418 |   | 
        
           |  |  | 1419 |         if ($cutpos === false) {
 | 
        
           |  |  | 1420 |             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.');
 | 
        
           |  |  | 1421 |         }
 | 
        
           |  |  | 1422 |   | 
        
           |  |  | 1423 |         $header = substr($rendered, 0, $cutpos);
 | 
        
           |  |  | 1424 |         $footer = substr($rendered, $cutpos + strlen($token));
 | 
        
           |  |  | 1425 |   | 
        
           |  |  | 1426 |         if (empty($this->contenttype)) {
 | 
        
           |  |  | 1427 |             debugging('The page layout file did not call $OUTPUT->doctype()');
 | 
        
           |  |  | 1428 |             $header = $this->doctype() . $header;
 | 
        
           |  |  | 1429 |         }
 | 
        
           |  |  | 1430 |   | 
        
           |  |  | 1431 |         // If this theme version is below 2.4 release and this is a course view page
 | 
        
           |  |  | 1432 |         if ((!isset($this->page->theme->settings->version) || $this->page->theme->settings->version < 2012101500) &&
 | 
        
           |  |  | 1433 |                 $this->page->pagelayout === 'course' && $this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
 | 
        
           |  |  | 1434 |             // check if course content header/footer have not been output during render of theme layout
 | 
        
           |  |  | 1435 |             $coursecontentheader = $this->course_content_header(true);
 | 
        
           |  |  | 1436 |             $coursecontentfooter = $this->course_content_footer(true);
 | 
        
           |  |  | 1437 |             if (!empty($coursecontentheader)) {
 | 
        
           |  |  | 1438 |                 // display debug message and add header and footer right above and below main content
 | 
        
           |  |  | 1439 |                 // Please note that course header and footer (to be displayed above and below the whole page)
 | 
        
           |  |  | 1440 |                 // are not displayed in this case at all.
 | 
        
           |  |  | 1441 |                 // Besides the content header and footer are not displayed on any other course page
 | 
        
           |  |  | 1442 |                 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);
 | 
        
           |  |  | 1443 |                 $header .= $coursecontentheader;
 | 
        
           |  |  | 1444 |                 $footer = $coursecontentfooter. $footer;
 | 
        
           |  |  | 1445 |             }
 | 
        
           |  |  | 1446 |         }
 | 
        
           |  |  | 1447 |   | 
        
           |  |  | 1448 |         send_headers($this->contenttype, $this->page->cacheable);
 | 
        
           |  |  | 1449 |   | 
        
           |  |  | 1450 |         $this->opencontainers->push('header/footer', $footer);
 | 
        
           |  |  | 1451 |         $this->page->set_state(moodle_page::STATE_IN_BODY);
 | 
        
           |  |  | 1452 |   | 
        
           |  |  | 1453 |         // If an activity record has been set, activity_header will handle this.
 | 
        
           |  |  | 1454 |         if (!$this->page->cm || !empty($this->page->layout_options['noactivityheader'])) {
 | 
        
           |  |  | 1455 |             $header .= $this->skip_link_target('maincontent');
 | 
        
           |  |  | 1456 |         }
 | 
        
           |  |  | 1457 |         return $header;
 | 
        
           |  |  | 1458 |     }
 | 
        
           |  |  | 1459 |   | 
        
           |  |  | 1460 |     /**
 | 
        
           |  |  | 1461 |      * Renders and outputs the page layout file.
 | 
        
           |  |  | 1462 |      *
 | 
        
           |  |  | 1463 |      * This is done by preparing the normal globals available to a script, and
 | 
        
           |  |  | 1464 |      * then including the layout file provided by the current theme for the
 | 
        
           |  |  | 1465 |      * requested layout.
 | 
        
           |  |  | 1466 |      *
 | 
        
           |  |  | 1467 |      * @param string $layoutfile The name of the layout file
 | 
        
           |  |  | 1468 |      * @return string HTML code
 | 
        
           |  |  | 1469 |      */
 | 
        
           |  |  | 1470 |     protected function render_page_layout($layoutfile) {
 | 
        
           |  |  | 1471 |         global $CFG, $SITE, $USER;
 | 
        
           |  |  | 1472 |         // The next lines are a bit tricky. The point is, here we are in a method
 | 
        
           |  |  | 1473 |         // of a renderer class, and this object may, or may not, be the same as
 | 
        
           |  |  | 1474 |         // the global $OUTPUT object. When rendering the page layout file, we want to use
 | 
        
           |  |  | 1475 |         // this object. However, people writing Moodle code expect the current
 | 
        
           |  |  | 1476 |         // renderer to be called $OUTPUT, not $this, so define a variable called
 | 
        
           |  |  | 1477 |         // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
 | 
        
           |  |  | 1478 |         $OUTPUT = $this;
 | 
        
           |  |  | 1479 |         $PAGE = $this->page;
 | 
        
           |  |  | 1480 |         $COURSE = $this->page->course;
 | 
        
           |  |  | 1481 |   | 
        
           |  |  | 1482 |         ob_start();
 | 
        
           |  |  | 1483 |         include($layoutfile);
 | 
        
           |  |  | 1484 |         $rendered = ob_get_contents();
 | 
        
           |  |  | 1485 |         ob_end_clean();
 | 
        
           |  |  | 1486 |         return $rendered;
 | 
        
           |  |  | 1487 |     }
 | 
        
           |  |  | 1488 |   | 
        
           |  |  | 1489 |     /**
 | 
        
           |  |  | 1490 |      * Outputs the page's footer
 | 
        
           |  |  | 1491 |      *
 | 
        
           |  |  | 1492 |      * @return string HTML fragment
 | 
        
           |  |  | 1493 |      */
 | 
        
           |  |  | 1494 |     public function footer() {
 | 
        
           |  |  | 1495 |         global $CFG, $DB, $PERF;
 | 
        
           |  |  | 1496 |   | 
        
           |  |  | 1497 |         // Ensure that the callback exists prior to cache purge.
 | 
        
           |  |  | 1498 |         // This is a critical page path.
 | 
        
           |  |  | 1499 |         // TODO MDL-81134 Remove after LTS+1.
 | 
        
           |  |  | 1500 |         require_once(__DIR__ . '/classes/hook/output/before_footer_html_generation.php');
 | 
        
           |  |  | 1501 |   | 
        
           |  |  | 1502 |         $hook = new before_footer_html_generation($this);
 | 
        
           |  |  | 1503 |         $hook->process_legacy_callbacks();
 | 
        
           |  |  | 1504 |         di::get(hook_manager::class)->dispatch($hook);
 | 
        
           |  |  | 1505 |         $hook->add_html($this->container_end_all(true));
 | 
        
           |  |  | 1506 |         $output = $hook->get_output();
 | 
        
           |  |  | 1507 |   | 
        
           |  |  | 1508 |         $footer = $this->opencontainers->pop('header/footer');
 | 
        
           |  |  | 1509 |   | 
        
           |  |  | 1510 |         if (debugging() and $DB and $DB->is_transaction_started()) {
 | 
        
           |  |  | 1511 |             // TODO: MDL-20625 print warning - transaction will be rolled back
 | 
        
           |  |  | 1512 |         }
 | 
        
           |  |  | 1513 |   | 
        
           |  |  | 1514 |         // Provide some performance info if required
 | 
        
           |  |  | 1515 |         $performanceinfo = '';
 | 
        
           |  |  | 1516 |         if (MDL_PERF || (!empty($CFG->perfdebug) && $CFG->perfdebug > 7)) {
 | 
        
           |  |  | 1517 |             if (MDL_PERFTOFOOT || debugging() || (!empty($CFG->perfdebug) && $CFG->perfdebug > 7)) {
 | 
        
           |  |  | 1518 |                 if (NO_OUTPUT_BUFFERING) {
 | 
        
           |  |  | 1519 |                     // If the output buffer was off then we render a placeholder and stream the
 | 
        
           |  |  | 1520 |                     // performance debugging into it at the very end in the shutdown handler.
 | 
        
           |  |  | 1521 |                     $PERF->perfdebugdeferred = true;
 | 
        
           |  |  | 1522 |                     $performanceinfo .= html_writer::tag('div',
 | 
        
           |  |  | 1523 |                         get_string('perfdebugdeferred', 'admin'),
 | 
        
           |  |  | 1524 |                         [
 | 
        
           |  |  | 1525 |                             'id' => 'perfdebugfooter',
 | 
        
           |  |  | 1526 |                             'style' => 'min-height: 30em',
 | 
        
           |  |  | 1527 |                         ]);
 | 
        
           |  |  | 1528 |                 } else {
 | 
        
           |  |  | 1529 |                     $perf = get_performance_info();
 | 
        
           |  |  | 1530 |                     $performanceinfo = $perf['html'];
 | 
        
           |  |  | 1531 |                 }
 | 
        
           |  |  | 1532 |             }
 | 
        
           |  |  | 1533 |         }
 | 
        
           |  |  | 1534 |   | 
        
           |  |  | 1535 |         // We always want performance data when running a performance test, even if the user is redirected to another page.
 | 
        
           |  |  | 1536 |         if (MDL_PERF_TEST && strpos($footer, $this->unique_performance_info_token) === false) {
 | 
        
           |  |  | 1537 |             $footer = $this->unique_performance_info_token . $footer;
 | 
        
           |  |  | 1538 |         }
 | 
        
           |  |  | 1539 |         $footer = str_replace($this->unique_performance_info_token, $performanceinfo, $footer);
 | 
        
           |  |  | 1540 |   | 
        
           |  |  | 1541 |         // Only show notifications when the current page has a context id.
 | 
        
           |  |  | 1542 |         if (!empty($this->page->context->id)) {
 | 
        
           |  |  | 1543 |             $this->page->requires->js_call_amd('core/notification', 'init', array(
 | 
        
           |  |  | 1544 |                 $this->page->context->id,
 | 
        
           |  |  | 1545 |                 \core\notification::fetch_as_array($this)
 | 
        
           |  |  | 1546 |             ));
 | 
        
           |  |  | 1547 |         }
 | 
        
           |  |  | 1548 |         $footer = str_replace($this->unique_end_html_token, $this->page->requires->get_end_code(), $footer);
 | 
        
           |  |  | 1549 |   | 
        
           |  |  | 1550 |         $this->page->set_state(moodle_page::STATE_DONE);
 | 
        
           |  |  | 1551 |   | 
        
           |  |  | 1552 |         // Here we remove the closing body and html tags and store them to be added back
 | 
        
           |  |  | 1553 |         // in the shutdown handler so we can have valid html with streaming script tags
 | 
        
           |  |  | 1554 |         // which are rendered after the visible footer.
 | 
        
           |  |  | 1555 |         $tags = '';
 | 
        
           |  |  | 1556 |         preg_match('#\<\/body>#i', $footer, $matches);
 | 
        
           |  |  | 1557 |         $tags .= $matches[0];
 | 
        
           |  |  | 1558 |         $footer = str_replace($matches[0], '', $footer);
 | 
        
           |  |  | 1559 |   | 
        
           |  |  | 1560 |         preg_match('#\<\/html>#i', $footer, $matches);
 | 
        
           |  |  | 1561 |         $tags .= $matches[0];
 | 
        
           |  |  | 1562 |         $footer = str_replace($matches[0], '', $footer);
 | 
        
           |  |  | 1563 |   | 
        
           |  |  | 1564 |         $CFG->closingtags = $tags;
 | 
        
           |  |  | 1565 |   | 
        
           |  |  | 1566 |         return $output . $footer;
 | 
        
           |  |  | 1567 |     }
 | 
        
           |  |  | 1568 |   | 
        
           |  |  | 1569 |     /**
 | 
        
           |  |  | 1570 |      * Close all but the last open container. This is useful in places like error
 | 
        
           |  |  | 1571 |      * handling, where you want to close all the open containers (apart from <body>)
 | 
        
           |  |  | 1572 |      * before outputting the error message.
 | 
        
           |  |  | 1573 |      *
 | 
        
           |  |  | 1574 |      * @param bool $shouldbenone assert that the stack should be empty now - causes a
 | 
        
           |  |  | 1575 |      *      developer debug warning if it isn't.
 | 
        
           |  |  | 1576 |      * @return string the HTML required to close any open containers inside <body>.
 | 
        
           |  |  | 1577 |      */
 | 
        
           |  |  | 1578 |     public function container_end_all($shouldbenone = false) {
 | 
        
           |  |  | 1579 |         return $this->opencontainers->pop_all_but_last($shouldbenone);
 | 
        
           |  |  | 1580 |     }
 | 
        
           |  |  | 1581 |   | 
        
           |  |  | 1582 |     /**
 | 
        
           |  |  | 1583 |      * Returns course-specific information to be output immediately above content on any course page
 | 
        
           |  |  | 1584 |      * (for the current course)
 | 
        
           |  |  | 1585 |      *
 | 
        
           |  |  | 1586 |      * @param bool $onlyifnotcalledbefore output content only if it has not been output before
 | 
        
           |  |  | 1587 |      * @return string
 | 
        
           |  |  | 1588 |      */
 | 
        
           |  |  | 1589 |     public function course_content_header($onlyifnotcalledbefore = false) {
 | 
        
           |  |  | 1590 |         global $CFG;
 | 
        
           |  |  | 1591 |         static $functioncalled = false;
 | 
        
           |  |  | 1592 |         if ($functioncalled && $onlyifnotcalledbefore) {
 | 
        
           |  |  | 1593 |             // we have already output the content header
 | 
        
           |  |  | 1594 |             return '';
 | 
        
           |  |  | 1595 |         }
 | 
        
           |  |  | 1596 |   | 
        
           |  |  | 1597 |         // Output any session notification.
 | 
        
           |  |  | 1598 |         $notifications = \core\notification::fetch();
 | 
        
           |  |  | 1599 |   | 
        
           |  |  | 1600 |         $bodynotifications = '';
 | 
        
           |  |  | 1601 |         foreach ($notifications as $notification) {
 | 
        
           |  |  | 1602 |             $bodynotifications .= $this->render_from_template(
 | 
        
           |  |  | 1603 |                     $notification->get_template_name(),
 | 
        
           |  |  | 1604 |                     $notification->export_for_template($this)
 | 
        
           |  |  | 1605 |                 );
 | 
        
           |  |  | 1606 |         }
 | 
        
           |  |  | 1607 |   | 
        
           |  |  | 1608 |         $output = html_writer::span($bodynotifications, 'notifications', array('id' => 'user-notifications'));
 | 
        
           |  |  | 1609 |   | 
        
           |  |  | 1610 |         if ($this->page->course->id == SITEID) {
 | 
        
           |  |  | 1611 |             // return immediately and do not include /course/lib.php if not necessary
 | 
        
           |  |  | 1612 |             return $output;
 | 
        
           |  |  | 1613 |         }
 | 
        
           |  |  | 1614 |   | 
        
           |  |  | 1615 |         require_once($CFG->dirroot.'/course/lib.php');
 | 
        
           |  |  | 1616 |         $functioncalled = true;
 | 
        
           |  |  | 1617 |         $courseformat = course_get_format($this->page->course);
 | 
        
           |  |  | 1618 |         if (($obj = $courseformat->course_content_header()) !== null) {
 | 
        
           |  |  | 1619 |             $output .= html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-header');
 | 
        
           |  |  | 1620 |         }
 | 
        
           |  |  | 1621 |         return $output;
 | 
        
           |  |  | 1622 |     }
 | 
        
           |  |  | 1623 |   | 
        
           |  |  | 1624 |     /**
 | 
        
           |  |  | 1625 |      * Returns course-specific information to be output immediately below content on any course page
 | 
        
           |  |  | 1626 |      * (for the current course)
 | 
        
           |  |  | 1627 |      *
 | 
        
           |  |  | 1628 |      * @param bool $onlyifnotcalledbefore output content only if it has not been output before
 | 
        
           |  |  | 1629 |      * @return string
 | 
        
           |  |  | 1630 |      */
 | 
        
           |  |  | 1631 |     public function course_content_footer($onlyifnotcalledbefore = false) {
 | 
        
           |  |  | 1632 |         global $CFG;
 | 
        
           |  |  | 1633 |         if ($this->page->course->id == SITEID) {
 | 
        
           |  |  | 1634 |             // return immediately and do not include /course/lib.php if not necessary
 | 
        
           |  |  | 1635 |             return '';
 | 
        
           |  |  | 1636 |         }
 | 
        
           |  |  | 1637 |         static $functioncalled = false;
 | 
        
           |  |  | 1638 |         if ($functioncalled && $onlyifnotcalledbefore) {
 | 
        
           |  |  | 1639 |             // we have already output the content footer
 | 
        
           |  |  | 1640 |             return '';
 | 
        
           |  |  | 1641 |         }
 | 
        
           |  |  | 1642 |         $functioncalled = true;
 | 
        
           |  |  | 1643 |         require_once($CFG->dirroot.'/course/lib.php');
 | 
        
           |  |  | 1644 |         $courseformat = course_get_format($this->page->course);
 | 
        
           |  |  | 1645 |         if (($obj = $courseformat->course_content_footer()) !== null) {
 | 
        
           |  |  | 1646 |             return html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-footer');
 | 
        
           |  |  | 1647 |         }
 | 
        
           |  |  | 1648 |         return '';
 | 
        
           |  |  | 1649 |     }
 | 
        
           |  |  | 1650 |   | 
        
           |  |  | 1651 |     /**
 | 
        
           |  |  | 1652 |      * Returns course-specific information to be output on any course page in the header area
 | 
        
           |  |  | 1653 |      * (for the current course)
 | 
        
           |  |  | 1654 |      *
 | 
        
           |  |  | 1655 |      * @return string
 | 
        
           |  |  | 1656 |      */
 | 
        
           |  |  | 1657 |     public function course_header() {
 | 
        
           |  |  | 1658 |         global $CFG;
 | 
        
           |  |  | 1659 |         if ($this->page->course->id == SITEID) {
 | 
        
           |  |  | 1660 |             // return immediately and do not include /course/lib.php if not necessary
 | 
        
           |  |  | 1661 |             return '';
 | 
        
           |  |  | 1662 |         }
 | 
        
           |  |  | 1663 |         require_once($CFG->dirroot.'/course/lib.php');
 | 
        
           |  |  | 1664 |         $courseformat = course_get_format($this->page->course);
 | 
        
           |  |  | 1665 |         if (($obj = $courseformat->course_header()) !== null) {
 | 
        
           |  |  | 1666 |             return $courseformat->get_renderer($this->page)->render($obj);
 | 
        
           |  |  | 1667 |         }
 | 
        
           |  |  | 1668 |         return '';
 | 
        
           |  |  | 1669 |     }
 | 
        
           |  |  | 1670 |   | 
        
           |  |  | 1671 |     /**
 | 
        
           |  |  | 1672 |      * Returns course-specific information to be output on any course page in the footer area
 | 
        
           |  |  | 1673 |      * (for the current course)
 | 
        
           |  |  | 1674 |      *
 | 
        
           |  |  | 1675 |      * @return string
 | 
        
           |  |  | 1676 |      */
 | 
        
           |  |  | 1677 |     public function course_footer() {
 | 
        
           |  |  | 1678 |         global $CFG;
 | 
        
           |  |  | 1679 |         if ($this->page->course->id == SITEID) {
 | 
        
           |  |  | 1680 |             // return immediately and do not include /course/lib.php if not necessary
 | 
        
           |  |  | 1681 |             return '';
 | 
        
           |  |  | 1682 |         }
 | 
        
           |  |  | 1683 |         require_once($CFG->dirroot.'/course/lib.php');
 | 
        
           |  |  | 1684 |         $courseformat = course_get_format($this->page->course);
 | 
        
           |  |  | 1685 |         if (($obj = $courseformat->course_footer()) !== null) {
 | 
        
           |  |  | 1686 |             return $courseformat->get_renderer($this->page)->render($obj);
 | 
        
           |  |  | 1687 |         }
 | 
        
           |  |  | 1688 |         return '';
 | 
        
           |  |  | 1689 |     }
 | 
        
           |  |  | 1690 |   | 
        
           |  |  | 1691 |     /**
 | 
        
           |  |  | 1692 |      * Get the course pattern datauri to show on a course card.
 | 
        
           |  |  | 1693 |      *
 | 
        
           |  |  | 1694 |      * The datauri is an encoded svg that can be passed as a url.
 | 
        
           |  |  | 1695 |      * @param int $id Id to use when generating the pattern
 | 
        
           |  |  | 1696 |      * @return string datauri
 | 
        
           |  |  | 1697 |      */
 | 
        
           |  |  | 1698 |     public function get_generated_image_for_id($id) {
 | 
        
           |  |  | 1699 |         $color = $this->get_generated_color_for_id($id);
 | 
        
           |  |  | 1700 |         $pattern = new \core_geopattern();
 | 
        
           |  |  | 1701 |         $pattern->setColor($color);
 | 
        
           |  |  | 1702 |         $pattern->patternbyid($id);
 | 
        
           |  |  | 1703 |         return $pattern->datauri();
 | 
        
           |  |  | 1704 |     }
 | 
        
           |  |  | 1705 |   | 
        
           |  |  | 1706 |     /**
 | 
        
           |  |  | 1707 |      * Get the course pattern image URL.
 | 
        
           |  |  | 1708 |      *
 | 
        
           |  |  | 1709 |      * @param context_course $context course context object
 | 
        
           |  |  | 1710 |      * @return string URL of the course pattern image in SVG format
 | 
        
           |  |  | 1711 |      */
 | 
        
           |  |  | 1712 |     public function get_generated_url_for_course(context_course $context): string {
 | 
        
           |  |  | 1713 |         return moodle_url::make_pluginfile_url($context->id, 'course', 'generated', null, '/', 'course.svg')->out();
 | 
        
           |  |  | 1714 |     }
 | 
        
           |  |  | 1715 |   | 
        
           |  |  | 1716 |     /**
 | 
        
           |  |  | 1717 |      * Get the course pattern in SVG format to show on a course card.
 | 
        
           |  |  | 1718 |      *
 | 
        
           |  |  | 1719 |      * @param int $id id to use when generating the pattern
 | 
        
           |  |  | 1720 |      * @return string SVG file contents
 | 
        
           |  |  | 1721 |      */
 | 
        
           |  |  | 1722 |     public function get_generated_svg_for_id(int $id): string {
 | 
        
           |  |  | 1723 |         $color = $this->get_generated_color_for_id($id);
 | 
        
           |  |  | 1724 |         $pattern = new \core_geopattern();
 | 
        
           |  |  | 1725 |         $pattern->setColor($color);
 | 
        
           |  |  | 1726 |         $pattern->patternbyid($id);
 | 
        
           |  |  | 1727 |         return $pattern->toSVG();
 | 
        
           |  |  | 1728 |     }
 | 
        
           |  |  | 1729 |   | 
        
           |  |  | 1730 |     /**
 | 
        
           |  |  | 1731 |      * Get the course color to show on a course card.
 | 
        
           |  |  | 1732 |      *
 | 
        
           |  |  | 1733 |      * @param int $id Id to use when generating the color.
 | 
        
           |  |  | 1734 |      * @return string hex color code.
 | 
        
           |  |  | 1735 |      */
 | 
        
           |  |  | 1736 |     public function get_generated_color_for_id($id) {
 | 
        
           |  |  | 1737 |         $colornumbers = range(1, 10);
 | 
        
           |  |  | 1738 |         $basecolors = [];
 | 
        
           |  |  | 1739 |         foreach ($colornumbers as $number) {
 | 
        
           |  |  | 1740 |             $basecolors[] = get_config('core_admin', 'coursecolor' . $number);
 | 
        
           |  |  | 1741 |         }
 | 
        
           |  |  | 1742 |   | 
        
           |  |  | 1743 |         $color = $basecolors[$id % 10];
 | 
        
           |  |  | 1744 |         return $color;
 | 
        
           |  |  | 1745 |     }
 | 
        
           |  |  | 1746 |   | 
        
           |  |  | 1747 |     /**
 | 
        
           |  |  | 1748 |      * Returns lang menu or '', this method also checks forcing of languages in courses.
 | 
        
           |  |  | 1749 |      *
 | 
        
           |  |  | 1750 |      * This function calls {@link core_renderer::render_single_select()} to actually display the language menu.
 | 
        
           |  |  | 1751 |      *
 | 
        
           |  |  | 1752 |      * @return string The lang menu HTML or empty string
 | 
        
           |  |  | 1753 |      */
 | 
        
           |  |  | 1754 |     public function lang_menu() {
 | 
        
           |  |  | 1755 |         $languagemenu = new \core\output\language_menu($this->page);
 | 
        
           |  |  | 1756 |         $data = $languagemenu->export_for_single_select($this);
 | 
        
           |  |  | 1757 |         if ($data) {
 | 
        
           |  |  | 1758 |             return $this->render_from_template('core/single_select', $data);
 | 
        
           |  |  | 1759 |         }
 | 
        
           |  |  | 1760 |         return '';
 | 
        
           |  |  | 1761 |     }
 | 
        
           |  |  | 1762 |   | 
        
           |  |  | 1763 |     /**
 | 
        
           |  |  | 1764 |      * Output the row of editing icons for a block, as defined by the controls array.
 | 
        
           |  |  | 1765 |      *
 | 
        
           |  |  | 1766 |      * @param array $controls an array like {@link block_contents::$controls}.
 | 
        
           |  |  | 1767 |      * @param string $blockid The ID given to the block.
 | 
        
           |  |  | 1768 |      * @return string HTML fragment.
 | 
        
           |  |  | 1769 |      */
 | 
        
           |  |  | 1770 |     public function block_controls($actions, $blockid = null) {
 | 
        
           |  |  | 1771 |         if (empty($actions)) {
 | 
        
           |  |  | 1772 |             return '';
 | 
        
           |  |  | 1773 |         }
 | 
        
           |  |  | 1774 |         $menu = new action_menu($actions);
 | 
        
           |  |  | 1775 |         if ($blockid !== null) {
 | 
        
           |  |  | 1776 |             $menu->set_owner_selector('#'.$blockid);
 | 
        
           |  |  | 1777 |         }
 | 
        
           |  |  | 1778 |         $menu->attributes['class'] .= ' block-control-actions commands';
 | 
        
           |  |  | 1779 |         return $this->render($menu);
 | 
        
           |  |  | 1780 |     }
 | 
        
           |  |  | 1781 |   | 
        
           |  |  | 1782 |     /**
 | 
        
           |  |  | 1783 |      * Returns the HTML for a basic textarea field.
 | 
        
           |  |  | 1784 |      *
 | 
        
           |  |  | 1785 |      * @param string $name Name to use for the textarea element
 | 
        
           |  |  | 1786 |      * @param string $id The id to use fort he textarea element
 | 
        
           |  |  | 1787 |      * @param string $value Initial content to display in the textarea
 | 
        
           |  |  | 1788 |      * @param int $rows Number of rows to display
 | 
        
           |  |  | 1789 |      * @param int $cols Number of columns to display
 | 
        
           |  |  | 1790 |      * @return string the HTML to display
 | 
        
           |  |  | 1791 |      */
 | 
        
           |  |  | 1792 |     public function print_textarea($name, $id, $value, $rows, $cols) {
 | 
        
           |  |  | 1793 |         editors_head_setup();
 | 
        
           |  |  | 1794 |         $editor = editors_get_preferred_editor(FORMAT_HTML);
 | 
        
           |  |  | 1795 |         $editor->set_text($value);
 | 
        
           |  |  | 1796 |         $editor->use_editor($id, []);
 | 
        
           |  |  | 1797 |   | 
        
           |  |  | 1798 |         $context = [
 | 
        
           |  |  | 1799 |             'id' => $id,
 | 
        
           |  |  | 1800 |             'name' => $name,
 | 
        
           |  |  | 1801 |             'value' => $value,
 | 
        
           |  |  | 1802 |             'rows' => $rows,
 | 
        
           |  |  | 1803 |             'cols' => $cols
 | 
        
           |  |  | 1804 |         ];
 | 
        
           |  |  | 1805 |   | 
        
           |  |  | 1806 |         return $this->render_from_template('core_form/editor_textarea', $context);
 | 
        
           |  |  | 1807 |     }
 | 
        
           |  |  | 1808 |   | 
        
           |  |  | 1809 |     /**
 | 
        
           |  |  | 1810 |      * Renders an action menu component.
 | 
        
           |  |  | 1811 |      *
 | 
        
           |  |  | 1812 |      * @param action_menu $menu
 | 
        
           |  |  | 1813 |      * @return string HTML
 | 
        
           |  |  | 1814 |      */
 | 
        
           |  |  | 1815 |     public function render_action_menu(action_menu $menu) {
 | 
        
           |  |  | 1816 |   | 
        
           |  |  | 1817 |         // We don't want the class icon there!
 | 
        
           |  |  | 1818 |         foreach ($menu->get_secondary_actions() as $action) {
 | 
        
           |  |  | 1819 |             if ($action instanceof \action_menu_link && $action->has_class('icon')) {
 | 
        
           |  |  | 1820 |                 $action->attributes['class'] = preg_replace('/(^|\s+)icon(\s+|$)/i', '', $action->attributes['class']);
 | 
        
           |  |  | 1821 |             }
 | 
        
           |  |  | 1822 |         }
 | 
        
           |  |  | 1823 |   | 
        
           |  |  | 1824 |         if ($menu->is_empty()) {
 | 
        
           |  |  | 1825 |             return '';
 | 
        
           |  |  | 1826 |         }
 | 
        
           |  |  | 1827 |         $context = $menu->export_for_template($this);
 | 
        
           |  |  | 1828 |   | 
        
           |  |  | 1829 |         return $this->render_from_template('core/action_menu', $context);
 | 
        
           |  |  | 1830 |     }
 | 
        
           |  |  | 1831 |   | 
        
           |  |  | 1832 |     /**
 | 
        
           |  |  | 1833 |      * Renders a full check API result including summary and details
 | 
        
           |  |  | 1834 |      *
 | 
        
           |  |  | 1835 |      * @param core\check\check $check the check that was run to get details from
 | 
        
           |  |  | 1836 |      * @param core\check\result $result the result of a check
 | 
        
           |  |  | 1837 |      * @param bool $includedetails if true, details are included as well
 | 
        
           |  |  | 1838 |      * @return string rendered html
 | 
        
           |  |  | 1839 |      */
 | 
        
           |  |  | 1840 |     protected function render_check_full_result(core\check\check $check, core\check\result $result, bool $includedetails): string {
 | 
        
           |  |  | 1841 |         // Initially render just badge itself.
 | 
        
           |  |  | 1842 |         $renderedresult = $this->render_from_template($result->get_template_name(), $result->export_for_template($this));
 | 
        
           |  |  | 1843 |   | 
        
           |  |  | 1844 |         // Add summary.
 | 
        
           |  |  | 1845 |         $renderedresult .= ' ' . $result->get_summary();
 | 
        
           |  |  | 1846 |   | 
        
           |  |  | 1847 |         // Wrap in notificaiton.
 | 
        
           |  |  | 1848 |         $notificationmap = [
 | 
        
           |  |  | 1849 |             \core\check\result::NA => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 1850 |             \core\check\result::OK => \core\output\notification::NOTIFY_SUCCESS,
 | 
        
           |  |  | 1851 |             \core\check\result::INFO => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 1852 |             \core\check\result::UNKNOWN => \core\output\notification::NOTIFY_WARNING,
 | 
        
           |  |  | 1853 |             \core\check\result::WARNING => \core\output\notification::NOTIFY_WARNING,
 | 
        
           |  |  | 1854 |             \core\check\result::ERROR => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 1855 |             \core\check\result::CRITICAL => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 1856 |         ];
 | 
        
           |  |  | 1857 |   | 
        
           |  |  | 1858 |         // Get type, or default to error.
 | 
        
           |  |  | 1859 |         $notificationtype = $notificationmap[$result->get_status()] ?? \core\output\notification::NOTIFY_ERROR;
 | 
        
           |  |  | 1860 |         $renderedresult = $this->notification($renderedresult, $notificationtype, false);
 | 
        
           |  |  | 1861 |   | 
        
           |  |  | 1862 |         // If adding details, add on new line.
 | 
        
           |  |  | 1863 |         if ($includedetails) {
 | 
        
           |  |  | 1864 |             $renderedresult .= $result->get_details();
 | 
        
           |  |  | 1865 |         }
 | 
        
           |  |  | 1866 |   | 
        
           |  |  | 1867 |         // Add the action link.
 | 
        
           |  |  | 1868 |         $renderedresult .= $this->render_action_link($check->get_action_link());
 | 
        
           |  |  | 1869 |   | 
        
           |  |  | 1870 |         return $renderedresult;
 | 
        
           |  |  | 1871 |     }
 | 
        
           |  |  | 1872 |   | 
        
           |  |  | 1873 |     /**
 | 
        
           |  |  | 1874 |      * Renders a full check API result including summary and details
 | 
        
           |  |  | 1875 |      *
 | 
        
           |  |  | 1876 |      * @param core\check\check $check the check that was run to get details from
 | 
        
           |  |  | 1877 |      * @param core\check\result $result the result of a check
 | 
        
           |  |  | 1878 |      * @param bool $includedetails if details should be included
 | 
        
           |  |  | 1879 |      * @return string HTML fragment
 | 
        
           |  |  | 1880 |      */
 | 
        
           |  |  | 1881 |     public function check_full_result(core\check\check $check, core\check\result $result, bool $includedetails = false) {
 | 
        
           |  |  | 1882 |         return $this->render_check_full_result($check, $result, $includedetails);
 | 
        
           |  |  | 1883 |     }
 | 
        
           |  |  | 1884 |   | 
        
           |  |  | 1885 |     /**
 | 
        
           |  |  | 1886 |      * Renders a Check API result
 | 
        
           |  |  | 1887 |      *
 | 
        
           |  |  | 1888 |      * @param core\check\result $result
 | 
        
           |  |  | 1889 |      * @return string HTML fragment
 | 
        
           |  |  | 1890 |      */
 | 
        
           |  |  | 1891 |     protected function render_check_result(core\check\result $result) {
 | 
        
           |  |  | 1892 |         return $this->render_from_template($result->get_template_name(), $result->export_for_template($this));
 | 
        
           |  |  | 1893 |     }
 | 
        
           |  |  | 1894 |   | 
        
           |  |  | 1895 |     /**
 | 
        
           |  |  | 1896 |      * Renders a Check API result
 | 
        
           |  |  | 1897 |      *
 | 
        
           |  |  | 1898 |      * @param core\check\result $result
 | 
        
           |  |  | 1899 |      * @return string HTML fragment
 | 
        
           |  |  | 1900 |      */
 | 
        
           |  |  | 1901 |     public function check_result(core\check\result $result) {
 | 
        
           |  |  | 1902 |         return $this->render_check_result($result);
 | 
        
           |  |  | 1903 |     }
 | 
        
           |  |  | 1904 |   | 
        
           |  |  | 1905 |     /**
 | 
        
           |  |  | 1906 |      * Renders an action_menu_link item.
 | 
        
           |  |  | 1907 |      *
 | 
        
           |  |  | 1908 |      * @param action_menu_link $action
 | 
        
           |  |  | 1909 |      * @return string HTML fragment
 | 
        
           |  |  | 1910 |      */
 | 
        
           |  |  | 1911 |     protected function render_action_menu_link(action_menu_link $action) {
 | 
        
           |  |  | 1912 |         return $this->render_from_template('core/action_menu_link', $action->export_for_template($this));
 | 
        
           |  |  | 1913 |     }
 | 
        
           |  |  | 1914 |   | 
        
           |  |  | 1915 |     /**
 | 
        
           |  |  | 1916 |      * Renders a primary action_menu_filler item.
 | 
        
           |  |  | 1917 |      *
 | 
        
           |  |  | 1918 |      * @param action_menu_filler $action
 | 
        
           |  |  | 1919 |      * @return string HTML fragment
 | 
        
           |  |  | 1920 |      */
 | 
        
           |  |  | 1921 |     protected function render_action_menu_filler(action_menu_filler $action) {
 | 
        
           |  |  | 1922 |         return html_writer::span(' ', 'filler');
 | 
        
           |  |  | 1923 |     }
 | 
        
           |  |  | 1924 |   | 
        
           |  |  | 1925 |     /**
 | 
        
           |  |  | 1926 |      * Renders a primary action_menu_link item.
 | 
        
           |  |  | 1927 |      *
 | 
        
           |  |  | 1928 |      * @param action_menu_link_primary $action
 | 
        
           |  |  | 1929 |      * @return string HTML fragment
 | 
        
           |  |  | 1930 |      */
 | 
        
           |  |  | 1931 |     protected function render_action_menu_link_primary(action_menu_link_primary $action) {
 | 
        
           |  |  | 1932 |         return $this->render_action_menu_link($action);
 | 
        
           |  |  | 1933 |     }
 | 
        
           |  |  | 1934 |   | 
        
           |  |  | 1935 |     /**
 | 
        
           |  |  | 1936 |      * Renders a secondary action_menu_link item.
 | 
        
           |  |  | 1937 |      *
 | 
        
           |  |  | 1938 |      * @param action_menu_link_secondary $action
 | 
        
           |  |  | 1939 |      * @return string HTML fragment
 | 
        
           |  |  | 1940 |      */
 | 
        
           |  |  | 1941 |     protected function render_action_menu_link_secondary(action_menu_link_secondary $action) {
 | 
        
           |  |  | 1942 |         return $this->render_action_menu_link($action);
 | 
        
           |  |  | 1943 |     }
 | 
        
           |  |  | 1944 |   | 
        
           |  |  | 1945 |     /**
 | 
        
           |  |  | 1946 |      * Prints a nice side block with an optional header.
 | 
        
           |  |  | 1947 |      *
 | 
        
           |  |  | 1948 |      * @param block_contents $bc HTML for the content
 | 
        
           |  |  | 1949 |      * @param string $region the region the block is appearing in.
 | 
        
           |  |  | 1950 |      * @return string the HTML to be output.
 | 
        
           |  |  | 1951 |      */
 | 
        
           |  |  | 1952 |     public function block(block_contents $bc, $region) {
 | 
        
           |  |  | 1953 |         $bc = clone($bc); // Avoid messing up the object passed in.
 | 
        
           |  |  | 1954 |         if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) {
 | 
        
           |  |  | 1955 |             $bc->collapsible = block_contents::NOT_HIDEABLE;
 | 
        
           |  |  | 1956 |         }
 | 
        
           |  |  | 1957 |   | 
        
           |  |  | 1958 |         $id = !empty($bc->attributes['id']) ? $bc->attributes['id'] : uniqid('block-');
 | 
        
           |  |  | 1959 |         $context = new stdClass();
 | 
        
           |  |  | 1960 |         $context->skipid = $bc->skipid;
 | 
        
           |  |  | 1961 |         $context->blockinstanceid = $bc->blockinstanceid ?: uniqid('fakeid-');
 | 
        
           |  |  | 1962 |         $context->dockable = $bc->dockable;
 | 
        
           |  |  | 1963 |         $context->id = $id;
 | 
        
           |  |  | 1964 |         $context->hidden = $bc->collapsible == block_contents::HIDDEN;
 | 
        
           |  |  | 1965 |         $context->skiptitle = strip_tags($bc->title);
 | 
        
           |  |  | 1966 |         $context->showskiplink = !empty($context->skiptitle);
 | 
        
           |  |  | 1967 |         $context->arialabel = $bc->arialabel;
 | 
        
           |  |  | 1968 |         $context->ariarole = !empty($bc->attributes['role']) ? $bc->attributes['role'] : 'complementary';
 | 
        
           |  |  | 1969 |         $context->class = $bc->attributes['class'];
 | 
        
           |  |  | 1970 |         $context->type = $bc->attributes['data-block'];
 | 
        
           |  |  | 1971 |         $context->title = $bc->title;
 | 
        
           |  |  | 1972 |         $context->content = $bc->content;
 | 
        
           |  |  | 1973 |         $context->annotation = $bc->annotation;
 | 
        
           |  |  | 1974 |         $context->footer = $bc->footer;
 | 
        
           |  |  | 1975 |         $context->hascontrols = !empty($bc->controls);
 | 
        
           |  |  | 1976 |         if ($context->hascontrols) {
 | 
        
           |  |  | 1977 |             $context->controls = $this->block_controls($bc->controls, $id);
 | 
        
           |  |  | 1978 |         }
 | 
        
           |  |  | 1979 |   | 
        
           |  |  | 1980 |         return $this->render_from_template('core/block', $context);
 | 
        
           |  |  | 1981 |     }
 | 
        
           |  |  | 1982 |   | 
        
           |  |  | 1983 |     /**
 | 
        
           |  |  | 1984 |      * Render the contents of a block_list.
 | 
        
           |  |  | 1985 |      *
 | 
        
           |  |  | 1986 |      * @param array $icons the icon for each item.
 | 
        
           |  |  | 1987 |      * @param array $items the content of each item.
 | 
        
           |  |  | 1988 |      * @return string HTML
 | 
        
           |  |  | 1989 |      */
 | 
        
           |  |  | 1990 |     public function list_block_contents($icons, $items) {
 | 
        
           |  |  | 1991 |         $row = 0;
 | 
        
           |  |  | 1992 |         $lis = array();
 | 
        
           |  |  | 1993 |         foreach ($items as $key => $string) {
 | 
        
           |  |  | 1994 |             $item = html_writer::start_tag('li', array('class' => 'r' . $row));
 | 
        
           |  |  | 1995 |             if (!empty($icons[$key])) { //test if the content has an assigned icon
 | 
        
           |  |  | 1996 |                 $item .= html_writer::tag('div', $icons[$key], array('class' => 'icon column c0'));
 | 
        
           |  |  | 1997 |             }
 | 
        
           |  |  | 1998 |             $item .= html_writer::tag('div', $string, array('class' => 'column c1'));
 | 
        
           |  |  | 1999 |             $item .= html_writer::end_tag('li');
 | 
        
           |  |  | 2000 |             $lis[] = $item;
 | 
        
           |  |  | 2001 |             $row = 1 - $row; // Flip even/odd.
 | 
        
           |  |  | 2002 |         }
 | 
        
           |  |  | 2003 |         return html_writer::tag('ul', implode("\n", $lis), array('class' => 'unlist'));
 | 
        
           |  |  | 2004 |     }
 | 
        
           |  |  | 2005 |   | 
        
           |  |  | 2006 |     /**
 | 
        
           |  |  | 2007 |      * Output all the blocks in a particular region.
 | 
        
           |  |  | 2008 |      *
 | 
        
           |  |  | 2009 |      * @param string $region the name of a region on this page.
 | 
        
           |  |  | 2010 |      * @param boolean $fakeblocksonly Output fake block only.
 | 
        
           |  |  | 2011 |      * @return string the HTML to be output.
 | 
        
           |  |  | 2012 |      */
 | 
        
           |  |  | 2013 |     public function blocks_for_region($region, $fakeblocksonly = false) {
 | 
        
           |  |  | 2014 |         $blockcontents = $this->page->blocks->get_content_for_region($region, $this);
 | 
        
           |  |  | 2015 |         $lastblock = null;
 | 
        
           |  |  | 2016 |         $zones = array();
 | 
        
           |  |  | 2017 |         foreach ($blockcontents as $bc) {
 | 
        
           |  |  | 2018 |             if ($bc instanceof block_contents) {
 | 
        
           |  |  | 2019 |                 $zones[] = $bc->title;
 | 
        
           |  |  | 2020 |             }
 | 
        
           |  |  | 2021 |         }
 | 
        
           |  |  | 2022 |         $output = '';
 | 
        
           |  |  | 2023 |   | 
        
           |  |  | 2024 |         foreach ($blockcontents as $bc) {
 | 
        
           |  |  | 2025 |             if ($bc instanceof block_contents) {
 | 
        
           |  |  | 2026 |                 if ($fakeblocksonly && !$bc->is_fake()) {
 | 
        
           |  |  | 2027 |                     // Skip rendering real blocks if we only want to show fake blocks.
 | 
        
           |  |  | 2028 |                     continue;
 | 
        
           |  |  | 2029 |                 }
 | 
        
           |  |  | 2030 |                 $output .= $this->block($bc, $region);
 | 
        
           |  |  | 2031 |                 $lastblock = $bc->title;
 | 
        
           |  |  | 2032 |             } else if ($bc instanceof block_move_target) {
 | 
        
           |  |  | 2033 |                 if (!$fakeblocksonly) {
 | 
        
           |  |  | 2034 |                     $output .= $this->block_move_target($bc, $zones, $lastblock, $region);
 | 
        
           |  |  | 2035 |                 }
 | 
        
           |  |  | 2036 |             } else {
 | 
        
           |  |  | 2037 |                 throw new coding_exception('Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.');
 | 
        
           |  |  | 2038 |             }
 | 
        
           |  |  | 2039 |         }
 | 
        
           |  |  | 2040 |         return $output;
 | 
        
           |  |  | 2041 |     }
 | 
        
           |  |  | 2042 |   | 
        
           |  |  | 2043 |     /**
 | 
        
           |  |  | 2044 |      * Output a place where the block that is currently being moved can be dropped.
 | 
        
           |  |  | 2045 |      *
 | 
        
           |  |  | 2046 |      * @param block_move_target $target with the necessary details.
 | 
        
           |  |  | 2047 |      * @param array $zones array of areas where the block can be moved to
 | 
        
           |  |  | 2048 |      * @param string $previous the block located before the area currently being rendered.
 | 
        
           |  |  | 2049 |      * @param string $region the name of the region
 | 
        
           |  |  | 2050 |      * @return string the HTML to be output.
 | 
        
           |  |  | 2051 |      */
 | 
        
           |  |  | 2052 |     public function block_move_target($target, $zones, $previous, $region) {
 | 
        
           |  |  | 2053 |         if ($previous == null) {
 | 
        
           |  |  | 2054 |             if (empty($zones)) {
 | 
        
           |  |  | 2055 |                 // There are no zones, probably because there are no blocks.
 | 
        
           |  |  | 2056 |                 $regions = $this->page->theme->get_all_block_regions();
 | 
        
           |  |  | 2057 |                 $position = get_string('moveblockinregion', 'block', $regions[$region]);
 | 
        
           |  |  | 2058 |             } else {
 | 
        
           |  |  | 2059 |                 $position = get_string('moveblockbefore', 'block', $zones[0]);
 | 
        
           |  |  | 2060 |             }
 | 
        
           |  |  | 2061 |         } else {
 | 
        
           |  |  | 2062 |             $position = get_string('moveblockafter', 'block', $previous);
 | 
        
           |  |  | 2063 |         }
 | 
        
           |  |  | 2064 |         return html_writer::tag('a', html_writer::tag('span', $position, array('class' => 'accesshide')), array('href' => $target->url, 'class' => 'blockmovetarget'));
 | 
        
           |  |  | 2065 |     }
 | 
        
           |  |  | 2066 |   | 
        
           |  |  | 2067 |     /**
 | 
        
           |  |  | 2068 |      * Renders a special html link with attached action
 | 
        
           |  |  | 2069 |      *
 | 
        
           |  |  | 2070 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2071 |      * {@link core_renderer::render_action_link()} instead.
 | 
        
           |  |  | 2072 |      *
 | 
        
           |  |  | 2073 |      * @param string|moodle_url $url
 | 
        
           |  |  | 2074 |      * @param string $text HTML fragment
 | 
        
           |  |  | 2075 |      * @param component_action $action
 | 
        
           |  |  | 2076 |      * @param array $attributes associative array of html link attributes + disabled
 | 
        
           |  |  | 2077 |      * @param pix_icon optional pix icon to render with the link
 | 
        
           |  |  | 2078 |      * @return string HTML fragment
 | 
        
           |  |  | 2079 |      */
 | 
        
           |  |  | 2080 |     public function action_link($url, $text, component_action $action = null, array $attributes = null, $icon = null) {
 | 
        
           |  |  | 2081 |         if (!($url instanceof moodle_url)) {
 | 
        
           |  |  | 2082 |             $url = new moodle_url($url);
 | 
        
           |  |  | 2083 |         }
 | 
        
           |  |  | 2084 |         $link = new action_link($url, $text, $action, $attributes, $icon);
 | 
        
           |  |  | 2085 |   | 
        
           |  |  | 2086 |         return $this->render($link);
 | 
        
           |  |  | 2087 |     }
 | 
        
           |  |  | 2088 |   | 
        
           |  |  | 2089 |     /**
 | 
        
           |  |  | 2090 |      * Renders an action_link object.
 | 
        
           |  |  | 2091 |      *
 | 
        
           |  |  | 2092 |      * The provided link is renderer and the HTML returned. At the same time the
 | 
        
           |  |  | 2093 |      * associated actions are setup in JS by {@link core_renderer::add_action_handler()}
 | 
        
           |  |  | 2094 |      *
 | 
        
           |  |  | 2095 |      * @param action_link $link
 | 
        
           |  |  | 2096 |      * @return string HTML fragment
 | 
        
           |  |  | 2097 |      */
 | 
        
           |  |  | 2098 |     protected function render_action_link(action_link $link) {
 | 
        
           |  |  | 2099 |         return $this->render_from_template('core/action_link', $link->export_for_template($this));
 | 
        
           |  |  | 2100 |     }
 | 
        
           |  |  | 2101 |   | 
        
           |  |  | 2102 |     /**
 | 
        
           |  |  | 2103 |      * Renders an action_icon.
 | 
        
           |  |  | 2104 |      *
 | 
        
           |  |  | 2105 |      * This function uses the {@link core_renderer::action_link()} method for the
 | 
        
           |  |  | 2106 |      * most part. What it does different is prepare the icon as HTML and use it
 | 
        
           |  |  | 2107 |      * as the link text.
 | 
        
           |  |  | 2108 |      *
 | 
        
           |  |  | 2109 |      * Theme developers: If you want to change how action links and/or icons are rendered,
 | 
        
           |  |  | 2110 |      * consider overriding function {@link core_renderer::render_action_link()} and
 | 
        
           |  |  | 2111 |      * {@link core_renderer::render_pix_icon()}.
 | 
        
           |  |  | 2112 |      *
 | 
        
           |  |  | 2113 |      * @param string|moodle_url $url A string URL or moodel_url
 | 
        
           |  |  | 2114 |      * @param pix_icon $pixicon
 | 
        
           |  |  | 2115 |      * @param component_action $action
 | 
        
           |  |  | 2116 |      * @param array $attributes associative array of html link attributes + disabled
 | 
        
           |  |  | 2117 |      * @param bool $linktext show title next to image in link
 | 
        
           |  |  | 2118 |      * @return string HTML fragment
 | 
        
           |  |  | 2119 |      */
 | 
        
           |  |  | 2120 |     public function action_icon($url, pix_icon $pixicon, component_action $action = null, array $attributes = null, $linktext=false) {
 | 
        
           |  |  | 2121 |         if (!($url instanceof moodle_url)) {
 | 
        
           |  |  | 2122 |             $url = new moodle_url($url);
 | 
        
           |  |  | 2123 |         }
 | 
        
           |  |  | 2124 |         $attributes = (array)$attributes;
 | 
        
           |  |  | 2125 |   | 
        
           |  |  | 2126 |         if (empty($attributes['class'])) {
 | 
        
           |  |  | 2127 |             // let ppl override the class via $options
 | 
        
           |  |  | 2128 |             $attributes['class'] = 'action-icon';
 | 
        
           |  |  | 2129 |         }
 | 
        
           |  |  | 2130 |   | 
        
           |  |  | 2131 |         if ($linktext) {
 | 
        
           |  |  | 2132 |             $text = $pixicon->attributes['alt'];
 | 
        
           |  |  | 2133 |             // Set the icon as a decorative image if we're displaying the action text.
 | 
        
           |  |  | 2134 |             // Otherwise, the action name will be read twice by assistive technologies.
 | 
        
           |  |  | 2135 |             $pixicon->attributes['alt'] = '';
 | 
        
           |  |  | 2136 |             $pixicon->attributes['title'] = '';
 | 
        
           |  |  | 2137 |             $pixicon->attributes['aria-hidden'] = 'true';
 | 
        
           |  |  | 2138 |         } else {
 | 
        
           |  |  | 2139 |             $text = '';
 | 
        
           |  |  | 2140 |         }
 | 
        
           |  |  | 2141 |   | 
        
           |  |  | 2142 |         $icon = $this->render($pixicon);
 | 
        
           |  |  | 2143 |   | 
        
           |  |  | 2144 |         return $this->action_link($url, $text.$icon, $action, $attributes);
 | 
        
           |  |  | 2145 |     }
 | 
        
           |  |  | 2146 |   | 
        
           |  |  | 2147 |    /**
 | 
        
           |  |  | 2148 |     * Print a message along with button choices for Continue/Cancel
 | 
        
           |  |  | 2149 |     *
 | 
        
           |  |  | 2150 |     * If a string or moodle_url is given instead of a single_button, method defaults to post.
 | 
        
           |  |  | 2151 |     *
 | 
        
           |  |  | 2152 |     * @param string $message The question to ask the user
 | 
        
           |  |  | 2153 |     * @param single_button|moodle_url|string $continue The single_button component representing the Continue answer. Can also be a moodle_url or string URL
 | 
        
           |  |  | 2154 |     * @param single_button|moodle_url|string $cancel The single_button component representing the Cancel answer. Can also be a moodle_url or string URL
 | 
        
           |  |  | 2155 |     * @param array $displayoptions optional extra display options
 | 
        
           |  |  | 2156 |     * @return string HTML fragment
 | 
        
           |  |  | 2157 |     */
 | 
        
           |  |  | 2158 |     public function confirm($message, $continue, $cancel, array $displayoptions = []) {
 | 
        
           |  |  | 2159 |   | 
        
           |  |  | 2160 |         // Check existing displayoptions.
 | 
        
           |  |  | 2161 |         $displayoptions['confirmtitle'] = $displayoptions['confirmtitle'] ?? get_string('confirm');
 | 
        
           |  |  | 2162 |         $displayoptions['continuestr'] = $displayoptions['continuestr'] ?? get_string('continue');
 | 
        
           |  |  | 2163 |         $displayoptions['cancelstr'] = $displayoptions['cancelstr'] ?? get_string('cancel');
 | 
        
           |  |  | 2164 |   | 
        
           |  |  | 2165 |         if ($continue instanceof single_button) {
 | 
        
           |  |  | 2166 |             // Continue button should be primary if set to secondary type as it is the fefault.
 | 
        
           |  |  | 2167 |             if ($continue->type === single_button::BUTTON_SECONDARY) {
 | 
        
           |  |  | 2168 |                 $continue->type = single_button::BUTTON_PRIMARY;
 | 
        
           |  |  | 2169 |             }
 | 
        
           |  |  | 2170 |         } else if (is_string($continue)) {
 | 
        
           |  |  | 2171 |             $continue = new single_button(new moodle_url($continue), $displayoptions['continuestr'], 'post',
 | 
        
           |  |  | 2172 |                 $displayoptions['type'] ?? single_button::BUTTON_PRIMARY);
 | 
        
           |  |  | 2173 |         } else if ($continue instanceof moodle_url) {
 | 
        
           |  |  | 2174 |             $continue = new single_button($continue, $displayoptions['continuestr'], 'post',
 | 
        
           |  |  | 2175 |                 $displayoptions['type'] ?? single_button::BUTTON_PRIMARY);
 | 
        
           |  |  | 2176 |         } else {
 | 
        
           |  |  | 2177 |             throw new coding_exception('The continue param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
 | 
        
           |  |  | 2178 |         }
 | 
        
           |  |  | 2179 |   | 
        
           |  |  | 2180 |         if ($cancel instanceof single_button) {
 | 
        
           |  |  | 2181 |             // ok
 | 
        
           |  |  | 2182 |         } else if (is_string($cancel)) {
 | 
        
           |  |  | 2183 |             $cancel = new single_button(new moodle_url($cancel), $displayoptions['cancelstr'], 'get');
 | 
        
           |  |  | 2184 |         } else if ($cancel instanceof moodle_url) {
 | 
        
           |  |  | 2185 |             $cancel = new single_button($cancel, $displayoptions['cancelstr'], 'get');
 | 
        
           |  |  | 2186 |         } else {
 | 
        
           |  |  | 2187 |             throw new coding_exception('The cancel param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
 | 
        
           |  |  | 2188 |         }
 | 
        
           |  |  | 2189 |   | 
        
           |  |  | 2190 |         $attributes = [
 | 
        
           |  |  | 2191 |             'role'=>'alertdialog',
 | 
        
           |  |  | 2192 |             'aria-labelledby'=>'modal-header',
 | 
        
           |  |  | 2193 |             'aria-describedby'=>'modal-body',
 | 
        
           |  |  | 2194 |             'aria-modal'=>'true'
 | 
        
           |  |  | 2195 |         ];
 | 
        
           |  |  | 2196 |   | 
        
           |  |  | 2197 |         $output = $this->box_start('generalbox modal modal-dialog modal-in-page show', 'notice', $attributes);
 | 
        
           |  |  | 2198 |         $output .= $this->box_start('modal-content', 'modal-content');
 | 
        
           |  |  | 2199 |         $output .= $this->box_start('modal-header px-3', 'modal-header');
 | 
        
           |  |  | 2200 |         $output .= html_writer::tag('h4', $displayoptions['confirmtitle']);
 | 
        
           |  |  | 2201 |         $output .= $this->box_end();
 | 
        
           |  |  | 2202 |         $attributes = [
 | 
        
           |  |  | 2203 |             'role'=>'alert',
 | 
        
           |  |  | 2204 |             'data-aria-autofocus'=>'true'
 | 
        
           |  |  | 2205 |         ];
 | 
        
           |  |  | 2206 |         $output .= $this->box_start('modal-body', 'modal-body', $attributes);
 | 
        
           |  |  | 2207 |         $output .= html_writer::tag('p', $message);
 | 
        
           |  |  | 2208 |         $output .= $this->box_end();
 | 
        
           |  |  | 2209 |         $output .= $this->box_start('modal-footer', 'modal-footer');
 | 
        
           |  |  | 2210 |         $output .= html_writer::tag('div', $this->render($cancel) . $this->render($continue), ['class' => 'buttons']);
 | 
        
           |  |  | 2211 |         $output .= $this->box_end();
 | 
        
           |  |  | 2212 |         $output .= $this->box_end();
 | 
        
           |  |  | 2213 |         $output .= $this->box_end();
 | 
        
           |  |  | 2214 |         return $output;
 | 
        
           |  |  | 2215 |     }
 | 
        
           |  |  | 2216 |   | 
        
           |  |  | 2217 |     /**
 | 
        
           |  |  | 2218 |      * Returns a form with a single button.
 | 
        
           |  |  | 2219 |      *
 | 
        
           |  |  | 2220 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2221 |      * {@link core_renderer::render_single_button()} instead.
 | 
        
           |  |  | 2222 |      *
 | 
        
           |  |  | 2223 |      * @param string|moodle_url $url
 | 
        
           |  |  | 2224 |      * @param string $label button text
 | 
        
           |  |  | 2225 |      * @param string $method get or post submit method
 | 
        
           |  |  | 2226 |      * @param array $options associative array {disabled, title, etc.}
 | 
        
           |  |  | 2227 |      * @return string HTML fragment
 | 
        
           |  |  | 2228 |      */
 | 
        
           |  |  | 2229 |     public function single_button($url, $label, $method='post', array $options=null) {
 | 
        
           |  |  | 2230 |         if (!($url instanceof moodle_url)) {
 | 
        
           |  |  | 2231 |             $url = new moodle_url($url);
 | 
        
           |  |  | 2232 |         }
 | 
        
           |  |  | 2233 |         $button = new single_button($url, $label, $method);
 | 
        
           |  |  | 2234 |   | 
        
           |  |  | 2235 |         foreach ((array)$options as $key=>$value) {
 | 
        
           |  |  | 2236 |             if (property_exists($button, $key)) {
 | 
        
           |  |  | 2237 |                 $button->$key = $value;
 | 
        
           |  |  | 2238 |             } else {
 | 
        
           |  |  | 2239 |                 $button->set_attribute($key, $value);
 | 
        
           |  |  | 2240 |             }
 | 
        
           |  |  | 2241 |         }
 | 
        
           |  |  | 2242 |   | 
        
           |  |  | 2243 |         return $this->render($button);
 | 
        
           |  |  | 2244 |     }
 | 
        
           |  |  | 2245 |   | 
        
           |  |  | 2246 |     /**
 | 
        
           |  |  | 2247 |      * Renders a single button widget.
 | 
        
           |  |  | 2248 |      *
 | 
        
           |  |  | 2249 |      * This will return HTML to display a form containing a single button.
 | 
        
           |  |  | 2250 |      *
 | 
        
           |  |  | 2251 |      * @param single_button $button
 | 
        
           |  |  | 2252 |      * @return string HTML fragment
 | 
        
           |  |  | 2253 |      */
 | 
        
           |  |  | 2254 |     protected function render_single_button(single_button $button) {
 | 
        
           |  |  | 2255 |         return $this->render_from_template('core/single_button', $button->export_for_template($this));
 | 
        
           |  |  | 2256 |     }
 | 
        
           |  |  | 2257 |   | 
        
           |  |  | 2258 |     /**
 | 
        
           |  |  | 2259 |      * Returns a form with a single select widget.
 | 
        
           |  |  | 2260 |      *
 | 
        
           |  |  | 2261 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2262 |      * {@link core_renderer::render_single_select()} instead.
 | 
        
           |  |  | 2263 |      *
 | 
        
           |  |  | 2264 |      * @param moodle_url $url form action target, includes hidden fields
 | 
        
           |  |  | 2265 |      * @param string $name name of selection field - the changing parameter in url
 | 
        
           |  |  | 2266 |      * @param array $options list of options
 | 
        
           |  |  | 2267 |      * @param string $selected selected element
 | 
        
           |  |  | 2268 |      * @param array $nothing
 | 
        
           |  |  | 2269 |      * @param string $formid
 | 
        
           |  |  | 2270 |      * @param array $attributes other attributes for the single select
 | 
        
           |  |  | 2271 |      * @return string HTML fragment
 | 
        
           |  |  | 2272 |      */
 | 
        
           |  |  | 2273 |     public function single_select($url, $name, array $options, $selected = '',
 | 
        
           |  |  | 2274 |                                 $nothing = array('' => 'choosedots'), $formid = null, $attributes = array()) {
 | 
        
           |  |  | 2275 |         if (!($url instanceof moodle_url)) {
 | 
        
           |  |  | 2276 |             $url = new moodle_url($url);
 | 
        
           |  |  | 2277 |         }
 | 
        
           |  |  | 2278 |         $select = new single_select($url, $name, $options, $selected, $nothing, $formid);
 | 
        
           |  |  | 2279 |   | 
        
           |  |  | 2280 |         if (array_key_exists('label', $attributes)) {
 | 
        
           |  |  | 2281 |             $select->set_label($attributes['label']);
 | 
        
           |  |  | 2282 |             unset($attributes['label']);
 | 
        
           |  |  | 2283 |         }
 | 
        
           |  |  | 2284 |         $select->attributes = $attributes;
 | 
        
           |  |  | 2285 |   | 
        
           |  |  | 2286 |         return $this->render($select);
 | 
        
           |  |  | 2287 |     }
 | 
        
           |  |  | 2288 |   | 
        
           |  |  | 2289 |     /**
 | 
        
           |  |  | 2290 |      * Returns a dataformat selection and download form
 | 
        
           |  |  | 2291 |      *
 | 
        
           |  |  | 2292 |      * @param string $label A text label
 | 
        
           |  |  | 2293 |      * @param moodle_url|string $base The download page url
 | 
        
           |  |  | 2294 |      * @param string $name The query param which will hold the type of the download
 | 
        
           |  |  | 2295 |      * @param array $params Extra params sent to the download page
 | 
        
           |  |  | 2296 |      * @return string HTML fragment
 | 
        
           |  |  | 2297 |      */
 | 
        
           |  |  | 2298 |     public function download_dataformat_selector($label, $base, $name = 'dataformat', $params = array()) {
 | 
        
           |  |  | 2299 |   | 
        
           |  |  | 2300 |         $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
 | 
        
           |  |  | 2301 |         $options = array();
 | 
        
           |  |  | 2302 |         foreach ($formats as $format) {
 | 
        
           |  |  | 2303 |             if ($format->is_enabled()) {
 | 
        
           |  |  | 2304 |                 $options[] = array(
 | 
        
           |  |  | 2305 |                     'value' => $format->name,
 | 
        
           |  |  | 2306 |                     'label' => get_string('dataformat', $format->component),
 | 
        
           |  |  | 2307 |                 );
 | 
        
           |  |  | 2308 |             }
 | 
        
           |  |  | 2309 |         }
 | 
        
           |  |  | 2310 |         $hiddenparams = array();
 | 
        
           |  |  | 2311 |         foreach ($params as $key => $value) {
 | 
        
           |  |  | 2312 |             $hiddenparams[] = array(
 | 
        
           |  |  | 2313 |                 'name' => $key,
 | 
        
           |  |  | 2314 |                 'value' => $value,
 | 
        
           |  |  | 2315 |             );
 | 
        
           |  |  | 2316 |         }
 | 
        
           |  |  | 2317 |         $data = array(
 | 
        
           |  |  | 2318 |             'label' => $label,
 | 
        
           |  |  | 2319 |             'base' => $base,
 | 
        
           |  |  | 2320 |             'name' => $name,
 | 
        
           |  |  | 2321 |             'params' => $hiddenparams,
 | 
        
           |  |  | 2322 |             'options' => $options,
 | 
        
           |  |  | 2323 |             'sesskey' => sesskey(),
 | 
        
           |  |  | 2324 |             'submit' => get_string('download'),
 | 
        
           |  |  | 2325 |         );
 | 
        
           |  |  | 2326 |   | 
        
           |  |  | 2327 |         return $this->render_from_template('core/dataformat_selector', $data);
 | 
        
           |  |  | 2328 |     }
 | 
        
           |  |  | 2329 |   | 
        
           |  |  | 2330 |   | 
        
           |  |  | 2331 |     /**
 | 
        
           |  |  | 2332 |      * Internal implementation of single_select rendering
 | 
        
           |  |  | 2333 |      *
 | 
        
           |  |  | 2334 |      * @param single_select $select
 | 
        
           |  |  | 2335 |      * @return string HTML fragment
 | 
        
           |  |  | 2336 |      */
 | 
        
           |  |  | 2337 |     protected function render_single_select(single_select $select) {
 | 
        
           |  |  | 2338 |         return $this->render_from_template('core/single_select', $select->export_for_template($this));
 | 
        
           |  |  | 2339 |     }
 | 
        
           |  |  | 2340 |   | 
        
           |  |  | 2341 |     /**
 | 
        
           |  |  | 2342 |      * Returns a form with a url select widget.
 | 
        
           |  |  | 2343 |      *
 | 
        
           |  |  | 2344 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2345 |      * {@link core_renderer::render_url_select()} instead.
 | 
        
           |  |  | 2346 |      *
 | 
        
           |  |  | 2347 |      * @param array $urls list of urls - array('/course/view.php?id=1'=>'Frontpage', ....)
 | 
        
           |  |  | 2348 |      * @param string $selected selected element
 | 
        
           |  |  | 2349 |      * @param array $nothing
 | 
        
           |  |  | 2350 |      * @param string $formid
 | 
        
           |  |  | 2351 |      * @return string HTML fragment
 | 
        
           |  |  | 2352 |      */
 | 
        
           |  |  | 2353 |     public function url_select(array $urls, $selected, $nothing = array('' => 'choosedots'), $formid = null) {
 | 
        
           |  |  | 2354 |         $select = new url_select($urls, $selected, $nothing, $formid);
 | 
        
           |  |  | 2355 |         return $this->render($select);
 | 
        
           |  |  | 2356 |     }
 | 
        
           |  |  | 2357 |   | 
        
           |  |  | 2358 |     /**
 | 
        
           |  |  | 2359 |      * Internal implementation of url_select rendering
 | 
        
           |  |  | 2360 |      *
 | 
        
           |  |  | 2361 |      * @param url_select $select
 | 
        
           |  |  | 2362 |      * @return string HTML fragment
 | 
        
           |  |  | 2363 |      */
 | 
        
           |  |  | 2364 |     protected function render_url_select(url_select $select) {
 | 
        
           |  |  | 2365 |         return $this->render_from_template('core/url_select', $select->export_for_template($this));
 | 
        
           |  |  | 2366 |     }
 | 
        
           |  |  | 2367 |   | 
        
           |  |  | 2368 |     /**
 | 
        
           |  |  | 2369 |      * Returns a string containing a link to the user documentation.
 | 
        
           |  |  | 2370 |      * Also contains an icon by default. Shown to teachers and admin only.
 | 
        
           |  |  | 2371 |      *
 | 
        
           |  |  | 2372 |      * @param string $path The page link after doc root and language, no leading slash.
 | 
        
           |  |  | 2373 |      * @param string $text The text to be displayed for the link
 | 
        
           |  |  | 2374 |      * @param boolean $forcepopup Whether to force a popup regardless of the value of $CFG->doctonewwindow
 | 
        
           |  |  | 2375 |      * @param array $attributes htm attributes
 | 
        
           |  |  | 2376 |      * @return string
 | 
        
           |  |  | 2377 |      */
 | 
        
           |  |  | 2378 |     public function doc_link($path, $text = '', $forcepopup = false, array $attributes = []) {
 | 
        
           |  |  | 2379 |         global $CFG;
 | 
        
           |  |  | 2380 |   | 
        
           |  |  | 2381 |         $icon = $this->pix_icon('book', '', 'moodle', array('class' => 'iconhelp icon-pre'));
 | 
        
           |  |  | 2382 |   | 
        
           |  |  | 2383 |         $attributes['href'] = new moodle_url(get_docs_url($path));
 | 
        
           |  |  | 2384 |         $newwindowicon = '';
 | 
        
           |  |  | 2385 |         if (!empty($CFG->doctonewwindow) || $forcepopup) {
 | 
        
           |  |  | 2386 |             $attributes['target'] = '_blank';
 | 
        
           |  |  | 2387 |             $newwindowicon = $this->pix_icon('i/externallink', get_string('opensinnewwindow'), 'moodle',
 | 
        
           |  |  | 2388 |             ['class' => 'fa fa-externallink fa-fw']);
 | 
        
           |  |  | 2389 |         }
 | 
        
           |  |  | 2390 |   | 
        
           |  |  | 2391 |         return html_writer::tag('a', $icon . $text . $newwindowicon, $attributes);
 | 
        
           |  |  | 2392 |     }
 | 
        
           |  |  | 2393 |   | 
        
           |  |  | 2394 |     /**
 | 
        
           |  |  | 2395 |      * Return HTML for an image_icon.
 | 
        
           |  |  | 2396 |      *
 | 
        
           |  |  | 2397 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2398 |      * {@link core_renderer::render_image_icon()} instead.
 | 
        
           |  |  | 2399 |      *
 | 
        
           |  |  | 2400 |      * @param string $pix short pix name
 | 
        
           |  |  | 2401 |      * @param string $alt mandatory alt attribute
 | 
        
           |  |  | 2402 |      * @param string $component standard compoennt name like 'moodle', 'mod_forum', etc.
 | 
        
           |  |  | 2403 |      * @param array $attributes htm attributes
 | 
        
           |  |  | 2404 |      * @return string HTML fragment
 | 
        
           |  |  | 2405 |      */
 | 
        
           |  |  | 2406 |     public function image_icon($pix, $alt, $component='moodle', array $attributes = null) {
 | 
        
           |  |  | 2407 |         $icon = new image_icon($pix, $alt, $component, $attributes);
 | 
        
           |  |  | 2408 |         return $this->render($icon);
 | 
        
           |  |  | 2409 |     }
 | 
        
           |  |  | 2410 |   | 
        
           |  |  | 2411 |     /**
 | 
        
           |  |  | 2412 |      * Renders a pix_icon widget and returns the HTML to display it.
 | 
        
           |  |  | 2413 |      *
 | 
        
           |  |  | 2414 |      * @param image_icon $icon
 | 
        
           |  |  | 2415 |      * @return string HTML fragment
 | 
        
           |  |  | 2416 |      */
 | 
        
           |  |  | 2417 |     protected function render_image_icon(image_icon $icon) {
 | 
        
           |  |  | 2418 |         $system = \core\output\icon_system::instance(\core\output\icon_system::STANDARD);
 | 
        
           |  |  | 2419 |         return $system->render_pix_icon($this, $icon);
 | 
        
           |  |  | 2420 |     }
 | 
        
           |  |  | 2421 |   | 
        
           |  |  | 2422 |     /**
 | 
        
           |  |  | 2423 |      * Return HTML for a pix_icon.
 | 
        
           |  |  | 2424 |      *
 | 
        
           |  |  | 2425 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2426 |      * {@link core_renderer::render_pix_icon()} instead.
 | 
        
           |  |  | 2427 |      *
 | 
        
           |  |  | 2428 |      * @param string $pix short pix name
 | 
        
           |  |  | 2429 |      * @param string $alt mandatory alt attribute
 | 
        
           |  |  | 2430 |      * @param string $component standard compoennt name like 'moodle', 'mod_forum', etc.
 | 
        
           |  |  | 2431 |      * @param array $attributes htm lattributes
 | 
        
           |  |  | 2432 |      * @return string HTML fragment
 | 
        
           |  |  | 2433 |      */
 | 
        
           |  |  | 2434 |     public function pix_icon($pix, $alt, $component='moodle', array $attributes = null) {
 | 
        
           |  |  | 2435 |         $icon = new pix_icon($pix, $alt, $component, $attributes);
 | 
        
           |  |  | 2436 |         return $this->render($icon);
 | 
        
           |  |  | 2437 |     }
 | 
        
           |  |  | 2438 |   | 
        
           |  |  | 2439 |     /**
 | 
        
           |  |  | 2440 |      * Renders a pix_icon widget and returns the HTML to display it.
 | 
        
           |  |  | 2441 |      *
 | 
        
           |  |  | 2442 |      * @param pix_icon $icon
 | 
        
           |  |  | 2443 |      * @return string HTML fragment
 | 
        
           |  |  | 2444 |      */
 | 
        
           |  |  | 2445 |     protected function render_pix_icon(pix_icon $icon) {
 | 
        
           |  |  | 2446 |         $system = \core\output\icon_system::instance();
 | 
        
           |  |  | 2447 |         return $system->render_pix_icon($this, $icon);
 | 
        
           |  |  | 2448 |     }
 | 
        
           |  |  | 2449 |   | 
        
           |  |  | 2450 |     /**
 | 
        
           |  |  | 2451 |      * Return HTML to display an emoticon icon.
 | 
        
           |  |  | 2452 |      *
 | 
        
           |  |  | 2453 |      * @param pix_emoticon $emoticon
 | 
        
           |  |  | 2454 |      * @return string HTML fragment
 | 
        
           |  |  | 2455 |      */
 | 
        
           |  |  | 2456 |     protected function render_pix_emoticon(pix_emoticon $emoticon) {
 | 
        
           |  |  | 2457 |         $system = \core\output\icon_system::instance(\core\output\icon_system::STANDARD);
 | 
        
           |  |  | 2458 |         return $system->render_pix_icon($this, $emoticon);
 | 
        
           |  |  | 2459 |     }
 | 
        
           |  |  | 2460 |   | 
        
           |  |  | 2461 |     /**
 | 
        
           |  |  | 2462 |      * Produces the html that represents this rating in the UI
 | 
        
           |  |  | 2463 |      *
 | 
        
           |  |  | 2464 |      * @param rating $rating the page object on which this rating will appear
 | 
        
           |  |  | 2465 |      * @return string
 | 
        
           |  |  | 2466 |      */
 | 
        
           |  |  | 2467 |     function render_rating(rating $rating) {
 | 
        
           |  |  | 2468 |         global $CFG, $USER;
 | 
        
           |  |  | 2469 |   | 
        
           |  |  | 2470 |         if ($rating->settings->aggregationmethod == RATING_AGGREGATE_NONE) {
 | 
        
           |  |  | 2471 |             return null;//ratings are turned off
 | 
        
           |  |  | 2472 |         }
 | 
        
           |  |  | 2473 |   | 
        
           |  |  | 2474 |         $ratingmanager = new rating_manager();
 | 
        
           |  |  | 2475 |         // Initialise the JavaScript so ratings can be done by AJAX.
 | 
        
           |  |  | 2476 |         $ratingmanager->initialise_rating_javascript($this->page);
 | 
        
           |  |  | 2477 |   | 
        
           |  |  | 2478 |         $strrate = get_string("rate", "rating");
 | 
        
           |  |  | 2479 |         $ratinghtml = ''; //the string we'll return
 | 
        
           |  |  | 2480 |   | 
        
           |  |  | 2481 |         // permissions check - can they view the aggregate?
 | 
        
           |  |  | 2482 |         if ($rating->user_can_view_aggregate()) {
 | 
        
           |  |  | 2483 |   | 
        
           |  |  | 2484 |             $aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
 | 
        
           |  |  | 2485 |             $aggregatelabel = html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
 | 
        
           |  |  | 2486 |             $aggregatestr   = $rating->get_aggregate_string();
 | 
        
           |  |  | 2487 |   | 
        
           |  |  | 2488 |             $aggregatehtml  = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid, 'class' => 'ratingaggregate')).' ';
 | 
        
           |  |  | 2489 |             if ($rating->count > 0) {
 | 
        
           |  |  | 2490 |                 $countstr = "({$rating->count})";
 | 
        
           |  |  | 2491 |             } else {
 | 
        
           |  |  | 2492 |                 $countstr = '-';
 | 
        
           |  |  | 2493 |             }
 | 
        
           |  |  | 2494 |             $aggregatehtml .= html_writer::tag('span', $countstr, array('id'=>"ratingcount{$rating->itemid}", 'class' => 'ratingcount')).' ';
 | 
        
           |  |  | 2495 |   | 
        
           |  |  | 2496 |             if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
 | 
        
           |  |  | 2497 |   | 
        
           |  |  | 2498 |                 $nonpopuplink = $rating->get_view_ratings_url();
 | 
        
           |  |  | 2499 |                 $popuplink = $rating->get_view_ratings_url(true);
 | 
        
           |  |  | 2500 |   | 
        
           |  |  | 2501 |                 $action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
 | 
        
           |  |  | 2502 |                 $aggregatehtml = $this->action_link($nonpopuplink, $aggregatehtml, $action);
 | 
        
           |  |  | 2503 |             }
 | 
        
           |  |  | 2504 |   | 
        
           |  |  | 2505 |             $ratinghtml .= html_writer::tag('span', $aggregatelabel . $aggregatehtml, array('class' => 'rating-aggregate-container'));
 | 
        
           |  |  | 2506 |         }
 | 
        
           |  |  | 2507 |   | 
        
           |  |  | 2508 |         $formstart = null;
 | 
        
           |  |  | 2509 |         // if the item doesn't belong to the current user, the user has permission to rate
 | 
        
           |  |  | 2510 |         // and we're within the assessable period
 | 
        
           |  |  | 2511 |         if ($rating->user_can_rate()) {
 | 
        
           |  |  | 2512 |   | 
        
           |  |  | 2513 |             $rateurl = $rating->get_rate_url();
 | 
        
           |  |  | 2514 |             $inputs = $rateurl->params();
 | 
        
           |  |  | 2515 |   | 
        
           |  |  | 2516 |             //start the rating form
 | 
        
           |  |  | 2517 |             $formattrs = array(
 | 
        
           |  |  | 2518 |                 'id'     => "postrating{$rating->itemid}",
 | 
        
           |  |  | 2519 |                 'class'  => 'postratingform',
 | 
        
           |  |  | 2520 |                 'method' => 'post',
 | 
        
           |  |  | 2521 |                 'action' => $rateurl->out_omit_querystring()
 | 
        
           |  |  | 2522 |             );
 | 
        
           |  |  | 2523 |             $formstart  = html_writer::start_tag('form', $formattrs);
 | 
        
           |  |  | 2524 |             $formstart .= html_writer::start_tag('div', array('class' => 'ratingform'));
 | 
        
           |  |  | 2525 |   | 
        
           |  |  | 2526 |             // add the hidden inputs
 | 
        
           |  |  | 2527 |             foreach ($inputs as $name => $value) {
 | 
        
           |  |  | 2528 |                 $attributes = array('type' => 'hidden', 'class' => 'ratinginput', 'name' => $name, 'value' => $value);
 | 
        
           |  |  | 2529 |                 $formstart .= html_writer::empty_tag('input', $attributes);
 | 
        
           |  |  | 2530 |             }
 | 
        
           |  |  | 2531 |   | 
        
           |  |  | 2532 |             if (empty($ratinghtml)) {
 | 
        
           |  |  | 2533 |                 $ratinghtml .= $strrate.': ';
 | 
        
           |  |  | 2534 |             }
 | 
        
           |  |  | 2535 |             $ratinghtml = $formstart.$ratinghtml;
 | 
        
           |  |  | 2536 |   | 
        
           |  |  | 2537 |             $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $rating->settings->scale->scaleitems;
 | 
        
           |  |  | 2538 |             $scaleattrs = array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid);
 | 
        
           |  |  | 2539 |             $ratinghtml .= html_writer::label($rating->rating, 'menurating'.$rating->itemid, false, array('class' => 'accesshide'));
 | 
        
           |  |  | 2540 |             $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, $scaleattrs);
 | 
        
           |  |  | 2541 |   | 
        
           |  |  | 2542 |             //output submit button
 | 
        
           |  |  | 2543 |             $ratinghtml .= html_writer::start_tag('span', array('class'=>"ratingsubmit"));
 | 
        
           |  |  | 2544 |   | 
        
           |  |  | 2545 |             $attributes = array('type' => 'submit', 'class' => 'postratingmenusubmit', 'id' => 'postratingsubmit'.$rating->itemid, 'value' => s(get_string('rate', 'rating')));
 | 
        
           |  |  | 2546 |             $ratinghtml .= html_writer::empty_tag('input', $attributes);
 | 
        
           |  |  | 2547 |   | 
        
           |  |  | 2548 |             if (!$rating->settings->scale->isnumeric) {
 | 
        
           |  |  | 2549 |                 // If a global scale, try to find current course ID from the context
 | 
        
           |  |  | 2550 |                 if (empty($rating->settings->scale->courseid) and $coursecontext = $rating->context->get_course_context(false)) {
 | 
        
           |  |  | 2551 |                     $courseid = $coursecontext->instanceid;
 | 
        
           |  |  | 2552 |                 } else {
 | 
        
           |  |  | 2553 |                     $courseid = $rating->settings->scale->courseid;
 | 
        
           |  |  | 2554 |                 }
 | 
        
           |  |  | 2555 |                 $ratinghtml .= $this->help_icon_scale($courseid, $rating->settings->scale);
 | 
        
           |  |  | 2556 |             }
 | 
        
           |  |  | 2557 |             $ratinghtml .= html_writer::end_tag('span');
 | 
        
           |  |  | 2558 |             $ratinghtml .= html_writer::end_tag('div');
 | 
        
           |  |  | 2559 |             $ratinghtml .= html_writer::end_tag('form');
 | 
        
           |  |  | 2560 |         }
 | 
        
           |  |  | 2561 |   | 
        
           |  |  | 2562 |         return $ratinghtml;
 | 
        
           |  |  | 2563 |     }
 | 
        
           |  |  | 2564 |   | 
        
           |  |  | 2565 |     /**
 | 
        
           |  |  | 2566 |      * Centered heading with attached help button (same title text)
 | 
        
           |  |  | 2567 |      * and optional icon attached.
 | 
        
           |  |  | 2568 |      *
 | 
        
           |  |  | 2569 |      * @param string $text A heading text
 | 
        
           |  |  | 2570 |      * @param string $helpidentifier The keyword that defines a help page
 | 
        
           |  |  | 2571 |      * @param string $component component name
 | 
        
           |  |  | 2572 |      * @param string|moodle_url $icon
 | 
        
           |  |  | 2573 |      * @param string $iconalt icon alt text
 | 
        
           |  |  | 2574 |      * @param int $level The level of importance of the heading. Defaulting to 2
 | 
        
           |  |  | 2575 |      * @param string $classnames A space-separated list of CSS classes. Defaulting to null
 | 
        
           |  |  | 2576 |      * @return string HTML fragment
 | 
        
           |  |  | 2577 |      */
 | 
        
           |  |  | 2578 |     public function heading_with_help($text, $helpidentifier, $component = 'moodle', $icon = '', $iconalt = '', $level = 2, $classnames = null) {
 | 
        
           |  |  | 2579 |         $image = '';
 | 
        
           |  |  | 2580 |         if ($icon) {
 | 
        
           |  |  | 2581 |             $image = $this->pix_icon($icon, $iconalt, $component, array('class'=>'icon iconlarge'));
 | 
        
           |  |  | 2582 |         }
 | 
        
           |  |  | 2583 |   | 
        
           |  |  | 2584 |         $help = '';
 | 
        
           |  |  | 2585 |         if ($helpidentifier) {
 | 
        
           |  |  | 2586 |             $help = $this->help_icon($helpidentifier, $component);
 | 
        
           |  |  | 2587 |         }
 | 
        
           |  |  | 2588 |   | 
        
           |  |  | 2589 |         return $this->heading($image.$text.$help, $level, $classnames);
 | 
        
           |  |  | 2590 |     }
 | 
        
           |  |  | 2591 |   | 
        
           |  |  | 2592 |     /**
 | 
        
           |  |  | 2593 |      * Returns HTML to display a help icon.
 | 
        
           |  |  | 2594 |      *
 | 
        
           |  |  | 2595 |      * @deprecated since Moodle 2.0
 | 
        
           |  |  | 2596 |      */
 | 
        
           |  |  | 2597 |     public function old_help_icon($helpidentifier, $title, $component = 'moodle', $linktext = '') {
 | 
        
           |  |  | 2598 |         throw new coding_exception('old_help_icon() can not be used any more, please see help_icon().');
 | 
        
           |  |  | 2599 |     }
 | 
        
           |  |  | 2600 |   | 
        
           |  |  | 2601 |     /**
 | 
        
           |  |  | 2602 |      * Returns HTML to display a help icon.
 | 
        
           |  |  | 2603 |      *
 | 
        
           |  |  | 2604 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2605 |      * {@link core_renderer::render_help_icon()} instead.
 | 
        
           |  |  | 2606 |      *
 | 
        
           |  |  | 2607 |      * @param string $identifier The keyword that defines a help page
 | 
        
           |  |  | 2608 |      * @param string $component component name
 | 
        
           |  |  | 2609 |      * @param string|bool $linktext true means use $title as link text, string means link text value
 | 
        
           |  |  | 2610 |      * @param string|object|array|int $a An object, string or number that can be used
 | 
        
           |  |  | 2611 |      *      within translation strings
 | 
        
           |  |  | 2612 |      * @return string HTML fragment
 | 
        
           |  |  | 2613 |      */
 | 
        
           |  |  | 2614 |     public function help_icon($identifier, $component = 'moodle', $linktext = '', $a = null) {
 | 
        
           |  |  | 2615 |         $icon = new help_icon($identifier, $component, $a);
 | 
        
           |  |  | 2616 |         $icon->diag_strings();
 | 
        
           |  |  | 2617 |         if ($linktext === true) {
 | 
        
           |  |  | 2618 |             $icon->linktext = get_string($icon->identifier, $icon->component, $a);
 | 
        
           |  |  | 2619 |         } else if (!empty($linktext)) {
 | 
        
           |  |  | 2620 |             $icon->linktext = $linktext;
 | 
        
           |  |  | 2621 |         }
 | 
        
           |  |  | 2622 |         return $this->render($icon);
 | 
        
           |  |  | 2623 |     }
 | 
        
           |  |  | 2624 |   | 
        
           |  |  | 2625 |     /**
 | 
        
           |  |  | 2626 |      * Implementation of user image rendering.
 | 
        
           |  |  | 2627 |      *
 | 
        
           |  |  | 2628 |      * @param help_icon $helpicon A help icon instance
 | 
        
           |  |  | 2629 |      * @return string HTML fragment
 | 
        
           |  |  | 2630 |      */
 | 
        
           |  |  | 2631 |     protected function render_help_icon(help_icon $helpicon) {
 | 
        
           |  |  | 2632 |         $context = $helpicon->export_for_template($this);
 | 
        
           |  |  | 2633 |         return $this->render_from_template('core/help_icon', $context);
 | 
        
           |  |  | 2634 |     }
 | 
        
           |  |  | 2635 |   | 
        
           |  |  | 2636 |     /**
 | 
        
           |  |  | 2637 |      * Returns HTML to display a scale help icon.
 | 
        
           |  |  | 2638 |      *
 | 
        
           |  |  | 2639 |      * @param int $courseid
 | 
        
           |  |  | 2640 |      * @param stdClass $scale instance
 | 
        
           |  |  | 2641 |      * @return string HTML fragment
 | 
        
           |  |  | 2642 |      */
 | 
        
           |  |  | 2643 |     public function help_icon_scale($courseid, stdClass $scale) {
 | 
        
           |  |  | 2644 |         global $CFG;
 | 
        
           |  |  | 2645 |   | 
        
           |  |  | 2646 |         $title = get_string('helpprefix2', '', $scale->name) .' ('.get_string('newwindow').')';
 | 
        
           |  |  | 2647 |   | 
        
           |  |  | 2648 |         $icon = $this->pix_icon('help', get_string('scales'), 'moodle', array('class'=>'iconhelp'));
 | 
        
           |  |  | 2649 |   | 
        
           |  |  | 2650 |         $scaleid = abs($scale->id);
 | 
        
           |  |  | 2651 |   | 
        
           |  |  | 2652 |         $link = new moodle_url('/course/scales.php', array('id' => $courseid, 'list' => true, 'scaleid' => $scaleid));
 | 
        
           |  |  | 2653 |         $action = new popup_action('click', $link, 'ratingscale');
 | 
        
           |  |  | 2654 |   | 
        
           |  |  | 2655 |         return html_writer::tag('span', $this->action_link($link, $icon, $action), array('class' => 'helplink'));
 | 
        
           |  |  | 2656 |     }
 | 
        
           |  |  | 2657 |   | 
        
           |  |  | 2658 |     /**
 | 
        
           |  |  | 2659 |      * Creates and returns a spacer image with optional line break.
 | 
        
           |  |  | 2660 |      *
 | 
        
           |  |  | 2661 |      * @param array $attributes Any HTML attributes to add to the spaced.
 | 
        
           |  |  | 2662 |      * @param bool $br Include a BR after the spacer.... DON'T USE THIS. Don't be
 | 
        
           |  |  | 2663 |      *     laxy do it with CSS which is a much better solution.
 | 
        
           |  |  | 2664 |      * @return string HTML fragment
 | 
        
           |  |  | 2665 |      */
 | 
        
           |  |  | 2666 |     public function spacer(array $attributes = null, $br = false) {
 | 
        
           |  |  | 2667 |         $attributes = (array)$attributes;
 | 
        
           |  |  | 2668 |         if (empty($attributes['width'])) {
 | 
        
           |  |  | 2669 |             $attributes['width'] = 1;
 | 
        
           |  |  | 2670 |         }
 | 
        
           |  |  | 2671 |         if (empty($attributes['height'])) {
 | 
        
           |  |  | 2672 |             $attributes['height'] = 1;
 | 
        
           |  |  | 2673 |         }
 | 
        
           |  |  | 2674 |         $attributes['class'] = 'spacer';
 | 
        
           |  |  | 2675 |   | 
        
           |  |  | 2676 |         $output = $this->pix_icon('spacer', '', 'moodle', $attributes);
 | 
        
           |  |  | 2677 |   | 
        
           |  |  | 2678 |         if (!empty($br)) {
 | 
        
           |  |  | 2679 |             $output .= '<br />';
 | 
        
           |  |  | 2680 |         }
 | 
        
           |  |  | 2681 |   | 
        
           |  |  | 2682 |         return $output;
 | 
        
           |  |  | 2683 |     }
 | 
        
           |  |  | 2684 |   | 
        
           |  |  | 2685 |     /**
 | 
        
           |  |  | 2686 |      * Returns HTML to display the specified user's avatar.
 | 
        
           |  |  | 2687 |      *
 | 
        
           |  |  | 2688 |      * User avatar may be obtained in two ways:
 | 
        
           |  |  | 2689 |      * <pre>
 | 
        
           |  |  | 2690 |      * // Option 1: (shortcut for simple cases, preferred way)
 | 
        
           |  |  | 2691 |      * // $user has come from the DB and has fields id, picture, imagealt, firstname and lastname
 | 
        
           |  |  | 2692 |      * $OUTPUT->user_picture($user, array('popup'=>true));
 | 
        
           |  |  | 2693 |      *
 | 
        
           |  |  | 2694 |      * // Option 2:
 | 
        
           |  |  | 2695 |      * $userpic = new user_picture($user);
 | 
        
           |  |  | 2696 |      * // Set properties of $userpic
 | 
        
           |  |  | 2697 |      * $userpic->popup = true;
 | 
        
           |  |  | 2698 |      * $OUTPUT->render($userpic);
 | 
        
           |  |  | 2699 |      * </pre>
 | 
        
           |  |  | 2700 |      *
 | 
        
           |  |  | 2701 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2702 |      * {@link core_renderer::render_user_picture()} instead.
 | 
        
           |  |  | 2703 |      *
 | 
        
           |  |  | 2704 |      * @param stdClass $user Object with at least fields id, picture, imagealt, firstname, lastname
 | 
        
           |  |  | 2705 |      *     If any of these are missing, the database is queried. Avoid this
 | 
        
           |  |  | 2706 |      *     if at all possible, particularly for reports. It is very bad for performance.
 | 
        
           |  |  | 2707 |      * @param array $options associative array with user picture options, used only if not a user_picture object,
 | 
        
           |  |  | 2708 |      *     options are:
 | 
        
           |  |  | 2709 |      *     - courseid=$this->page->course->id (course id of user profile in link)
 | 
        
           |  |  | 2710 |      *     - size=35 (size of image)
 | 
        
           |  |  | 2711 |      *     - link=true (make image clickable - the link leads to user profile)
 | 
        
           |  |  | 2712 |      *     - popup=false (open in popup)
 | 
        
           |  |  | 2713 |      *     - alttext=true (add image alt attribute)
 | 
        
           |  |  | 2714 |      *     - class = image class attribute (default 'userpicture')
 | 
        
           |  |  | 2715 |      *     - visibletoscreenreaders=true (whether to be visible to screen readers)
 | 
        
           |  |  | 2716 |      *     - includefullname=false (whether to include the user's full name together with the user picture)
 | 
        
           |  |  | 2717 |      *     - includetoken = false (whether to use a token for authentication. True for current user, int value for other user id)
 | 
        
           |  |  | 2718 |      * @return string HTML fragment
 | 
        
           |  |  | 2719 |      */
 | 
        
           |  |  | 2720 |     public function user_picture(stdClass $user, array $options = null) {
 | 
        
           |  |  | 2721 |         $userpicture = new user_picture($user);
 | 
        
           |  |  | 2722 |         foreach ((array)$options as $key=>$value) {
 | 
        
           |  |  | 2723 |             if (property_exists($userpicture, $key)) {
 | 
        
           |  |  | 2724 |                 $userpicture->$key = $value;
 | 
        
           |  |  | 2725 |             }
 | 
        
           |  |  | 2726 |         }
 | 
        
           |  |  | 2727 |         return $this->render($userpicture);
 | 
        
           |  |  | 2728 |     }
 | 
        
           |  |  | 2729 |   | 
        
           |  |  | 2730 |     /**
 | 
        
           |  |  | 2731 |      * Internal implementation of user image rendering.
 | 
        
           |  |  | 2732 |      *
 | 
        
           |  |  | 2733 |      * @param user_picture $userpicture
 | 
        
           |  |  | 2734 |      * @return string
 | 
        
           |  |  | 2735 |      */
 | 
        
           |  |  | 2736 |     protected function render_user_picture(user_picture $userpicture) {
 | 
        
           |  |  | 2737 |         global $CFG;
 | 
        
           |  |  | 2738 |   | 
        
           |  |  | 2739 |         $user = $userpicture->user;
 | 
        
           |  |  | 2740 |         $canviewfullnames = has_capability('moodle/site:viewfullnames', $this->page->context);
 | 
        
           |  |  | 2741 |   | 
        
           |  |  | 2742 |         $alt = '';
 | 
        
           |  |  | 2743 |         if ($userpicture->alttext) {
 | 
        
           |  |  | 2744 |             if (!empty($user->imagealt)) {
 | 
        
           |  |  | 2745 |                 $alt = trim($user->imagealt);
 | 
        
           |  |  | 2746 |             }
 | 
        
           |  |  | 2747 |         }
 | 
        
           |  |  | 2748 |   | 
        
           |  |  | 2749 |         // If the user picture is being rendered as a link but without the full name, an empty alt text for the user picture
 | 
        
           |  |  | 2750 |         // would mean that the link displayed will not have any discernible text. This becomes an accessibility issue,
 | 
        
           |  |  | 2751 |         // especially to screen reader users. Use the user's full name by default for the user picture's alt-text if this is
 | 
        
           |  |  | 2752 |         // the case.
 | 
        
           |  |  | 2753 |         if ($userpicture->link && !$userpicture->includefullname && empty($alt)) {
 | 
        
           |  |  | 2754 |             $alt = fullname($user);
 | 
        
           |  |  | 2755 |         }
 | 
        
           |  |  | 2756 |   | 
        
           |  |  | 2757 |         if (empty($userpicture->size)) {
 | 
        
           |  |  | 2758 |             $size = 35;
 | 
        
           |  |  | 2759 |         } else if ($userpicture->size === true or $userpicture->size == 1) {
 | 
        
           |  |  | 2760 |             $size = 100;
 | 
        
           |  |  | 2761 |         } else {
 | 
        
           |  |  | 2762 |             $size = $userpicture->size;
 | 
        
           |  |  | 2763 |         }
 | 
        
           |  |  | 2764 |   | 
        
           |  |  | 2765 |         $class = $userpicture->class;
 | 
        
           |  |  | 2766 |   | 
        
           |  |  | 2767 |         if ($user->picture == 0) {
 | 
        
           |  |  | 2768 |             $class .= ' defaultuserpic';
 | 
        
           |  |  | 2769 |         }
 | 
        
           |  |  | 2770 |   | 
        
           |  |  | 2771 |         $src = $userpicture->get_url($this->page, $this);
 | 
        
           |  |  | 2772 |   | 
        
           |  |  | 2773 |         $attributes = array('src' => $src, 'class' => $class, 'width' => $size, 'height' => $size);
 | 
        
           |  |  | 2774 |         if (!$userpicture->visibletoscreenreaders) {
 | 
        
           |  |  | 2775 |             $alt = '';
 | 
        
           |  |  | 2776 |         }
 | 
        
           |  |  | 2777 |         $attributes['alt'] = $alt;
 | 
        
           |  |  | 2778 |   | 
        
           |  |  | 2779 |         if (!empty($alt)) {
 | 
        
           |  |  | 2780 |             $attributes['title'] = $alt;
 | 
        
           |  |  | 2781 |         }
 | 
        
           |  |  | 2782 |   | 
        
           |  |  | 2783 |         // Get the image html output first, auto generated based on initials if one isn't already set.
 | 
        
           |  |  | 2784 |         if ($user->picture == 0 && empty($CFG->enablegravatar) && !defined('BEHAT_SITE_RUNNING')) {
 | 
        
           |  |  | 2785 |             $initials = \core_user::get_initials($user);
 | 
        
           |  |  | 2786 |             $fullname = fullname($userpicture->user, $canviewfullnames);
 | 
        
           |  |  | 2787 |             // Don't modify in corner cases where neither the firstname nor the lastname appears.
 | 
        
           |  |  | 2788 |             $output = html_writer::tag(
 | 
        
           |  |  | 2789 |                 'span', $initials,
 | 
        
           |  |  | 2790 |                 [
 | 
        
           |  |  | 2791 |                     'class' => 'userinitials size-' . $size,
 | 
        
           |  |  | 2792 |                     'title' => $fullname,
 | 
        
           |  |  | 2793 |                     'aria-label' => $fullname,
 | 
        
           |  |  | 2794 |                     'role' => 'img',
 | 
        
           |  |  | 2795 |                 ]
 | 
        
           |  |  | 2796 |             );
 | 
        
           |  |  | 2797 |         } else {
 | 
        
           |  |  | 2798 |             $output = html_writer::empty_tag('img', $attributes);
 | 
        
           |  |  | 2799 |         }
 | 
        
           |  |  | 2800 |   | 
        
           |  |  | 2801 |         // Show fullname together with the picture when desired.
 | 
        
           |  |  | 2802 |         if ($userpicture->includefullname) {
 | 
        
           |  |  | 2803 |             $output .= fullname($userpicture->user, $canviewfullnames);
 | 
        
           |  |  | 2804 |         }
 | 
        
           |  |  | 2805 |   | 
        
           |  |  | 2806 |         if (empty($userpicture->courseid)) {
 | 
        
           |  |  | 2807 |             $courseid = $this->page->course->id;
 | 
        
           |  |  | 2808 |         } else {
 | 
        
           |  |  | 2809 |             $courseid = $userpicture->courseid;
 | 
        
           |  |  | 2810 |         }
 | 
        
           |  |  | 2811 |         if ($courseid == SITEID) {
 | 
        
           |  |  | 2812 |             $url = new moodle_url('/user/profile.php', array('id' => $user->id));
 | 
        
           |  |  | 2813 |         } else {
 | 
        
           |  |  | 2814 |             $url = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $courseid));
 | 
        
           |  |  | 2815 |         }
 | 
        
           |  |  | 2816 |   | 
        
           |  |  | 2817 |         // Then wrap it in link if needed. Also we don't wrap it in link if the link redirects to itself.
 | 
        
           |  |  | 2818 |         if (!$userpicture->link ||
 | 
        
           |  |  | 2819 |                 ($this->page->has_set_url() && $this->page->url == $url)) { // Protect against unset page->url.
 | 
        
           |  |  | 2820 |             return $output;
 | 
        
           |  |  | 2821 |         }
 | 
        
           |  |  | 2822 |   | 
        
           |  |  | 2823 |         $attributes = array('href' => $url, 'class' => 'd-inline-block aabtn');
 | 
        
           |  |  | 2824 |         if (!$userpicture->visibletoscreenreaders) {
 | 
        
           |  |  | 2825 |             $attributes['tabindex'] = '-1';
 | 
        
           |  |  | 2826 |             $attributes['aria-hidden'] = 'true';
 | 
        
           |  |  | 2827 |         }
 | 
        
           |  |  | 2828 |   | 
        
           |  |  | 2829 |         if ($userpicture->popup) {
 | 
        
           |  |  | 2830 |             $id = html_writer::random_id('userpicture');
 | 
        
           |  |  | 2831 |             $attributes['id'] = $id;
 | 
        
           |  |  | 2832 |             $this->add_action_handler(new popup_action('click', $url), $id);
 | 
        
           |  |  | 2833 |         }
 | 
        
           |  |  | 2834 |   | 
        
           |  |  | 2835 |         return html_writer::tag('a', $output, $attributes);
 | 
        
           |  |  | 2836 |     }
 | 
        
           |  |  | 2837 |   | 
        
           |  |  | 2838 |     /**
 | 
        
           |  |  | 2839 |      * @deprecated since Moodle 4.3
 | 
        
           |  |  | 2840 |      */
 | 
        
           |  |  | 2841 |     public function htmllize_file_tree() {
 | 
        
           |  |  | 2842 |         throw new coding_exception('This function is deprecated and no longer relevant.');
 | 
        
           |  |  | 2843 |     }
 | 
        
           |  |  | 2844 |   | 
        
           |  |  | 2845 |     /**
 | 
        
           |  |  | 2846 |      * Returns HTML to display the file picker
 | 
        
           |  |  | 2847 |      *
 | 
        
           |  |  | 2848 |      * <pre>
 | 
        
           |  |  | 2849 |      * $OUTPUT->file_picker($options);
 | 
        
           |  |  | 2850 |      * </pre>
 | 
        
           |  |  | 2851 |      *
 | 
        
           |  |  | 2852 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 2853 |      * {@link core_renderer::render_file_picker()} instead.
 | 
        
           |  |  | 2854 |      *
 | 
        
           |  |  | 2855 |      * @param stdClass $options file manager options
 | 
        
           |  |  | 2856 |      *   options are:
 | 
        
           |  |  | 2857 |      *       maxbytes=>-1,
 | 
        
           |  |  | 2858 |      *       itemid=>0,
 | 
        
           |  |  | 2859 |      *       client_id=>uniqid(),
 | 
        
           |  |  | 2860 |      *       acepted_types=>'*',
 | 
        
           |  |  | 2861 |      *       return_types=>FILE_INTERNAL,
 | 
        
           |  |  | 2862 |      *       context=>current page context
 | 
        
           |  |  | 2863 |      * @return string HTML fragment
 | 
        
           |  |  | 2864 |      */
 | 
        
           |  |  | 2865 |     public function file_picker($options) {
 | 
        
           |  |  | 2866 |         $fp = new file_picker($options);
 | 
        
           |  |  | 2867 |         return $this->render($fp);
 | 
        
           |  |  | 2868 |     }
 | 
        
           |  |  | 2869 |   | 
        
           |  |  | 2870 |     /**
 | 
        
           |  |  | 2871 |      * Internal implementation of file picker rendering.
 | 
        
           |  |  | 2872 |      *
 | 
        
           |  |  | 2873 |      * @param file_picker $fp
 | 
        
           |  |  | 2874 |      * @return string
 | 
        
           |  |  | 2875 |      */
 | 
        
           |  |  | 2876 |     public function render_file_picker(file_picker $fp) {
 | 
        
           |  |  | 2877 |         $options = $fp->options;
 | 
        
           |  |  | 2878 |         $client_id = $options->client_id;
 | 
        
           |  |  | 2879 |         $strsaved = get_string('filesaved', 'repository');
 | 
        
           |  |  | 2880 |         $straddfile = get_string('openpicker', 'repository');
 | 
        
           |  |  | 2881 |         $strloading  = get_string('loading', 'repository');
 | 
        
           |  |  | 2882 |         $strdndenabled = get_string('dndenabled_inbox', 'moodle');
 | 
        
           |  |  | 2883 |         $strdroptoupload = get_string('droptoupload', 'moodle');
 | 
        
           |  |  | 2884 |         $iconprogress = $this->pix_icon('i/loading_small', $strloading).'';
 | 
        
           |  |  | 2885 |   | 
        
           |  |  | 2886 |         $currentfile = $options->currentfile;
 | 
        
           |  |  | 2887 |         if (empty($currentfile)) {
 | 
        
           |  |  | 2888 |             $currentfile = '';
 | 
        
           |  |  | 2889 |         } else {
 | 
        
           |  |  | 2890 |             $currentfile .= ' - ';
 | 
        
           |  |  | 2891 |         }
 | 
        
           |  |  | 2892 |         if ($options->maxbytes) {
 | 
        
           |  |  | 2893 |             $size = $options->maxbytes;
 | 
        
           |  |  | 2894 |         } else {
 | 
        
           |  |  | 2895 |             $size = get_max_upload_file_size();
 | 
        
           |  |  | 2896 |         }
 | 
        
           |  |  | 2897 |         if ($size == -1) {
 | 
        
           |  |  | 2898 |             $maxsize = '';
 | 
        
           |  |  | 2899 |         } else {
 | 
        
           |  |  | 2900 |             $maxsize = get_string('maxfilesize', 'moodle', display_size($size, 0));
 | 
        
           |  |  | 2901 |         }
 | 
        
           |  |  | 2902 |         if ($options->buttonname) {
 | 
        
           |  |  | 2903 |             $buttonname = ' name="' . $options->buttonname . '"';
 | 
        
           |  |  | 2904 |         } else {
 | 
        
           |  |  | 2905 |             $buttonname = '';
 | 
        
           |  |  | 2906 |         }
 | 
        
           |  |  | 2907 |         $html = <<<EOD
 | 
        
           |  |  | 2908 | <div class="filemanager-loading mdl-align" id='filepicker-loading-{$client_id}'>
 | 
        
           |  |  | 2909 | $iconprogress
 | 
        
           |  |  | 2910 | </div>
 | 
        
           |  |  | 2911 | <div id="filepicker-wrapper-{$client_id}" class="mdl-left w-100" style="display:none">
 | 
        
           |  |  | 2912 |     <div>
 | 
        
           |  |  | 2913 |         <input type="button" class="btn btn-secondary fp-btn-choose" id="filepicker-button-{$client_id}" value="{$straddfile}"{$buttonname}/>
 | 
        
           |  |  | 2914 |         <span> $maxsize </span>
 | 
        
           |  |  | 2915 |     </div>
 | 
        
           |  |  | 2916 | EOD;
 | 
        
           |  |  | 2917 |         if ($options->env != 'url') {
 | 
        
           |  |  | 2918 |             $html .= <<<EOD
 | 
        
           |  |  | 2919 |     <div id="file_info_{$client_id}" class="mdl-left filepicker-filelist" style="position: relative">
 | 
        
           |  |  | 2920 |     <div class="filepicker-filename">
 | 
        
           |  |  | 2921 |         <div class="filepicker-container">$currentfile
 | 
        
           |  |  | 2922 |             <div class="dndupload-message">$strdndenabled <br/>
 | 
        
           |  |  | 2923 |                 <div class="dndupload-arrow d-flex"><i class="fa fa-arrow-circle-o-down fa-3x m-auto"></i></div>
 | 
        
           |  |  | 2924 |             </div>
 | 
        
           |  |  | 2925 |         </div>
 | 
        
           |  |  | 2926 |         <div class="dndupload-progressbars"></div>
 | 
        
           |  |  | 2927 |     </div>
 | 
        
           |  |  | 2928 |     <div>
 | 
        
           |  |  | 2929 |         <div class="dndupload-target">{$strdroptoupload}<br/>
 | 
        
           |  |  | 2930 |             <div class="dndupload-arrow d-flex"><i class="fa fa-arrow-circle-o-down fa-3x m-auto"></i></div>
 | 
        
           |  |  | 2931 |         </div>
 | 
        
           |  |  | 2932 |     </div>
 | 
        
           |  |  | 2933 |     </div>
 | 
        
           |  |  | 2934 | EOD;
 | 
        
           |  |  | 2935 |         }
 | 
        
           |  |  | 2936 |         $html .= '</div>';
 | 
        
           |  |  | 2937 |         return $html;
 | 
        
           |  |  | 2938 |     }
 | 
        
           |  |  | 2939 |   | 
        
           |  |  | 2940 |     /**
 | 
        
           |  |  | 2941 |      * @deprecated since Moodle 3.2
 | 
        
           |  |  | 2942 |      */
 | 
        
           |  |  | 2943 |     public function update_module_button() {
 | 
        
           |  |  | 2944 |         throw new coding_exception('core_renderer::update_module_button() can not be used anymore. Activity ' .
 | 
        
           |  |  | 2945 |             'modules should not add the edit module button, the link is already available in the Administration block. ' .
 | 
        
           |  |  | 2946 |             'Themes can choose to display the link in the buttons row consistently for all module types.');
 | 
        
           |  |  | 2947 |     }
 | 
        
           |  |  | 2948 |   | 
        
           |  |  | 2949 |     /**
 | 
        
           |  |  | 2950 |      * Returns HTML to display a "Turn editing on/off" button in a form.
 | 
        
           |  |  | 2951 |      *
 | 
        
           |  |  | 2952 |      * @param moodle_url $url The URL + params to send through when clicking the button
 | 
        
           |  |  | 2953 |      * @param string $method
 | 
        
           |  |  | 2954 |      * @return ?string HTML the button
 | 
        
           |  |  | 2955 |      */
 | 
        
           |  |  | 2956 |     public function edit_button(moodle_url $url, string $method = 'post') {
 | 
        
           |  |  | 2957 |   | 
        
           |  |  | 2958 |         if ($this->page->theme->haseditswitch == true) {
 | 
        
           |  |  | 2959 |             return;
 | 
        
           |  |  | 2960 |         }
 | 
        
           |  |  | 2961 |         $url->param('sesskey', sesskey());
 | 
        
           |  |  | 2962 |         if ($this->page->user_is_editing()) {
 | 
        
           |  |  | 2963 |             $url->param('edit', 'off');
 | 
        
           |  |  | 2964 |             $editstring = get_string('turneditingoff');
 | 
        
           |  |  | 2965 |         } else {
 | 
        
           |  |  | 2966 |             $url->param('edit', 'on');
 | 
        
           |  |  | 2967 |             $editstring = get_string('turneditingon');
 | 
        
           |  |  | 2968 |         }
 | 
        
           |  |  | 2969 |   | 
        
           |  |  | 2970 |         return $this->single_button($url, $editstring, $method);
 | 
        
           |  |  | 2971 |     }
 | 
        
           |  |  | 2972 |   | 
        
           |  |  | 2973 |     /**
 | 
        
           |  |  | 2974 |      * Create a navbar switch for toggling editing mode.
 | 
        
           |  |  | 2975 |      *
 | 
        
           |  |  | 2976 |      * @return ?string Html containing the edit switch
 | 
        
           |  |  | 2977 |      */
 | 
        
           |  |  | 2978 |     public function edit_switch() {
 | 
        
           |  |  | 2979 |         if ($this->page->user_allowed_editing()) {
 | 
        
           |  |  | 2980 |   | 
        
           |  |  | 2981 |             $temp = (object) [
 | 
        
           |  |  | 2982 |                 'legacyseturl' => (new moodle_url('/editmode.php'))->out(false),
 | 
        
           |  |  | 2983 |                 'pagecontextid' => $this->page->context->id,
 | 
        
           |  |  | 2984 |                 'pageurl' => $this->page->url,
 | 
        
           |  |  | 2985 |                 'sesskey' => sesskey(),
 | 
        
           |  |  | 2986 |             ];
 | 
        
           |  |  | 2987 |             if ($this->page->user_is_editing()) {
 | 
        
           |  |  | 2988 |                 $temp->checked = true;
 | 
        
           |  |  | 2989 |             }
 | 
        
           |  |  | 2990 |             return $this->render_from_template('core/editswitch', $temp);
 | 
        
           |  |  | 2991 |         }
 | 
        
           |  |  | 2992 |     }
 | 
        
           |  |  | 2993 |   | 
        
           |  |  | 2994 |     /**
 | 
        
           |  |  | 2995 |      * Returns HTML to display a simple button to close a window
 | 
        
           |  |  | 2996 |      *
 | 
        
           |  |  | 2997 |      * @param string $text The lang string for the button's label (already output from get_string())
 | 
        
           |  |  | 2998 |      * @return string html fragment
 | 
        
           |  |  | 2999 |      */
 | 
        
           |  |  | 3000 |     public function close_window_button($text='') {
 | 
        
           |  |  | 3001 |         if (empty($text)) {
 | 
        
           |  |  | 3002 |             $text = get_string('closewindow');
 | 
        
           |  |  | 3003 |         }
 | 
        
           |  |  | 3004 |         $button = new single_button(new moodle_url('#'), $text, 'get');
 | 
        
           |  |  | 3005 |         $button->add_action(new component_action('click', 'close_window'));
 | 
        
           |  |  | 3006 |   | 
        
           |  |  | 3007 |         return $this->container($this->render($button), 'closewindow');
 | 
        
           |  |  | 3008 |     }
 | 
        
           |  |  | 3009 |   | 
        
           |  |  | 3010 |     /**
 | 
        
           |  |  | 3011 |      * Output an error message. By default wraps the error message in <span class="error">.
 | 
        
           |  |  | 3012 |      * If the error message is blank, nothing is output.
 | 
        
           |  |  | 3013 |      *
 | 
        
           |  |  | 3014 |      * @param string $message the error message.
 | 
        
           |  |  | 3015 |      * @return string the HTML to output.
 | 
        
           |  |  | 3016 |      */
 | 
        
           |  |  | 3017 |     public function error_text($message) {
 | 
        
           |  |  | 3018 |         if (empty($message)) {
 | 
        
           |  |  | 3019 |             return '';
 | 
        
           |  |  | 3020 |         }
 | 
        
           |  |  | 3021 |         $message = $this->pix_icon('i/warning', get_string('error'), '', array('class' => 'icon icon-pre', 'title'=>'')) . $message;
 | 
        
           |  |  | 3022 |         return html_writer::tag('span', $message, array('class' => 'error'));
 | 
        
           |  |  | 3023 |     }
 | 
        
           |  |  | 3024 |   | 
        
           |  |  | 3025 |     /**
 | 
        
           |  |  | 3026 |      * Do not call this function directly.
 | 
        
           |  |  | 3027 |      *
 | 
        
           |  |  | 3028 |      * To terminate the current script with a fatal error, throw an exception.
 | 
        
           |  |  | 3029 |      * Doing this will then call this function to display the error, before terminating the execution.
 | 
        
           |  |  | 3030 |      *
 | 
        
           |  |  | 3031 |      * @param string $message The message to output
 | 
        
           |  |  | 3032 |      * @param string $moreinfourl URL where more info can be found about the error
 | 
        
           |  |  | 3033 |      * @param string $link Link for the Continue button
 | 
        
           |  |  | 3034 |      * @param array $backtrace The execution backtrace
 | 
        
           |  |  | 3035 |      * @param string $debuginfo Debugging information
 | 
        
           |  |  | 3036 |      * @return string the HTML to output.
 | 
        
           |  |  | 3037 |      */
 | 
        
           |  |  | 3038 |     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = "") {
 | 
        
           |  |  | 3039 |         global $CFG;
 | 
        
           |  |  | 3040 |   | 
        
           |  |  | 3041 |         $output = '';
 | 
        
           |  |  | 3042 |         $obbuffer = '';
 | 
        
           |  |  | 3043 |   | 
        
           |  |  | 3044 |         if ($this->has_started()) {
 | 
        
           |  |  | 3045 |             // we can not always recover properly here, we have problems with output buffering,
 | 
        
           |  |  | 3046 |             // html tables, etc.
 | 
        
           |  |  | 3047 |             $output .= $this->opencontainers->pop_all_but_last();
 | 
        
           |  |  | 3048 |   | 
        
           |  |  | 3049 |         } else {
 | 
        
           |  |  | 3050 |             // It is really bad if library code throws exception when output buffering is on,
 | 
        
           |  |  | 3051 |             // because the buffered text would be printed before our start of page.
 | 
        
           |  |  | 3052 |             // NOTE: this hack might be behave unexpectedly in case output buffering is enabled in PHP.ini
 | 
        
           |  |  | 3053 |             error_reporting(0); // disable notices from gzip compression, etc.
 | 
        
           |  |  | 3054 |             while (ob_get_level() > 0) {
 | 
        
           |  |  | 3055 |                 $buff = ob_get_clean();
 | 
        
           |  |  | 3056 |                 if ($buff === false) {
 | 
        
           |  |  | 3057 |                     break;
 | 
        
           |  |  | 3058 |                 }
 | 
        
           |  |  | 3059 |                 $obbuffer .= $buff;
 | 
        
           |  |  | 3060 |             }
 | 
        
           |  |  | 3061 |             error_reporting($CFG->debug);
 | 
        
           |  |  | 3062 |   | 
        
           |  |  | 3063 |             // Output not yet started.
 | 
        
           |  |  | 3064 |             $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
 | 
        
           |  |  | 3065 |             if (empty($_SERVER['HTTP_RANGE'])) {
 | 
        
           |  |  | 3066 |                 @header($protocol . ' 404 Not Found');
 | 
        
           |  |  | 3067 |             } else if (core_useragent::check_safari_ios_version(602) && !empty($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) {
 | 
        
           |  |  | 3068 |                 // Coax iOS 10 into sending the session cookie.
 | 
        
           |  |  | 3069 |                 @header($protocol . ' 403 Forbidden');
 | 
        
           |  |  | 3070 |             } else {
 | 
        
           |  |  | 3071 |                 // Must stop byteserving attempts somehow,
 | 
        
           |  |  | 3072 |                 // this is weird but Chrome PDF viewer can be stopped only with 407!
 | 
        
           |  |  | 3073 |                 @header($protocol . ' 407 Proxy Authentication Required');
 | 
        
           |  |  | 3074 |             }
 | 
        
           |  |  | 3075 |   | 
        
           |  |  | 3076 |             $this->page->set_context(null); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
 | 
        
           |  |  | 3077 |             $this->page->set_url('/'); // no url
 | 
        
           |  |  | 3078 |             //$this->page->set_pagelayout('base'); //TODO: MDL-20676 blocks on error pages are weird, unfortunately it somehow detect the pagelayout from URL :-(
 | 
        
           |  |  | 3079 |             $this->page->set_title(get_string('error'));
 | 
        
           |  |  | 3080 |             $this->page->set_heading($this->page->course->fullname);
 | 
        
           |  |  | 3081 |             // No need to display the activity header when encountering an error.
 | 
        
           |  |  | 3082 |             $this->page->activityheader->disable();
 | 
        
           |  |  | 3083 |             $output .= $this->header();
 | 
        
           |  |  | 3084 |         }
 | 
        
           |  |  | 3085 |   | 
        
           |  |  | 3086 |         $message = '<p class="errormessage">' . s($message) . '</p>'.
 | 
        
           |  |  | 3087 |                 '<p class="errorcode"><a href="' . s($moreinfourl) . '">' .
 | 
        
           |  |  | 3088 |                 get_string('moreinformation') . '</a></p>';
 | 
        
           |  |  | 3089 |         if (empty($CFG->rolesactive)) {
 | 
        
           |  |  | 3090 |             $message .= '<p class="errormessage">' . get_string('installproblem', 'error') . '</p>';
 | 
        
           |  |  | 3091 |             //It is usually not possible to recover from errors triggered during installation, you may need to create a new database or use a different database prefix for new installation.
 | 
        
           |  |  | 3092 |         }
 | 
        
           |  |  | 3093 |         $output .= $this->box($message, 'errorbox alert alert-danger', null, array('data-rel' => 'fatalerror'));
 | 
        
           |  |  | 3094 |   | 
        
           |  |  | 3095 |         if ($CFG->debugdeveloper) {
 | 
        
           |  |  | 3096 |             $labelsep = get_string('labelsep', 'langconfig');
 | 
        
           |  |  | 3097 |             if (!empty($debuginfo)) {
 | 
        
           |  |  | 3098 |                 $debuginfo = s($debuginfo); // removes all nasty JS
 | 
        
           |  |  | 3099 |                 $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines
 | 
        
           |  |  | 3100 |                 $label = get_string('debuginfo', 'debug') . $labelsep;
 | 
        
           |  |  | 3101 |                 $output .= $this->notification("<strong>$label</strong> " . $debuginfo, 'notifytiny');
 | 
        
           |  |  | 3102 |             }
 | 
        
           |  |  | 3103 |             if (!empty($backtrace)) {
 | 
        
           |  |  | 3104 |                 $label = get_string('stacktrace', 'debug') . $labelsep;
 | 
        
           |  |  | 3105 |                 $output .= $this->notification("<strong>$label</strong> " . format_backtrace($backtrace), 'notifytiny');
 | 
        
           |  |  | 3106 |             }
 | 
        
           |  |  | 3107 |             if ($obbuffer !== '' ) {
 | 
        
           |  |  | 3108 |                 $label = get_string('outputbuffer', 'debug') . $labelsep;
 | 
        
           |  |  | 3109 |                 $output .= $this->notification("<strong>$label</strong> " . s($obbuffer), 'notifytiny');
 | 
        
           |  |  | 3110 |             }
 | 
        
           |  |  | 3111 |         }
 | 
        
           |  |  | 3112 |   | 
        
           |  |  | 3113 |         if (empty($CFG->rolesactive)) {
 | 
        
           |  |  | 3114 |             // continue does not make much sense if moodle is not installed yet because error is most probably not recoverable
 | 
        
           |  |  | 3115 |         } else if (!empty($link)) {
 | 
        
           |  |  | 3116 |             $output .= $this->continue_button($link);
 | 
        
           |  |  | 3117 |         }
 | 
        
           |  |  | 3118 |   | 
        
           |  |  | 3119 |         $output .= $this->footer();
 | 
        
           |  |  | 3120 |   | 
        
           |  |  | 3121 |         // Padding to encourage IE to display our error page, rather than its own.
 | 
        
           |  |  | 3122 |         $output .= str_repeat(' ', 512);
 | 
        
           |  |  | 3123 |   | 
        
           |  |  | 3124 |         return $output;
 | 
        
           |  |  | 3125 |     }
 | 
        
           |  |  | 3126 |   | 
        
           |  |  | 3127 |     /**
 | 
        
           |  |  | 3128 |      * Output a notification (that is, a status message about something that has just happened).
 | 
        
           |  |  | 3129 |      *
 | 
        
           |  |  | 3130 |      * Note: \core\notification::add() may be more suitable for your usage.
 | 
        
           |  |  | 3131 |      *
 | 
        
           |  |  | 3132 |      * @param string $message The message to print out.
 | 
        
           |  |  | 3133 |      * @param ?string $type   The type of notification. See constants on \core\output\notification.
 | 
        
           |  |  | 3134 |      * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
 | 
        
           |  |  | 3135 |      * @return string the HTML to output.
 | 
        
           |  |  | 3136 |      */
 | 
        
           |  |  | 3137 |     public function notification($message, $type = null, $closebutton = true) {
 | 
        
           |  |  | 3138 |         $typemappings = [
 | 
        
           |  |  | 3139 |             // Valid types.
 | 
        
           |  |  | 3140 |             'success'           => \core\output\notification::NOTIFY_SUCCESS,
 | 
        
           |  |  | 3141 |             'info'              => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 3142 |             'warning'           => \core\output\notification::NOTIFY_WARNING,
 | 
        
           |  |  | 3143 |             'error'             => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 3144 |   | 
        
           |  |  | 3145 |             // Legacy types mapped to current types.
 | 
        
           |  |  | 3146 |             'notifyproblem'     => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 3147 |             'notifytiny'        => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 3148 |             'notifyerror'       => \core\output\notification::NOTIFY_ERROR,
 | 
        
           |  |  | 3149 |             'notifysuccess'     => \core\output\notification::NOTIFY_SUCCESS,
 | 
        
           |  |  | 3150 |             'notifymessage'     => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 3151 |             'notifyredirect'    => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 3152 |             'redirectmessage'   => \core\output\notification::NOTIFY_INFO,
 | 
        
           |  |  | 3153 |         ];
 | 
        
           |  |  | 3154 |   | 
        
           |  |  | 3155 |         $extraclasses = [];
 | 
        
           |  |  | 3156 |   | 
        
           |  |  | 3157 |         if ($type) {
 | 
        
           |  |  | 3158 |             if (strpos($type, ' ') === false) {
 | 
        
           |  |  | 3159 |                 // No spaces in the list of classes, therefore no need to loop over and determine the class.
 | 
        
           |  |  | 3160 |                 if (isset($typemappings[$type])) {
 | 
        
           |  |  | 3161 |                     $type = $typemappings[$type];
 | 
        
           |  |  | 3162 |                 } else {
 | 
        
           |  |  | 3163 |                     // The value provided did not match a known type. It must be an extra class.
 | 
        
           |  |  | 3164 |                     $extraclasses = [$type];
 | 
        
           |  |  | 3165 |                 }
 | 
        
           |  |  | 3166 |             } else {
 | 
        
           |  |  | 3167 |                 // Identify what type of notification this is.
 | 
        
           |  |  | 3168 |                 $classarray = explode(' ', self::prepare_classes($type));
 | 
        
           |  |  | 3169 |   | 
        
           |  |  | 3170 |                 // Separate out the type of notification from the extra classes.
 | 
        
           |  |  | 3171 |                 foreach ($classarray as $class) {
 | 
        
           |  |  | 3172 |                     if (isset($typemappings[$class])) {
 | 
        
           |  |  | 3173 |                         $type = $typemappings[$class];
 | 
        
           |  |  | 3174 |                     } else {
 | 
        
           |  |  | 3175 |                         $extraclasses[] = $class;
 | 
        
           |  |  | 3176 |                     }
 | 
        
           |  |  | 3177 |                 }
 | 
        
           |  |  | 3178 |             }
 | 
        
           |  |  | 3179 |         }
 | 
        
           |  |  | 3180 |   | 
        
           |  |  | 3181 |         $notification = new \core\output\notification($message, $type, $closebutton);
 | 
        
           |  |  | 3182 |         if (count($extraclasses)) {
 | 
        
           |  |  | 3183 |             $notification->set_extra_classes($extraclasses);
 | 
        
           |  |  | 3184 |         }
 | 
        
           |  |  | 3185 |   | 
        
           |  |  | 3186 |         // Return the rendered template.
 | 
        
           |  |  | 3187 |         return $this->render_from_template($notification->get_template_name(), $notification->export_for_template($this));
 | 
        
           |  |  | 3188 |     }
 | 
        
           |  |  | 3189 |   | 
        
           |  |  | 3190 |     /**
 | 
        
           |  |  | 3191 |      * @deprecated since Moodle 3.1 MDL-30811 - please do not use this function any more.
 | 
        
           |  |  | 3192 |      */
 | 
        
           |  |  | 3193 |     public function notify_problem() {
 | 
        
           |  |  | 3194 |         throw new coding_exception('core_renderer::notify_problem() can not be used any more, '.
 | 
        
           |  |  | 3195 |             'please use \core\notification::add(), or \core\output\notification as required.');
 | 
        
           |  |  | 3196 |     }
 | 
        
           |  |  | 3197 |   | 
        
           |  |  | 3198 |     /**
 | 
        
           |  |  | 3199 |      * @deprecated since Moodle 3.1 MDL-30811 - please do not use this function any more.
 | 
        
           |  |  | 3200 |      */
 | 
        
           |  |  | 3201 |     public function notify_success() {
 | 
        
           |  |  | 3202 |         throw new coding_exception('core_renderer::notify_success() can not be used any more, '.
 | 
        
           |  |  | 3203 |             'please use \core\notification::add(), or \core\output\notification as required.');
 | 
        
           |  |  | 3204 |     }
 | 
        
           |  |  | 3205 |   | 
        
           |  |  | 3206 |     /**
 | 
        
           |  |  | 3207 |      * @deprecated since Moodle 3.1 MDL-30811 - please do not use this function any more.
 | 
        
           |  |  | 3208 |      */
 | 
        
           |  |  | 3209 |     public function notify_message() {
 | 
        
           |  |  | 3210 |         throw new coding_exception('core_renderer::notify_message() can not be used any more, '.
 | 
        
           |  |  | 3211 |             'please use \core\notification::add(), or \core\output\notification as required.');
 | 
        
           |  |  | 3212 |     }
 | 
        
           |  |  | 3213 |   | 
        
           |  |  | 3214 |     /**
 | 
        
           |  |  | 3215 |      * @deprecated since Moodle 3.1 MDL-30811 - please do not use this function any more.
 | 
        
           |  |  | 3216 |      */
 | 
        
           |  |  | 3217 |     public function notify_redirect() {
 | 
        
           |  |  | 3218 |         throw new coding_exception('core_renderer::notify_redirect() can not be used any more, '.
 | 
        
           |  |  | 3219 |             'please use \core\notification::add(), or \core\output\notification as required.');
 | 
        
           |  |  | 3220 |     }
 | 
        
           |  |  | 3221 |   | 
        
           |  |  | 3222 |     /**
 | 
        
           |  |  | 3223 |      * Render a notification (that is, a status message about something that has
 | 
        
           |  |  | 3224 |      * just happened).
 | 
        
           |  |  | 3225 |      *
 | 
        
           |  |  | 3226 |      * @param \core\output\notification $notification the notification to print out
 | 
        
           |  |  | 3227 |      * @return string the HTML to output.
 | 
        
           |  |  | 3228 |      */
 | 
        
           |  |  | 3229 |     protected function render_notification(\core\output\notification $notification) {
 | 
        
           |  |  | 3230 |         return $this->render_from_template($notification->get_template_name(), $notification->export_for_template($this));
 | 
        
           |  |  | 3231 |     }
 | 
        
           |  |  | 3232 |   | 
        
           |  |  | 3233 |     /**
 | 
        
           |  |  | 3234 |      * Returns HTML to display a continue button that goes to a particular URL.
 | 
        
           |  |  | 3235 |      *
 | 
        
           |  |  | 3236 |      * @param string|moodle_url $url The url the button goes to.
 | 
        
           |  |  | 3237 |      * @return string the HTML to output.
 | 
        
           |  |  | 3238 |      */
 | 
        
           |  |  | 3239 |     public function continue_button($url) {
 | 
        
           |  |  | 3240 |         if (!($url instanceof moodle_url)) {
 | 
        
           |  |  | 3241 |             $url = new moodle_url($url);
 | 
        
           |  |  | 3242 |         }
 | 
        
           |  |  | 3243 |         $button = new single_button($url, get_string('continue'), 'get', single_button::BUTTON_PRIMARY);
 | 
        
           |  |  | 3244 |         $button->class = 'continuebutton';
 | 
        
           |  |  | 3245 |   | 
        
           |  |  | 3246 |         return $this->render($button);
 | 
        
           |  |  | 3247 |     }
 | 
        
           |  |  | 3248 |   | 
        
           |  |  | 3249 |     /**
 | 
        
           |  |  | 3250 |      * Returns HTML to display a single paging bar to provide access to other pages  (usually in a search)
 | 
        
           |  |  | 3251 |      *
 | 
        
           |  |  | 3252 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 3253 |      * {@link core_renderer::render_paging_bar()} instead.
 | 
        
           |  |  | 3254 |      *
 | 
        
           |  |  | 3255 |      * @param int $totalcount The total number of entries available to be paged through
 | 
        
           |  |  | 3256 |      * @param int $page The page you are currently viewing
 | 
        
           |  |  | 3257 |      * @param int $perpage The number of entries that should be shown per page
 | 
        
           |  |  | 3258 |      * @param string|moodle_url $baseurl url of the current page, the $pagevar parameter is added
 | 
        
           |  |  | 3259 |      * @param string $pagevar name of page parameter that holds the page number
 | 
        
           |  |  | 3260 |      * @return string the HTML to output.
 | 
        
           |  |  | 3261 |      */
 | 
        
           |  |  | 3262 |     public function paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') {
 | 
        
           |  |  | 3263 |         $pb = new paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar);
 | 
        
           |  |  | 3264 |         return $this->render($pb);
 | 
        
           |  |  | 3265 |     }
 | 
        
           |  |  | 3266 |   | 
        
           |  |  | 3267 |     /**
 | 
        
           |  |  | 3268 |      * Returns HTML to display the paging bar.
 | 
        
           |  |  | 3269 |      *
 | 
        
           |  |  | 3270 |      * @param paging_bar $pagingbar
 | 
        
           |  |  | 3271 |      * @return string the HTML to output.
 | 
        
           |  |  | 3272 |      */
 | 
        
           |  |  | 3273 |     protected function render_paging_bar(paging_bar $pagingbar) {
 | 
        
           |  |  | 3274 |         // Any more than 10 is not usable and causes weird wrapping of the pagination.
 | 
        
           |  |  | 3275 |         $pagingbar->maxdisplay = 10;
 | 
        
           |  |  | 3276 |         return $this->render_from_template('core/paging_bar', $pagingbar->export_for_template($this));
 | 
        
           |  |  | 3277 |     }
 | 
        
           |  |  | 3278 |   | 
        
           |  |  | 3279 |     /**
 | 
        
           |  |  | 3280 |      * Returns HTML to display initials bar to provide access to other pages  (usually in a search)
 | 
        
           |  |  | 3281 |      *
 | 
        
           |  |  | 3282 |      * @param string $current the currently selected letter.
 | 
        
           |  |  | 3283 |      * @param string $class class name to add to this initial bar.
 | 
        
           |  |  | 3284 |      * @param string $title the name to put in front of this initial bar.
 | 
        
           |  |  | 3285 |      * @param string $urlvar URL parameter name for this initial.
 | 
        
           |  |  | 3286 |      * @param string $url URL object.
 | 
        
           |  |  | 3287 |      * @param array $alpha of letters in the alphabet.
 | 
        
           |  |  | 3288 |      * @param bool $minirender Return a trimmed down view of the initials bar.
 | 
        
           |  |  | 3289 |      * @return string the HTML to output.
 | 
        
           |  |  | 3290 |      */
 | 
        
           |  |  | 3291 |     public function initials_bar($current, $class, $title, $urlvar, $url, $alpha = null, bool $minirender = false) {
 | 
        
           |  |  | 3292 |         $ib = new initials_bar($current, $class, $title, $urlvar, $url, $alpha, $minirender);
 | 
        
           |  |  | 3293 |         return $this->render($ib);
 | 
        
           |  |  | 3294 |     }
 | 
        
           |  |  | 3295 |   | 
        
           |  |  | 3296 |     /**
 | 
        
           |  |  | 3297 |      * Internal implementation of initials bar rendering.
 | 
        
           |  |  | 3298 |      *
 | 
        
           |  |  | 3299 |      * @param initials_bar $initialsbar
 | 
        
           |  |  | 3300 |      * @return string
 | 
        
           |  |  | 3301 |      */
 | 
        
           |  |  | 3302 |     protected function render_initials_bar(initials_bar $initialsbar) {
 | 
        
           |  |  | 3303 |         return $this->render_from_template('core/initials_bar', $initialsbar->export_for_template($this));
 | 
        
           |  |  | 3304 |     }
 | 
        
           |  |  | 3305 |   | 
        
           |  |  | 3306 |     /**
 | 
        
           |  |  | 3307 |      * Output the place a skip link goes to.
 | 
        
           |  |  | 3308 |      *
 | 
        
           |  |  | 3309 |      * @param string $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
 | 
        
           |  |  | 3310 |      * @return string the HTML to output.
 | 
        
           |  |  | 3311 |      */
 | 
        
           |  |  | 3312 |     public function skip_link_target($id = null) {
 | 
        
           |  |  | 3313 |         return html_writer::span('', '', array('id' => $id));
 | 
        
           |  |  | 3314 |     }
 | 
        
           |  |  | 3315 |   | 
        
           |  |  | 3316 |     /**
 | 
        
           |  |  | 3317 |      * Outputs a heading
 | 
        
           |  |  | 3318 |      *
 | 
        
           |  |  | 3319 |      * @param string $text The text of the heading
 | 
        
           |  |  | 3320 |      * @param int $level The level of importance of the heading. Defaulting to 2
 | 
        
           |  |  | 3321 |      * @param string $classes A space-separated list of CSS classes. Defaulting to null
 | 
        
           |  |  | 3322 |      * @param string $id An optional ID
 | 
        
           |  |  | 3323 |      * @return string the HTML to output.
 | 
        
           |  |  | 3324 |      */
 | 
        
           |  |  | 3325 |     public function heading($text, $level = 2, $classes = null, $id = null) {
 | 
        
           |  |  | 3326 |         $level = (integer) $level;
 | 
        
           |  |  | 3327 |         if ($level < 1 or $level > 6) {
 | 
        
           |  |  | 3328 |             throw new coding_exception('Heading level must be an integer between 1 and 6.');
 | 
        
           |  |  | 3329 |         }
 | 
        
           |  |  | 3330 |         return html_writer::tag('h' . $level, $text, array('id' => $id, 'class' => renderer_base::prepare_classes($classes)));
 | 
        
           |  |  | 3331 |     }
 | 
        
           |  |  | 3332 |   | 
        
           |  |  | 3333 |     /**
 | 
        
           |  |  | 3334 |      * Outputs a box.
 | 
        
           |  |  | 3335 |      *
 | 
        
           |  |  | 3336 |      * @param string $contents The contents of the box
 | 
        
           |  |  | 3337 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 3338 |      * @param string $id An optional ID
 | 
        
           |  |  | 3339 |      * @param array $attributes An array of other attributes to give the box.
 | 
        
           |  |  | 3340 |      * @return string the HTML to output.
 | 
        
           |  |  | 3341 |      */
 | 
        
           |  |  | 3342 |     public function box($contents, $classes = 'generalbox', $id = null, $attributes = array()) {
 | 
        
           |  |  | 3343 |         return $this->box_start($classes, $id, $attributes) . $contents . $this->box_end();
 | 
        
           |  |  | 3344 |     }
 | 
        
           |  |  | 3345 |   | 
        
           |  |  | 3346 |     /**
 | 
        
           |  |  | 3347 |      * Outputs the opening section of a box.
 | 
        
           |  |  | 3348 |      *
 | 
        
           |  |  | 3349 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 3350 |      * @param string $id An optional ID
 | 
        
           |  |  | 3351 |      * @param array $attributes An array of other attributes to give the box.
 | 
        
           |  |  | 3352 |      * @return string the HTML to output.
 | 
        
           |  |  | 3353 |      */
 | 
        
           |  |  | 3354 |     public function box_start($classes = 'generalbox', $id = null, $attributes = array()) {
 | 
        
           |  |  | 3355 |         $this->opencontainers->push('box', html_writer::end_tag('div'));
 | 
        
           |  |  | 3356 |         $attributes['id'] = $id;
 | 
        
           |  |  | 3357 |         $attributes['class'] = 'box py-3 ' . renderer_base::prepare_classes($classes);
 | 
        
           |  |  | 3358 |         return html_writer::start_tag('div', $attributes);
 | 
        
           |  |  | 3359 |     }
 | 
        
           |  |  | 3360 |   | 
        
           |  |  | 3361 |     /**
 | 
        
           |  |  | 3362 |      * Outputs the closing section of a box.
 | 
        
           |  |  | 3363 |      *
 | 
        
           |  |  | 3364 |      * @return string the HTML to output.
 | 
        
           |  |  | 3365 |      */
 | 
        
           |  |  | 3366 |     public function box_end() {
 | 
        
           |  |  | 3367 |         return $this->opencontainers->pop('box');
 | 
        
           |  |  | 3368 |     }
 | 
        
           |  |  | 3369 |   | 
        
           |  |  | 3370 |     /**
 | 
        
           |  |  | 3371 |      * Outputs a paragraph.
 | 
        
           |  |  | 3372 |      *
 | 
        
           |  |  | 3373 |      * @param string $contents The contents of the paragraph
 | 
        
           |  |  | 3374 |      * @param string|null $classes A space-separated list of CSS classes
 | 
        
           |  |  | 3375 |      * @param string|null $id An optional ID
 | 
        
           |  |  | 3376 |      * @return string the HTML to output.
 | 
        
           |  |  | 3377 |      */
 | 
        
           |  |  | 3378 |     public function paragraph(string $contents, ?string $classes = null, ?string $id = null): string {
 | 
        
           |  |  | 3379 |         return html_writer::tag(
 | 
        
           |  |  | 3380 |             'p',
 | 
        
           |  |  | 3381 |             $contents,
 | 
        
           |  |  | 3382 |             ['id' => $id, 'class' => renderer_base::prepare_classes($classes)]
 | 
        
           |  |  | 3383 |         );
 | 
        
           |  |  | 3384 |     }
 | 
        
           |  |  | 3385 |   | 
        
           |  |  | 3386 |     /**
 | 
        
           |  |  | 3387 |      * Outputs a screen reader only inline text.
 | 
        
           |  |  | 3388 |      *
 | 
        
           |  |  | 3389 |      * @param string $contents The contents of the paragraph
 | 
        
           |  |  | 3390 |      * @return string the HTML to output.
 | 
        
           |  |  | 3391 |      */
 | 
        
           |  |  | 3392 |     public function sr_text(string $contents): string {
 | 
        
           |  |  | 3393 |         return html_writer::tag(
 | 
        
           |  |  | 3394 |             'span',
 | 
        
           |  |  | 3395 |             $contents,
 | 
        
           |  |  | 3396 |             ['class' => 'sr-only']
 | 
        
           |  |  | 3397 |         ) . ' ';
 | 
        
           |  |  | 3398 |     }
 | 
        
           |  |  | 3399 |   | 
        
           |  |  | 3400 |     /**
 | 
        
           |  |  | 3401 |      * Outputs a container.
 | 
        
           |  |  | 3402 |      *
 | 
        
           |  |  | 3403 |      * @param string $contents The contents of the box
 | 
        
           |  |  | 3404 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 3405 |      * @param string $id An optional ID
 | 
        
           |  |  | 3406 |      * @param array $attributes Optional other attributes as array
 | 
        
           |  |  | 3407 |      * @return string the HTML to output.
 | 
        
           |  |  | 3408 |      */
 | 
        
           |  |  | 3409 |     public function container($contents, $classes = null, $id = null, $attributes = []) {
 | 
        
           |  |  | 3410 |         return $this->container_start($classes, $id, $attributes) . $contents . $this->container_end();
 | 
        
           |  |  | 3411 |     }
 | 
        
           |  |  | 3412 |   | 
        
           |  |  | 3413 |     /**
 | 
        
           |  |  | 3414 |      * Outputs the opening section of a container.
 | 
        
           |  |  | 3415 |      *
 | 
        
           |  |  | 3416 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 3417 |      * @param string $id An optional ID
 | 
        
           |  |  | 3418 |      * @param array $attributes Optional other attributes as array
 | 
        
           |  |  | 3419 |      * @return string the HTML to output.
 | 
        
           |  |  | 3420 |      */
 | 
        
           |  |  | 3421 |     public function container_start($classes = null, $id = null, $attributes = []) {
 | 
        
           |  |  | 3422 |         $this->opencontainers->push('container', html_writer::end_tag('div'));
 | 
        
           |  |  | 3423 |         $attributes = array_merge(['id' => $id, 'class' => renderer_base::prepare_classes($classes)], $attributes);
 | 
        
           |  |  | 3424 |         return html_writer::start_tag('div', $attributes);
 | 
        
           |  |  | 3425 |     }
 | 
        
           |  |  | 3426 |   | 
        
           |  |  | 3427 |     /**
 | 
        
           |  |  | 3428 |      * Outputs the closing section of a container.
 | 
        
           |  |  | 3429 |      *
 | 
        
           |  |  | 3430 |      * @return string the HTML to output.
 | 
        
           |  |  | 3431 |      */
 | 
        
           |  |  | 3432 |     public function container_end() {
 | 
        
           |  |  | 3433 |         return $this->opencontainers->pop('container');
 | 
        
           |  |  | 3434 |     }
 | 
        
           |  |  | 3435 |   | 
        
           |  |  | 3436 |     /**
 | 
        
           |  |  | 3437 |      * Make nested HTML lists out of the items
 | 
        
           |  |  | 3438 |      *
 | 
        
           |  |  | 3439 |      * The resulting list will look something like this:
 | 
        
           |  |  | 3440 |      *
 | 
        
           |  |  | 3441 |      * <pre>
 | 
        
           |  |  | 3442 |      * <<ul>>
 | 
        
           |  |  | 3443 |      * <<li>><div class='tree_item parent'>(item contents)</div>
 | 
        
           |  |  | 3444 |      *      <<ul>
 | 
        
           |  |  | 3445 |      *      <<li>><div class='tree_item'>(item contents)</div><</li>>
 | 
        
           |  |  | 3446 |      *      <</ul>>
 | 
        
           |  |  | 3447 |      * <</li>>
 | 
        
           |  |  | 3448 |      * <</ul>>
 | 
        
           |  |  | 3449 |      * </pre>
 | 
        
           |  |  | 3450 |      *
 | 
        
           |  |  | 3451 |      * @param array $items
 | 
        
           |  |  | 3452 |      * @param array $attrs html attributes passed to the top ofs the list
 | 
        
           |  |  | 3453 |      * @return string HTML
 | 
        
           |  |  | 3454 |      */
 | 
        
           |  |  | 3455 |     public function tree_block_contents($items, $attrs = array()) {
 | 
        
           |  |  | 3456 |         // exit if empty, we don't want an empty ul element
 | 
        
           |  |  | 3457 |         if (empty($items)) {
 | 
        
           |  |  | 3458 |             return '';
 | 
        
           |  |  | 3459 |         }
 | 
        
           |  |  | 3460 |         // array of nested li elements
 | 
        
           |  |  | 3461 |         $lis = array();
 | 
        
           |  |  | 3462 |         foreach ($items as $item) {
 | 
        
           |  |  | 3463 |             // this applies to the li item which contains all child lists too
 | 
        
           |  |  | 3464 |             $content = $item->content($this);
 | 
        
           |  |  | 3465 |             $liclasses = array($item->get_css_type());
 | 
        
           |  |  | 3466 |             if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count()==0  && $item->nodetype==navigation_node::NODETYPE_BRANCH)) {
 | 
        
           |  |  | 3467 |                 $liclasses[] = 'collapsed';
 | 
        
           |  |  | 3468 |             }
 | 
        
           |  |  | 3469 |             if ($item->isactive === true) {
 | 
        
           |  |  | 3470 |                 $liclasses[] = 'current_branch';
 | 
        
           |  |  | 3471 |             }
 | 
        
           |  |  | 3472 |             $liattr = array('class'=>join(' ',$liclasses));
 | 
        
           |  |  | 3473 |             // class attribute on the div item which only contains the item content
 | 
        
           |  |  | 3474 |             $divclasses = array('tree_item');
 | 
        
           |  |  | 3475 |             if ($item->children->count()>0  || $item->nodetype==navigation_node::NODETYPE_BRANCH) {
 | 
        
           |  |  | 3476 |                 $divclasses[] = 'branch';
 | 
        
           |  |  | 3477 |             } else {
 | 
        
           |  |  | 3478 |                 $divclasses[] = 'leaf';
 | 
        
           |  |  | 3479 |             }
 | 
        
           |  |  | 3480 |             if (!empty($item->classes) && count($item->classes)>0) {
 | 
        
           |  |  | 3481 |                 $divclasses[] = join(' ', $item->classes);
 | 
        
           |  |  | 3482 |             }
 | 
        
           |  |  | 3483 |             $divattr = array('class'=>join(' ', $divclasses));
 | 
        
           |  |  | 3484 |             if (!empty($item->id)) {
 | 
        
           |  |  | 3485 |                 $divattr['id'] = $item->id;
 | 
        
           |  |  | 3486 |             }
 | 
        
           |  |  | 3487 |             $content = html_writer::tag('p', $content, $divattr) . $this->tree_block_contents($item->children);
 | 
        
           |  |  | 3488 |             if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
 | 
        
           |  |  | 3489 |                 $content = html_writer::empty_tag('hr') . $content;
 | 
        
           |  |  | 3490 |             }
 | 
        
           |  |  | 3491 |             $content = html_writer::tag('li', $content, $liattr);
 | 
        
           |  |  | 3492 |             $lis[] = $content;
 | 
        
           |  |  | 3493 |         }
 | 
        
           |  |  | 3494 |         return html_writer::tag('ul', implode("\n", $lis), $attrs);
 | 
        
           |  |  | 3495 |     }
 | 
        
           |  |  | 3496 |   | 
        
           |  |  | 3497 |     /**
 | 
        
           |  |  | 3498 |      * Returns a search box.
 | 
        
           |  |  | 3499 |      *
 | 
        
           |  |  | 3500 |      * @param  string $id     The search box wrapper div id, defaults to an autogenerated one.
 | 
        
           |  |  | 3501 |      * @return string         HTML with the search form hidden by default.
 | 
        
           |  |  | 3502 |      */
 | 
        
           |  |  | 3503 |     public function search_box($id = false) {
 | 
        
           |  |  | 3504 |         global $CFG;
 | 
        
           |  |  | 3505 |   | 
        
           |  |  | 3506 |         // Accessing $CFG directly as using \core_search::is_global_search_enabled would
 | 
        
           |  |  | 3507 |         // result in an extra included file for each site, even the ones where global search
 | 
        
           |  |  | 3508 |         // is disabled.
 | 
        
           |  |  | 3509 |         if (empty($CFG->enableglobalsearch) || !has_capability('moodle/search:query', context_system::instance())) {
 | 
        
           |  |  | 3510 |             return '';
 | 
        
           |  |  | 3511 |         }
 | 
        
           |  |  | 3512 |   | 
        
           |  |  | 3513 |         $data = [
 | 
        
           |  |  | 3514 |             'action' => new moodle_url('/search/index.php'),
 | 
        
           |  |  | 3515 |             'hiddenfields' => (object) ['name' => 'context', 'value' => $this->page->context->id],
 | 
        
           |  |  | 3516 |             'inputname' => 'q',
 | 
        
           |  |  | 3517 |             'searchstring' => get_string('search'),
 | 
        
           |  |  | 3518 |             ];
 | 
        
           |  |  | 3519 |         return $this->render_from_template('core/search_input_navbar', $data);
 | 
        
           |  |  | 3520 |     }
 | 
        
           |  |  | 3521 |   | 
        
           |  |  | 3522 |     /**
 | 
        
           |  |  | 3523 |      * Allow plugins to provide some content to be rendered in the navbar.
 | 
        
           |  |  | 3524 |      * The plugin must define a PLUGIN_render_navbar_output function that returns
 | 
        
           |  |  | 3525 |      * the HTML they wish to add to the navbar.
 | 
        
           |  |  | 3526 |      *
 | 
        
           |  |  | 3527 |      * @return string HTML for the navbar
 | 
        
           |  |  | 3528 |      */
 | 
        
           |  |  | 3529 |     public function navbar_plugin_output() {
 | 
        
           |  |  | 3530 |         $output = '';
 | 
        
           |  |  | 3531 |   | 
        
           |  |  | 3532 |         // Give subsystems an opportunity to inject extra html content. The callback
 | 
        
           |  |  | 3533 |         // must always return a string containing valid html.
 | 
        
           |  |  | 3534 |         foreach (\core_component::get_core_subsystems() as $name => $path) {
 | 
        
           |  |  | 3535 |             if ($path) {
 | 
        
           |  |  | 3536 |                 $output .= component_callback($name, 'render_navbar_output', [$this], '');
 | 
        
           |  |  | 3537 |             }
 | 
        
           |  |  | 3538 |         }
 | 
        
           |  |  | 3539 |   | 
        
           |  |  | 3540 |         if ($pluginsfunction = get_plugins_with_function('render_navbar_output')) {
 | 
        
           |  |  | 3541 |             foreach ($pluginsfunction as $plugintype => $plugins) {
 | 
        
           |  |  | 3542 |                 foreach ($plugins as $pluginfunction) {
 | 
        
           |  |  | 3543 |                     $output .= $pluginfunction($this);
 | 
        
           |  |  | 3544 |                 }
 | 
        
           |  |  | 3545 |             }
 | 
        
           |  |  | 3546 |         }
 | 
        
           |  |  | 3547 |   | 
        
           |  |  | 3548 |         return $output;
 | 
        
           |  |  | 3549 |     }
 | 
        
           |  |  | 3550 |   | 
        
           |  |  | 3551 |     /**
 | 
        
           |  |  | 3552 |      * Construct a user menu, returning HTML that can be echoed out by a
 | 
        
           |  |  | 3553 |      * layout file.
 | 
        
           |  |  | 3554 |      *
 | 
        
           |  |  | 3555 |      * @param stdClass $user A user object, usually $USER.
 | 
        
           |  |  | 3556 |      * @param bool $withlinks true if a dropdown should be built.
 | 
        
           |  |  | 3557 |      * @return string HTML fragment.
 | 
        
           |  |  | 3558 |      */
 | 
        
           |  |  | 3559 |     public function user_menu($user = null, $withlinks = null) {
 | 
        
           |  |  | 3560 |         global $USER, $CFG;
 | 
        
           |  |  | 3561 |         require_once($CFG->dirroot . '/user/lib.php');
 | 
        
           |  |  | 3562 |   | 
        
           |  |  | 3563 |         if (is_null($user)) {
 | 
        
           |  |  | 3564 |             $user = $USER;
 | 
        
           |  |  | 3565 |         }
 | 
        
           |  |  | 3566 |   | 
        
           |  |  | 3567 |         // Note: this behaviour is intended to match that of core_renderer::login_info,
 | 
        
           |  |  | 3568 |         // but should not be considered to be good practice; layout options are
 | 
        
           |  |  | 3569 |         // intended to be theme-specific. Please don't copy this snippet anywhere else.
 | 
        
           |  |  | 3570 |         if (is_null($withlinks)) {
 | 
        
           |  |  | 3571 |             $withlinks = empty($this->page->layout_options['nologinlinks']);
 | 
        
           |  |  | 3572 |         }
 | 
        
           |  |  | 3573 |   | 
        
           |  |  | 3574 |         // Add a class for when $withlinks is false.
 | 
        
           |  |  | 3575 |         $usermenuclasses = 'usermenu';
 | 
        
           |  |  | 3576 |         if (!$withlinks) {
 | 
        
           |  |  | 3577 |             $usermenuclasses .= ' withoutlinks';
 | 
        
           |  |  | 3578 |         }
 | 
        
           |  |  | 3579 |   | 
        
           |  |  | 3580 |         $returnstr = "";
 | 
        
           |  |  | 3581 |   | 
        
           |  |  | 3582 |         // If during initial install, return the empty return string.
 | 
        
           |  |  | 3583 |         if (during_initial_install()) {
 | 
        
           |  |  | 3584 |             return $returnstr;
 | 
        
           |  |  | 3585 |         }
 | 
        
           |  |  | 3586 |   | 
        
           |  |  | 3587 |         $loginpage = $this->is_login_page();
 | 
        
           |  |  | 3588 |         $loginurl = get_login_url();
 | 
        
           |  |  | 3589 |   | 
        
           |  |  | 3590 |         // Get some navigation opts.
 | 
        
           |  |  | 3591 |         $opts = user_get_user_navigation_info($user, $this->page);
 | 
        
           |  |  | 3592 |   | 
        
           |  |  | 3593 |         if (!empty($opts->unauthenticateduser)) {
 | 
        
           |  |  | 3594 |             $returnstr = get_string($opts->unauthenticateduser['content'], 'moodle');
 | 
        
           |  |  | 3595 |             // If not logged in, show the typical not-logged-in string.
 | 
        
           |  |  | 3596 |             if (!$loginpage && (!$opts->unauthenticateduser['guest'] || $withlinks)) {
 | 
        
           |  |  | 3597 |                 $returnstr .= " (<a href=\"$loginurl\">" . get_string('login') . '</a>)';
 | 
        
           |  |  | 3598 |             }
 | 
        
           |  |  | 3599 |   | 
        
           |  |  | 3600 |             return html_writer::div(
 | 
        
           |  |  | 3601 |                 html_writer::span(
 | 
        
           |  |  | 3602 |                     $returnstr,
 | 
        
           |  |  | 3603 |                     'login nav-link'
 | 
        
           |  |  | 3604 |                 ),
 | 
        
           |  |  | 3605 |                 $usermenuclasses
 | 
        
           |  |  | 3606 |             );
 | 
        
           |  |  | 3607 |         }
 | 
        
           |  |  | 3608 |   | 
        
           |  |  | 3609 |         $avatarclasses = "avatars";
 | 
        
           |  |  | 3610 |         $avatarcontents = html_writer::span($opts->metadata['useravatar'], 'avatar current');
 | 
        
           |  |  | 3611 |         $usertextcontents = $opts->metadata['userfullname'];
 | 
        
           |  |  | 3612 |   | 
        
           |  |  | 3613 |         // Other user.
 | 
        
           |  |  | 3614 |         if (!empty($opts->metadata['asotheruser'])) {
 | 
        
           |  |  | 3615 |             $avatarcontents .= html_writer::span(
 | 
        
           |  |  | 3616 |                 $opts->metadata['realuseravatar'],
 | 
        
           |  |  | 3617 |                 'avatar realuser'
 | 
        
           |  |  | 3618 |             );
 | 
        
           |  |  | 3619 |             $usertextcontents = $opts->metadata['realuserfullname'];
 | 
        
           |  |  | 3620 |             $usertextcontents .= html_writer::tag(
 | 
        
           |  |  | 3621 |                 'span',
 | 
        
           |  |  | 3622 |                 get_string(
 | 
        
           |  |  | 3623 |                     'loggedinas',
 | 
        
           |  |  | 3624 |                     'moodle',
 | 
        
           |  |  | 3625 |                     html_writer::span(
 | 
        
           |  |  | 3626 |                         $opts->metadata['userfullname'],
 | 
        
           |  |  | 3627 |                         'value'
 | 
        
           |  |  | 3628 |                     )
 | 
        
           |  |  | 3629 |                 ),
 | 
        
           |  |  | 3630 |                 array('class' => 'meta viewingas')
 | 
        
           |  |  | 3631 |             );
 | 
        
           |  |  | 3632 |         }
 | 
        
           |  |  | 3633 |   | 
        
           |  |  | 3634 |         // Role.
 | 
        
           |  |  | 3635 |         if (!empty($opts->metadata['asotherrole'])) {
 | 
        
           |  |  | 3636 |             $role = core_text::strtolower(preg_replace('#[ ]+#', '-', trim($opts->metadata['rolename'])));
 | 
        
           |  |  | 3637 |             $usertextcontents .= html_writer::span(
 | 
        
           |  |  | 3638 |                 $opts->metadata['rolename'],
 | 
        
           |  |  | 3639 |                 'meta role role-' . $role
 | 
        
           |  |  | 3640 |             );
 | 
        
           |  |  | 3641 |         }
 | 
        
           |  |  | 3642 |   | 
        
           |  |  | 3643 |         // User login failures.
 | 
        
           |  |  | 3644 |         if (!empty($opts->metadata['userloginfail'])) {
 | 
        
           |  |  | 3645 |             $usertextcontents .= html_writer::span(
 | 
        
           |  |  | 3646 |                 $opts->metadata['userloginfail'],
 | 
        
           |  |  | 3647 |                 'meta loginfailures'
 | 
        
           |  |  | 3648 |             );
 | 
        
           |  |  | 3649 |         }
 | 
        
           |  |  | 3650 |   | 
        
           |  |  | 3651 |         // MNet.
 | 
        
           |  |  | 3652 |         if (!empty($opts->metadata['asmnetuser'])) {
 | 
        
           |  |  | 3653 |             $mnet = strtolower(preg_replace('#[ ]+#', '-', trim($opts->metadata['mnetidprovidername'])));
 | 
        
           |  |  | 3654 |             $usertextcontents .= html_writer::span(
 | 
        
           |  |  | 3655 |                 $opts->metadata['mnetidprovidername'],
 | 
        
           |  |  | 3656 |                 'meta mnet mnet-' . $mnet
 | 
        
           |  |  | 3657 |             );
 | 
        
           |  |  | 3658 |         }
 | 
        
           |  |  | 3659 |   | 
        
           |  |  | 3660 |         $returnstr .= html_writer::span(
 | 
        
           |  |  | 3661 |             html_writer::span($usertextcontents, 'usertext mr-1') .
 | 
        
           |  |  | 3662 |             html_writer::span($avatarcontents, $avatarclasses),
 | 
        
           |  |  | 3663 |             'userbutton'
 | 
        
           |  |  | 3664 |         );
 | 
        
           |  |  | 3665 |   | 
        
           |  |  | 3666 |         // Create a divider (well, a filler).
 | 
        
           |  |  | 3667 |         $divider = new action_menu_filler();
 | 
        
           |  |  | 3668 |         $divider->primary = false;
 | 
        
           |  |  | 3669 |   | 
        
           |  |  | 3670 |         $am = new action_menu();
 | 
        
           |  |  | 3671 |         $am->set_menu_trigger(
 | 
        
           |  |  | 3672 |             $returnstr,
 | 
        
           |  |  | 3673 |             'nav-link'
 | 
        
           |  |  | 3674 |         );
 | 
        
           |  |  | 3675 |         $am->set_action_label(get_string('usermenu'));
 | 
        
           |  |  | 3676 |         $am->set_nowrap_on_items();
 | 
        
           |  |  | 3677 |         if ($withlinks) {
 | 
        
           |  |  | 3678 |             $navitemcount = count($opts->navitems);
 | 
        
           |  |  | 3679 |             $idx = 0;
 | 
        
           |  |  | 3680 |             foreach ($opts->navitems as $key => $value) {
 | 
        
           |  |  | 3681 |   | 
        
           |  |  | 3682 |                 switch ($value->itemtype) {
 | 
        
           |  |  | 3683 |                     case 'divider':
 | 
        
           |  |  | 3684 |                         // If the nav item is a divider, add one and skip link processing.
 | 
        
           |  |  | 3685 |                         $am->add($divider);
 | 
        
           |  |  | 3686 |                         break;
 | 
        
           |  |  | 3687 |   | 
        
           |  |  | 3688 |                     case 'invalid':
 | 
        
           |  |  | 3689 |                         // Silently skip invalid entries (should we post a notification?).
 | 
        
           |  |  | 3690 |                         break;
 | 
        
           |  |  | 3691 |   | 
        
           |  |  | 3692 |                     case 'link':
 | 
        
           |  |  | 3693 |                         // Process this as a link item.
 | 
        
           |  |  | 3694 |                         $pix = null;
 | 
        
           |  |  | 3695 |                         if (isset($value->pix) && !empty($value->pix)) {
 | 
        
           |  |  | 3696 |                             $pix = new pix_icon($value->pix, '', null, array('class' => 'iconsmall'));
 | 
        
           |  |  | 3697 |                         } else if (isset($value->imgsrc) && !empty($value->imgsrc)) {
 | 
        
           |  |  | 3698 |                             $value->title = html_writer::img(
 | 
        
           |  |  | 3699 |                                 $value->imgsrc,
 | 
        
           |  |  | 3700 |                                 $value->title,
 | 
        
           |  |  | 3701 |                                 array('class' => 'iconsmall')
 | 
        
           |  |  | 3702 |                             ) . $value->title;
 | 
        
           |  |  | 3703 |                         }
 | 
        
           |  |  | 3704 |   | 
        
           |  |  | 3705 |                         $al = new action_menu_link_secondary(
 | 
        
           |  |  | 3706 |                             $value->url,
 | 
        
           |  |  | 3707 |                             $pix,
 | 
        
           |  |  | 3708 |                             $value->title,
 | 
        
           |  |  | 3709 |                             array('class' => 'icon')
 | 
        
           |  |  | 3710 |                         );
 | 
        
           |  |  | 3711 |                         if (!empty($value->titleidentifier)) {
 | 
        
           |  |  | 3712 |                             $al->attributes['data-title'] = $value->titleidentifier;
 | 
        
           |  |  | 3713 |                         }
 | 
        
           |  |  | 3714 |                         $am->add($al);
 | 
        
           |  |  | 3715 |                         break;
 | 
        
           |  |  | 3716 |                 }
 | 
        
           |  |  | 3717 |   | 
        
           |  |  | 3718 |                 $idx++;
 | 
        
           |  |  | 3719 |   | 
        
           |  |  | 3720 |                 // Add dividers after the first item and before the last item.
 | 
        
           |  |  | 3721 |                 if ($idx == 1 || $idx == $navitemcount - 1) {
 | 
        
           |  |  | 3722 |                     $am->add($divider);
 | 
        
           |  |  | 3723 |                 }
 | 
        
           |  |  | 3724 |             }
 | 
        
           |  |  | 3725 |         }
 | 
        
           |  |  | 3726 |   | 
        
           |  |  | 3727 |         return html_writer::div(
 | 
        
           |  |  | 3728 |             $this->render($am),
 | 
        
           |  |  | 3729 |             $usermenuclasses
 | 
        
           |  |  | 3730 |         );
 | 
        
           |  |  | 3731 |     }
 | 
        
           |  |  | 3732 |   | 
        
           |  |  | 3733 |     /**
 | 
        
           |  |  | 3734 |      * Secure layout login info.
 | 
        
           |  |  | 3735 |      *
 | 
        
           |  |  | 3736 |      * @return string
 | 
        
           |  |  | 3737 |      */
 | 
        
           |  |  | 3738 |     public function secure_layout_login_info() {
 | 
        
           |  |  | 3739 |         if (get_config('core', 'logininfoinsecurelayout')) {
 | 
        
           |  |  | 3740 |             return $this->login_info(false);
 | 
        
           |  |  | 3741 |         } else {
 | 
        
           |  |  | 3742 |             return '';
 | 
        
           |  |  | 3743 |         }
 | 
        
           |  |  | 3744 |     }
 | 
        
           |  |  | 3745 |   | 
        
           |  |  | 3746 |     /**
 | 
        
           |  |  | 3747 |      * Returns the language menu in the secure layout.
 | 
        
           |  |  | 3748 |      *
 | 
        
           |  |  | 3749 |      * No custom menu items are passed though, such that it will render only the language selection.
 | 
        
           |  |  | 3750 |      *
 | 
        
           |  |  | 3751 |      * @return string
 | 
        
           |  |  | 3752 |      */
 | 
        
           |  |  | 3753 |     public function secure_layout_language_menu() {
 | 
        
           |  |  | 3754 |         if (get_config('core', 'langmenuinsecurelayout')) {
 | 
        
           |  |  | 3755 |             $custommenu = new custom_menu('', current_language());
 | 
        
           |  |  | 3756 |             return $this->render_custom_menu($custommenu);
 | 
        
           |  |  | 3757 |         } else {
 | 
        
           |  |  | 3758 |             return '';
 | 
        
           |  |  | 3759 |         }
 | 
        
           |  |  | 3760 |     }
 | 
        
           |  |  | 3761 |   | 
        
           |  |  | 3762 |     /**
 | 
        
           |  |  | 3763 |      * This renders the navbar.
 | 
        
           |  |  | 3764 |      * Uses bootstrap compatible html.
 | 
        
           |  |  | 3765 |      */
 | 
        
           |  |  | 3766 |     public function navbar() {
 | 
        
           |  |  | 3767 |         return $this->render_from_template('core/navbar', $this->page->navbar);
 | 
        
           |  |  | 3768 |     }
 | 
        
           |  |  | 3769 |   | 
        
           |  |  | 3770 |     /**
 | 
        
           |  |  | 3771 |      * Renders a breadcrumb navigation node object.
 | 
        
           |  |  | 3772 |      *
 | 
        
           |  |  | 3773 |      * @param breadcrumb_navigation_node $item The navigation node to render.
 | 
        
           |  |  | 3774 |      * @return string HTML fragment
 | 
        
           |  |  | 3775 |      */
 | 
        
           |  |  | 3776 |     protected function render_breadcrumb_navigation_node(breadcrumb_navigation_node $item) {
 | 
        
           |  |  | 3777 |   | 
        
           |  |  | 3778 |         if ($item->action instanceof moodle_url) {
 | 
        
           |  |  | 3779 |             $content = $item->get_content();
 | 
        
           |  |  | 3780 |             $title = $item->get_title();
 | 
        
           |  |  | 3781 |             $attributes = array();
 | 
        
           |  |  | 3782 |             $attributes['itemprop'] = 'url';
 | 
        
           |  |  | 3783 |             if ($title !== '') {
 | 
        
           |  |  | 3784 |                 $attributes['title'] = $title;
 | 
        
           |  |  | 3785 |             }
 | 
        
           |  |  | 3786 |             if ($item->hidden) {
 | 
        
           |  |  | 3787 |                 $attributes['class'] = 'dimmed_text';
 | 
        
           |  |  | 3788 |             }
 | 
        
           |  |  | 3789 |             if ($item->is_last()) {
 | 
        
           |  |  | 3790 |                 $attributes['aria-current'] = 'page';
 | 
        
           |  |  | 3791 |             }
 | 
        
           |  |  | 3792 |             $content = html_writer::tag('span', $content, array('itemprop' => 'title'));
 | 
        
           |  |  | 3793 |             $content = html_writer::link($item->action, $content, $attributes);
 | 
        
           |  |  | 3794 |   | 
        
           |  |  | 3795 |             $attributes = array();
 | 
        
           |  |  | 3796 |             $attributes['itemscope'] = '';
 | 
        
           |  |  | 3797 |             $attributes['itemtype'] = 'http://data-vocabulary.org/Breadcrumb';
 | 
        
           |  |  | 3798 |             $content = html_writer::tag('span', $content, $attributes);
 | 
        
           |  |  | 3799 |   | 
        
           |  |  | 3800 |         } else {
 | 
        
           |  |  | 3801 |             $content = $this->render_navigation_node($item);
 | 
        
           |  |  | 3802 |         }
 | 
        
           |  |  | 3803 |         return $content;
 | 
        
           |  |  | 3804 |     }
 | 
        
           |  |  | 3805 |   | 
        
           |  |  | 3806 |     /**
 | 
        
           |  |  | 3807 |      * Renders a navigation node object.
 | 
        
           |  |  | 3808 |      *
 | 
        
           |  |  | 3809 |      * @param navigation_node $item The navigation node to render.
 | 
        
           |  |  | 3810 |      * @return string HTML fragment
 | 
        
           |  |  | 3811 |      */
 | 
        
           |  |  | 3812 |     protected function render_navigation_node(navigation_node $item) {
 | 
        
           |  |  | 3813 |         $content = $item->get_content();
 | 
        
           |  |  | 3814 |         $title = $item->get_title();
 | 
        
           |  |  | 3815 |         if ($item->icon instanceof renderable && !$item->hideicon) {
 | 
        
           |  |  | 3816 |             $icon = $this->render($item->icon);
 | 
        
           |  |  | 3817 |             $content = $icon.$content; // use CSS for spacing of icons
 | 
        
           |  |  | 3818 |         }
 | 
        
           |  |  | 3819 |         if ($item->helpbutton !== null) {
 | 
        
           |  |  | 3820 |             $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton', 'tabindex'=>'0'));
 | 
        
           |  |  | 3821 |         }
 | 
        
           |  |  | 3822 |         if ($content === '') {
 | 
        
           |  |  | 3823 |             return '';
 | 
        
           |  |  | 3824 |         }
 | 
        
           |  |  | 3825 |         if ($item->action instanceof action_link) {
 | 
        
           |  |  | 3826 |             $link = $item->action;
 | 
        
           |  |  | 3827 |             if ($item->hidden) {
 | 
        
           |  |  | 3828 |                 $link->add_class('dimmed');
 | 
        
           |  |  | 3829 |             }
 | 
        
           |  |  | 3830 |             if (!empty($content)) {
 | 
        
           |  |  | 3831 |                 // Providing there is content we will use that for the link content.
 | 
        
           |  |  | 3832 |                 $link->text = $content;
 | 
        
           |  |  | 3833 |             }
 | 
        
           |  |  | 3834 |             $content = $this->render($link);
 | 
        
           |  |  | 3835 |         } else if ($item->action instanceof moodle_url) {
 | 
        
           |  |  | 3836 |             $attributes = array();
 | 
        
           |  |  | 3837 |             if ($title !== '') {
 | 
        
           |  |  | 3838 |                 $attributes['title'] = $title;
 | 
        
           |  |  | 3839 |             }
 | 
        
           |  |  | 3840 |             if ($item->hidden) {
 | 
        
           |  |  | 3841 |                 $attributes['class'] = 'dimmed_text';
 | 
        
           |  |  | 3842 |             }
 | 
        
           |  |  | 3843 |             $content = html_writer::link($item->action, $content, $attributes);
 | 
        
           |  |  | 3844 |   | 
        
           |  |  | 3845 |         } else if (is_string($item->action) || empty($item->action)) {
 | 
        
           |  |  | 3846 |             $attributes = array('tabindex'=>'0'); //add tab support to span but still maintain character stream sequence.
 | 
        
           |  |  | 3847 |             if ($title !== '') {
 | 
        
           |  |  | 3848 |                 $attributes['title'] = $title;
 | 
        
           |  |  | 3849 |             }
 | 
        
           |  |  | 3850 |             if ($item->hidden) {
 | 
        
           |  |  | 3851 |                 $attributes['class'] = 'dimmed_text';
 | 
        
           |  |  | 3852 |             }
 | 
        
           |  |  | 3853 |             $content = html_writer::tag('span', $content, $attributes);
 | 
        
           |  |  | 3854 |         }
 | 
        
           |  |  | 3855 |         return $content;
 | 
        
           |  |  | 3856 |     }
 | 
        
           |  |  | 3857 |   | 
        
           |  |  | 3858 |     /**
 | 
        
           |  |  | 3859 |      * Accessibility: Right arrow-like character is
 | 
        
           |  |  | 3860 |      * used in the breadcrumb trail, course navigation menu
 | 
        
           |  |  | 3861 |      * (previous/next activity), calendar, and search forum block.
 | 
        
           |  |  | 3862 |      * If the theme does not set characters, appropriate defaults
 | 
        
           |  |  | 3863 |      * are set automatically. Please DO NOT
 | 
        
           |  |  | 3864 |      * use < > » - these are confusing for blind users.
 | 
        
           |  |  | 3865 |      *
 | 
        
           |  |  | 3866 |      * @return string
 | 
        
           |  |  | 3867 |      */
 | 
        
           |  |  | 3868 |     public function rarrow() {
 | 
        
           |  |  | 3869 |         return $this->page->theme->rarrow;
 | 
        
           |  |  | 3870 |     }
 | 
        
           |  |  | 3871 |   | 
        
           |  |  | 3872 |     /**
 | 
        
           |  |  | 3873 |      * Accessibility: Left arrow-like character is
 | 
        
           |  |  | 3874 |      * used in the breadcrumb trail, course navigation menu
 | 
        
           |  |  | 3875 |      * (previous/next activity), calendar, and search forum block.
 | 
        
           |  |  | 3876 |      * If the theme does not set characters, appropriate defaults
 | 
        
           |  |  | 3877 |      * are set automatically. Please DO NOT
 | 
        
           |  |  | 3878 |      * use < > » - these are confusing for blind users.
 | 
        
           |  |  | 3879 |      *
 | 
        
           |  |  | 3880 |      * @return string
 | 
        
           |  |  | 3881 |      */
 | 
        
           |  |  | 3882 |     public function larrow() {
 | 
        
           |  |  | 3883 |         return $this->page->theme->larrow;
 | 
        
           |  |  | 3884 |     }
 | 
        
           |  |  | 3885 |   | 
        
           |  |  | 3886 |     /**
 | 
        
           |  |  | 3887 |      * Accessibility: Up arrow-like character is used in
 | 
        
           |  |  | 3888 |      * the book heirarchical navigation.
 | 
        
           |  |  | 3889 |      * If the theme does not set characters, appropriate defaults
 | 
        
           |  |  | 3890 |      * are set automatically. Please DO NOT
 | 
        
           |  |  | 3891 |      * use ^ - this is confusing for blind users.
 | 
        
           |  |  | 3892 |      *
 | 
        
           |  |  | 3893 |      * @return string
 | 
        
           |  |  | 3894 |      */
 | 
        
           |  |  | 3895 |     public function uarrow() {
 | 
        
           |  |  | 3896 |         return $this->page->theme->uarrow;
 | 
        
           |  |  | 3897 |     }
 | 
        
           |  |  | 3898 |   | 
        
           |  |  | 3899 |     /**
 | 
        
           |  |  | 3900 |      * Accessibility: Down arrow-like character.
 | 
        
           |  |  | 3901 |      * If the theme does not set characters, appropriate defaults
 | 
        
           |  |  | 3902 |      * are set automatically.
 | 
        
           |  |  | 3903 |      *
 | 
        
           |  |  | 3904 |      * @return string
 | 
        
           |  |  | 3905 |      */
 | 
        
           |  |  | 3906 |     public function darrow() {
 | 
        
           |  |  | 3907 |         return $this->page->theme->darrow;
 | 
        
           |  |  | 3908 |     }
 | 
        
           |  |  | 3909 |   | 
        
           |  |  | 3910 |     /**
 | 
        
           |  |  | 3911 |      * Returns the custom menu if one has been set
 | 
        
           |  |  | 3912 |      *
 | 
        
           |  |  | 3913 |      * A custom menu can be configured by browsing to a theme's settings page
 | 
        
           |  |  | 3914 |      * and then configuring the custommenu config setting as described.
 | 
        
           |  |  | 3915 |      *
 | 
        
           |  |  | 3916 |      * Theme developers: DO NOT OVERRIDE! Please override function
 | 
        
           |  |  | 3917 |      * {@link core_renderer::render_custom_menu()} instead.
 | 
        
           |  |  | 3918 |      *
 | 
        
           |  |  | 3919 |      * @param string $custommenuitems - custom menuitems set by theme instead of global theme settings
 | 
        
           |  |  | 3920 |      * @return string
 | 
        
           |  |  | 3921 |      */
 | 
        
           |  |  | 3922 |     public function custom_menu($custommenuitems = '') {
 | 
        
           |  |  | 3923 |         global $CFG;
 | 
        
           |  |  | 3924 |   | 
        
           |  |  | 3925 |         if (empty($custommenuitems) && !empty($CFG->custommenuitems)) {
 | 
        
           |  |  | 3926 |             $custommenuitems = $CFG->custommenuitems;
 | 
        
           |  |  | 3927 |         }
 | 
        
           |  |  | 3928 |         $custommenu = new custom_menu($custommenuitems, current_language());
 | 
        
           |  |  | 3929 |         return $this->render_custom_menu($custommenu);
 | 
        
           |  |  | 3930 |     }
 | 
        
           |  |  | 3931 |   | 
        
           |  |  | 3932 |     /**
 | 
        
           |  |  | 3933 |      * We want to show the custom menus as a list of links in the footer on small screens.
 | 
        
           |  |  | 3934 |      * Just return the menu object exported so we can render it differently.
 | 
        
           |  |  | 3935 |      */
 | 
        
           |  |  | 3936 |     public function custom_menu_flat() {
 | 
        
           |  |  | 3937 |         global $CFG;
 | 
        
           |  |  | 3938 |         $custommenuitems = '';
 | 
        
           |  |  | 3939 |   | 
        
           |  |  | 3940 |         if (empty($custommenuitems) && !empty($CFG->custommenuitems)) {
 | 
        
           |  |  | 3941 |             $custommenuitems = $CFG->custommenuitems;
 | 
        
           |  |  | 3942 |         }
 | 
        
           |  |  | 3943 |         $custommenu = new custom_menu($custommenuitems, current_language());
 | 
        
           |  |  | 3944 |         $langs = get_string_manager()->get_list_of_translations();
 | 
        
           |  |  | 3945 |         $haslangmenu = $this->lang_menu() != '';
 | 
        
           |  |  | 3946 |   | 
        
           |  |  | 3947 |         if ($haslangmenu) {
 | 
        
           |  |  | 3948 |             $strlang = get_string('language');
 | 
        
           |  |  | 3949 |             $currentlang = current_language();
 | 
        
           |  |  | 3950 |             if (isset($langs[$currentlang])) {
 | 
        
           |  |  | 3951 |                 $currentlang = $langs[$currentlang];
 | 
        
           |  |  | 3952 |             } else {
 | 
        
           |  |  | 3953 |                 $currentlang = $strlang;
 | 
        
           |  |  | 3954 |             }
 | 
        
           |  |  | 3955 |             $this->language = $custommenu->add($currentlang, new moodle_url('#'), $strlang, 10000);
 | 
        
           |  |  | 3956 |             foreach ($langs as $langtype => $langname) {
 | 
        
           |  |  | 3957 |                 $this->language->add($langname, new moodle_url($this->page->url, array('lang' => $langtype)), $langname);
 | 
        
           |  |  | 3958 |             }
 | 
        
           |  |  | 3959 |         }
 | 
        
           |  |  | 3960 |   | 
        
           |  |  | 3961 |         return $custommenu->export_for_template($this);
 | 
        
           |  |  | 3962 |     }
 | 
        
           |  |  | 3963 |   | 
        
           |  |  | 3964 |     /**
 | 
        
           |  |  | 3965 |      * Renders a custom menu object (located in outputcomponents.php)
 | 
        
           |  |  | 3966 |      *
 | 
        
           |  |  | 3967 |      * The custom menu this method produces makes use of the YUI3 menunav widget
 | 
        
           |  |  | 3968 |      * and requires very specific html elements and classes.
 | 
        
           |  |  | 3969 |      *
 | 
        
           |  |  | 3970 |      * @staticvar int $menucount
 | 
        
           |  |  | 3971 |      * @param custom_menu $menu
 | 
        
           |  |  | 3972 |      * @return string
 | 
        
           |  |  | 3973 |      */
 | 
        
           |  |  | 3974 |     protected function render_custom_menu(custom_menu $menu) {
 | 
        
           |  |  | 3975 |         global $CFG;
 | 
        
           |  |  | 3976 |   | 
        
           |  |  | 3977 |         $langs = get_string_manager()->get_list_of_translations();
 | 
        
           |  |  | 3978 |         $haslangmenu = $this->lang_menu() != '';
 | 
        
           |  |  | 3979 |   | 
        
           |  |  | 3980 |         if (!$menu->has_children() && !$haslangmenu) {
 | 
        
           |  |  | 3981 |             return '';
 | 
        
           |  |  | 3982 |         }
 | 
        
           |  |  | 3983 |   | 
        
           |  |  | 3984 |         if ($haslangmenu) {
 | 
        
           |  |  | 3985 |             $strlang = get_string('language');
 | 
        
           |  |  | 3986 |             $currentlang = current_language();
 | 
        
           |  |  | 3987 |             if (isset($langs[$currentlang])) {
 | 
        
           |  |  | 3988 |                 $currentlangstr = $langs[$currentlang];
 | 
        
           |  |  | 3989 |             } else {
 | 
        
           |  |  | 3990 |                 $currentlangstr = $strlang;
 | 
        
           |  |  | 3991 |             }
 | 
        
           |  |  | 3992 |             $this->language = $menu->add($currentlangstr, new moodle_url('#'), $strlang, 10000);
 | 
        
           |  |  | 3993 |             foreach ($langs as $langtype => $langname) {
 | 
        
           |  |  | 3994 |                 $attributes = [];
 | 
        
           |  |  | 3995 |                 // Set the lang attribute for languages different from the page's current language.
 | 
        
           |  |  | 3996 |                 if ($langtype !== $currentlang) {
 | 
        
           |  |  | 3997 |                     $attributes[] = [
 | 
        
           |  |  | 3998 |                         'key' => 'lang',
 | 
        
           |  |  | 3999 |                         'value' => get_html_lang_attribute_value($langtype),
 | 
        
           |  |  | 4000 |                     ];
 | 
        
           |  |  | 4001 |                 }
 | 
        
           |  |  | 4002 |                 $this->language->add($langname, new moodle_url($this->page->url, ['lang' => $langtype]), null, null, $attributes);
 | 
        
           |  |  | 4003 |             }
 | 
        
           |  |  | 4004 |         }
 | 
        
           |  |  | 4005 |   | 
        
           |  |  | 4006 |         $content = '';
 | 
        
           |  |  | 4007 |         foreach ($menu->get_children() as $item) {
 | 
        
           |  |  | 4008 |             $context = $item->export_for_template($this);
 | 
        
           |  |  | 4009 |             $content .= $this->render_from_template('core/custom_menu_item', $context);
 | 
        
           |  |  | 4010 |         }
 | 
        
           |  |  | 4011 |   | 
        
           |  |  | 4012 |         return $content;
 | 
        
           |  |  | 4013 |     }
 | 
        
           |  |  | 4014 |   | 
        
           |  |  | 4015 |     /**
 | 
        
           |  |  | 4016 |      * Renders a custom menu node as part of a submenu
 | 
        
           |  |  | 4017 |      *
 | 
        
           |  |  | 4018 |      * The custom menu this method produces makes use of the YUI3 menunav widget
 | 
        
           |  |  | 4019 |      * and requires very specific html elements and classes.
 | 
        
           |  |  | 4020 |      *
 | 
        
           |  |  | 4021 |      * @see core:renderer::render_custom_menu()
 | 
        
           |  |  | 4022 |      *
 | 
        
           |  |  | 4023 |      * @staticvar int $submenucount
 | 
        
           |  |  | 4024 |      * @param custom_menu_item $menunode
 | 
        
           |  |  | 4025 |      * @return string
 | 
        
           |  |  | 4026 |      */
 | 
        
           |  |  | 4027 |     protected function render_custom_menu_item(custom_menu_item $menunode) {
 | 
        
           |  |  | 4028 |         // Required to ensure we get unique trackable id's
 | 
        
           |  |  | 4029 |         static $submenucount = 0;
 | 
        
           |  |  | 4030 |         if ($menunode->has_children()) {
 | 
        
           |  |  | 4031 |             // If the child has menus render it as a sub menu
 | 
        
           |  |  | 4032 |             $submenucount++;
 | 
        
           |  |  | 4033 |             $content = html_writer::start_tag('li');
 | 
        
           |  |  | 4034 |             if ($menunode->get_url() !== null) {
 | 
        
           |  |  | 4035 |                 $url = $menunode->get_url();
 | 
        
           |  |  | 4036 |             } else {
 | 
        
           |  |  | 4037 |                 $url = '#cm_submenu_'.$submenucount;
 | 
        
           |  |  | 4038 |             }
 | 
        
           |  |  | 4039 |             $content .= html_writer::link($url, $menunode->get_text(), array('class'=>'yui3-menu-label', 'title'=>$menunode->get_title()));
 | 
        
           |  |  | 4040 |             $content .= html_writer::start_tag('div', array('id'=>'cm_submenu_'.$submenucount, 'class'=>'yui3-menu custom_menu_submenu'));
 | 
        
           |  |  | 4041 |             $content .= html_writer::start_tag('div', array('class'=>'yui3-menu-content'));
 | 
        
           |  |  | 4042 |             $content .= html_writer::start_tag('ul');
 | 
        
           |  |  | 4043 |             foreach ($menunode->get_children() as $menunode) {
 | 
        
           |  |  | 4044 |                 $content .= $this->render_custom_menu_item($menunode);
 | 
        
           |  |  | 4045 |             }
 | 
        
           |  |  | 4046 |             $content .= html_writer::end_tag('ul');
 | 
        
           |  |  | 4047 |             $content .= html_writer::end_tag('div');
 | 
        
           |  |  | 4048 |             $content .= html_writer::end_tag('div');
 | 
        
           |  |  | 4049 |             $content .= html_writer::end_tag('li');
 | 
        
           |  |  | 4050 |         } else {
 | 
        
           |  |  | 4051 |             // The node doesn't have children so produce a final menuitem.
 | 
        
           |  |  | 4052 |             // Also, if the node's text matches '####', add a class so we can treat it as a divider.
 | 
        
           |  |  | 4053 |             $content = '';
 | 
        
           |  |  | 4054 |             if (preg_match("/^#+$/", $menunode->get_text())) {
 | 
        
           |  |  | 4055 |   | 
        
           |  |  | 4056 |                 // This is a divider.
 | 
        
           |  |  | 4057 |                 $content = html_writer::start_tag('li', array('class' => 'yui3-menuitem divider'));
 | 
        
           |  |  | 4058 |             } else {
 | 
        
           |  |  | 4059 |                 $content = html_writer::start_tag(
 | 
        
           |  |  | 4060 |                     'li',
 | 
        
           |  |  | 4061 |                     array(
 | 
        
           |  |  | 4062 |                         'class' => 'yui3-menuitem'
 | 
        
           |  |  | 4063 |                     )
 | 
        
           |  |  | 4064 |                 );
 | 
        
           |  |  | 4065 |                 if ($menunode->get_url() !== null) {
 | 
        
           |  |  | 4066 |                     $url = $menunode->get_url();
 | 
        
           |  |  | 4067 |                 } else {
 | 
        
           |  |  | 4068 |                     $url = '#';
 | 
        
           |  |  | 4069 |                 }
 | 
        
           |  |  | 4070 |                 $content .= html_writer::link(
 | 
        
           |  |  | 4071 |                     $url,
 | 
        
           |  |  | 4072 |                     $menunode->get_text(),
 | 
        
           |  |  | 4073 |                     array('class' => 'yui3-menuitem-content', 'title' => $menunode->get_title())
 | 
        
           |  |  | 4074 |                 );
 | 
        
           |  |  | 4075 |             }
 | 
        
           |  |  | 4076 |             $content .= html_writer::end_tag('li');
 | 
        
           |  |  | 4077 |         }
 | 
        
           |  |  | 4078 |         // Return the sub menu
 | 
        
           |  |  | 4079 |         return $content;
 | 
        
           |  |  | 4080 |     }
 | 
        
           |  |  | 4081 |   | 
        
           |  |  | 4082 |     /**
 | 
        
           |  |  | 4083 |      * Renders theme links for switching between default and other themes.
 | 
        
           |  |  | 4084 |      *
 | 
        
           |  |  | 4085 |      * @return string
 | 
        
           |  |  | 4086 |      */
 | 
        
           |  |  | 4087 |     protected function theme_switch_links() {
 | 
        
           |  |  | 4088 |   | 
        
           |  |  | 4089 |         $actualdevice = core_useragent::get_device_type();
 | 
        
           |  |  | 4090 |         $currentdevice = $this->page->devicetypeinuse;
 | 
        
           |  |  | 4091 |         $switched = ($actualdevice != $currentdevice);
 | 
        
           |  |  | 4092 |   | 
        
           |  |  | 4093 |         if (!$switched && $currentdevice == 'default' && $actualdevice == 'default') {
 | 
        
           |  |  | 4094 |             // The user is using the a default device and hasn't switched so don't shown the switch
 | 
        
           |  |  | 4095 |             // device links.
 | 
        
           |  |  | 4096 |             return '';
 | 
        
           |  |  | 4097 |         }
 | 
        
           |  |  | 4098 |   | 
        
           |  |  | 4099 |         if ($switched) {
 | 
        
           |  |  | 4100 |             $linktext = get_string('switchdevicerecommended');
 | 
        
           |  |  | 4101 |             $devicetype = $actualdevice;
 | 
        
           |  |  | 4102 |         } else {
 | 
        
           |  |  | 4103 |             $linktext = get_string('switchdevicedefault');
 | 
        
           |  |  | 4104 |             $devicetype = 'default';
 | 
        
           |  |  | 4105 |         }
 | 
        
           |  |  | 4106 |         $linkurl = new moodle_url('/theme/switchdevice.php', array('url' => $this->page->url, 'device' => $devicetype, 'sesskey' => sesskey()));
 | 
        
           |  |  | 4107 |   | 
        
           |  |  | 4108 |         $content  = html_writer::start_tag('div', array('id' => 'theme_switch_link'));
 | 
        
           |  |  | 4109 |         $content .= html_writer::link($linkurl, $linktext, array('rel' => 'nofollow'));
 | 
        
           |  |  | 4110 |         $content .= html_writer::end_tag('div');
 | 
        
           |  |  | 4111 |   | 
        
           |  |  | 4112 |         return $content;
 | 
        
           |  |  | 4113 |     }
 | 
        
           |  |  | 4114 |   | 
        
           |  |  | 4115 |     /**
 | 
        
           |  |  | 4116 |      * Renders tabs
 | 
        
           |  |  | 4117 |      *
 | 
        
           |  |  | 4118 |      * This function replaces print_tabs() used before Moodle 2.5 but with slightly different arguments
 | 
        
           |  |  | 4119 |      *
 | 
        
           |  |  | 4120 |      * Theme developers: In order to change how tabs are displayed please override functions
 | 
        
           |  |  | 4121 |      * {@link core_renderer::render_tabtree()} and/or {@link core_renderer::render_tabobject()}
 | 
        
           |  |  | 4122 |      *
 | 
        
           |  |  | 4123 |      * @param array $tabs array of tabs, each of them may have it's own ->subtree
 | 
        
           |  |  | 4124 |      * @param string|null $selected which tab to mark as selected, all parent tabs will
 | 
        
           |  |  | 4125 |      *     automatically be marked as activated
 | 
        
           |  |  | 4126 |      * @param array|string|null $inactive list of ids of inactive tabs, regardless of
 | 
        
           |  |  | 4127 |      *     their level. Note that you can as weel specify tabobject::$inactive for separate instances
 | 
        
           |  |  | 4128 |      * @return string
 | 
        
           |  |  | 4129 |      */
 | 
        
           |  |  | 4130 |     final public function tabtree($tabs, $selected = null, $inactive = null) {
 | 
        
           |  |  | 4131 |         return $this->render(new tabtree($tabs, $selected, $inactive));
 | 
        
           |  |  | 4132 |     }
 | 
        
           |  |  | 4133 |   | 
        
           |  |  | 4134 |     /**
 | 
        
           |  |  | 4135 |      * Renders tabtree
 | 
        
           |  |  | 4136 |      *
 | 
        
           |  |  | 4137 |      * @param tabtree $tabtree
 | 
        
           |  |  | 4138 |      * @return string
 | 
        
           |  |  | 4139 |      */
 | 
        
           |  |  | 4140 |     protected function render_tabtree(tabtree $tabtree) {
 | 
        
           |  |  | 4141 |         if (empty($tabtree->subtree)) {
 | 
        
           |  |  | 4142 |             return '';
 | 
        
           |  |  | 4143 |         }
 | 
        
           |  |  | 4144 |         $data = $tabtree->export_for_template($this);
 | 
        
           |  |  | 4145 |         return $this->render_from_template('core/tabtree', $data);
 | 
        
           |  |  | 4146 |     }
 | 
        
           |  |  | 4147 |   | 
        
           |  |  | 4148 |     /**
 | 
        
           |  |  | 4149 |      * Renders tabobject (part of tabtree)
 | 
        
           |  |  | 4150 |      *
 | 
        
           |  |  | 4151 |      * This function is called from {@link core_renderer::render_tabtree()}
 | 
        
           |  |  | 4152 |      * and also it calls itself when printing the $tabobject subtree recursively.
 | 
        
           |  |  | 4153 |      *
 | 
        
           |  |  | 4154 |      * Property $tabobject->level indicates the number of row of tabs.
 | 
        
           |  |  | 4155 |      *
 | 
        
           |  |  | 4156 |      * @param tabobject $tabobject
 | 
        
           |  |  | 4157 |      * @return string HTML fragment
 | 
        
           |  |  | 4158 |      */
 | 
        
           |  |  | 4159 |     protected function render_tabobject(tabobject $tabobject) {
 | 
        
           |  |  | 4160 |         $str = '';
 | 
        
           |  |  | 4161 |   | 
        
           |  |  | 4162 |         // Print name of the current tab.
 | 
        
           |  |  | 4163 |         if ($tabobject instanceof tabtree) {
 | 
        
           |  |  | 4164 |             // No name for tabtree root.
 | 
        
           |  |  | 4165 |         } else if ($tabobject->inactive || $tabobject->activated || ($tabobject->selected && !$tabobject->linkedwhenselected)) {
 | 
        
           |  |  | 4166 |             // Tab name without a link. The <a> tag is used for styling.
 | 
        
           |  |  | 4167 |             $str .= html_writer::tag('a', html_writer::span($tabobject->text), array('class' => 'nolink moodle-has-zindex'));
 | 
        
           |  |  | 4168 |         } else {
 | 
        
           |  |  | 4169 |             // Tab name with a link.
 | 
        
           |  |  | 4170 |             if (!($tabobject->link instanceof moodle_url)) {
 | 
        
           |  |  | 4171 |                 // backward compartibility when link was passed as quoted string
 | 
        
           |  |  | 4172 |                 $str .= "<a href=\"$tabobject->link\" title=\"$tabobject->title\"><span>$tabobject->text</span></a>";
 | 
        
           |  |  | 4173 |             } else {
 | 
        
           |  |  | 4174 |                 $str .= html_writer::link($tabobject->link, html_writer::span($tabobject->text), array('title' => $tabobject->title));
 | 
        
           |  |  | 4175 |             }
 | 
        
           |  |  | 4176 |         }
 | 
        
           |  |  | 4177 |   | 
        
           |  |  | 4178 |         if (empty($tabobject->subtree)) {
 | 
        
           |  |  | 4179 |             if ($tabobject->selected) {
 | 
        
           |  |  | 4180 |                 $str .= html_writer::tag('div', ' ', array('class' => 'tabrow'. ($tabobject->level + 1). ' empty'));
 | 
        
           |  |  | 4181 |             }
 | 
        
           |  |  | 4182 |             return $str;
 | 
        
           |  |  | 4183 |         }
 | 
        
           |  |  | 4184 |   | 
        
           |  |  | 4185 |         // Print subtree.
 | 
        
           |  |  | 4186 |         if ($tabobject->level == 0 || $tabobject->selected || $tabobject->activated) {
 | 
        
           |  |  | 4187 |             $str .= html_writer::start_tag('ul', array('class' => 'tabrow'. $tabobject->level));
 | 
        
           |  |  | 4188 |             $cnt = 0;
 | 
        
           |  |  | 4189 |             foreach ($tabobject->subtree as $tab) {
 | 
        
           |  |  | 4190 |                 $liclass = '';
 | 
        
           |  |  | 4191 |                 if (!$cnt) {
 | 
        
           |  |  | 4192 |                     $liclass .= ' first';
 | 
        
           |  |  | 4193 |                 }
 | 
        
           |  |  | 4194 |                 if ($cnt == count($tabobject->subtree) - 1) {
 | 
        
           |  |  | 4195 |                     $liclass .= ' last';
 | 
        
           |  |  | 4196 |                 }
 | 
        
           |  |  | 4197 |                 if ((empty($tab->subtree)) && (!empty($tab->selected))) {
 | 
        
           |  |  | 4198 |                     $liclass .= ' onerow';
 | 
        
           |  |  | 4199 |                 }
 | 
        
           |  |  | 4200 |   | 
        
           |  |  | 4201 |                 if ($tab->selected) {
 | 
        
           |  |  | 4202 |                     $liclass .= ' here selected';
 | 
        
           |  |  | 4203 |                 } else if ($tab->activated) {
 | 
        
           |  |  | 4204 |                     $liclass .= ' here active';
 | 
        
           |  |  | 4205 |                 }
 | 
        
           |  |  | 4206 |   | 
        
           |  |  | 4207 |                 // This will recursively call function render_tabobject() for each item in subtree.
 | 
        
           |  |  | 4208 |                 $str .= html_writer::tag('li', $this->render($tab), array('class' => trim($liclass)));
 | 
        
           |  |  | 4209 |                 $cnt++;
 | 
        
           |  |  | 4210 |             }
 | 
        
           |  |  | 4211 |             $str .= html_writer::end_tag('ul');
 | 
        
           |  |  | 4212 |         }
 | 
        
           |  |  | 4213 |   | 
        
           |  |  | 4214 |         return $str;
 | 
        
           |  |  | 4215 |     }
 | 
        
           |  |  | 4216 |   | 
        
           |  |  | 4217 |     /**
 | 
        
           |  |  | 4218 |      * Get the HTML for blocks in the given region.
 | 
        
           |  |  | 4219 |      *
 | 
        
           |  |  | 4220 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4221 |      * @param string $region The region to get HTML for.
 | 
        
           |  |  | 4222 |      * @param array $classes Wrapping tag classes.
 | 
        
           |  |  | 4223 |      * @param string $tag Wrapping tag.
 | 
        
           |  |  | 4224 |      * @param boolean $fakeblocksonly Include fake blocks only.
 | 
        
           |  |  | 4225 |      * @return string HTML.
 | 
        
           |  |  | 4226 |      */
 | 
        
           |  |  | 4227 |     public function blocks($region, $classes = array(), $tag = 'aside', $fakeblocksonly = false) {
 | 
        
           |  |  | 4228 |         $displayregion = $this->page->apply_theme_region_manipulations($region);
 | 
        
           |  |  | 4229 |         $classes = (array)$classes;
 | 
        
           |  |  | 4230 |         $classes[] = 'block-region';
 | 
        
           |  |  | 4231 |         $attributes = array(
 | 
        
           |  |  | 4232 |             'id' => 'block-region-'.preg_replace('#[^a-zA-Z0-9_\-]+#', '-', $displayregion),
 | 
        
           |  |  | 4233 |             'class' => join(' ', $classes),
 | 
        
           |  |  | 4234 |             'data-blockregion' => $displayregion,
 | 
        
           |  |  | 4235 |             'data-droptarget' => '1'
 | 
        
           |  |  | 4236 |         );
 | 
        
           |  |  | 4237 |         if ($this->page->blocks->region_has_content($displayregion, $this)) {
 | 
        
           |  |  | 4238 |             $content = html_writer::tag('h2', get_string('blocks'), ['class' => 'sr-only']) .
 | 
        
           |  |  | 4239 |                 $this->blocks_for_region($displayregion, $fakeblocksonly);
 | 
        
           |  |  | 4240 |         } else {
 | 
        
           |  |  | 4241 |             $content = html_writer::tag('h2', get_string('blocks'), ['class' => 'sr-only']);
 | 
        
           |  |  | 4242 |         }
 | 
        
           |  |  | 4243 |         return html_writer::tag($tag, $content, $attributes);
 | 
        
           |  |  | 4244 |     }
 | 
        
           |  |  | 4245 |   | 
        
           |  |  | 4246 |     /**
 | 
        
           |  |  | 4247 |      * Renders a custom block region.
 | 
        
           |  |  | 4248 |      *
 | 
        
           |  |  | 4249 |      * Use this method if you want to add an additional block region to the content of the page.
 | 
        
           |  |  | 4250 |      * Please note this should only be used in special situations.
 | 
        
           |  |  | 4251 |      * We want to leave the theme is control where ever possible!
 | 
        
           |  |  | 4252 |      *
 | 
        
           |  |  | 4253 |      * This method must use the same method that the theme uses within its layout file.
 | 
        
           |  |  | 4254 |      * As such it asks the theme what method it is using.
 | 
        
           |  |  | 4255 |      * It can be one of two values, blocks or blocks_for_region (deprecated).
 | 
        
           |  |  | 4256 |      *
 | 
        
           |  |  | 4257 |      * @param string $regionname The name of the custom region to add.
 | 
        
           |  |  | 4258 |      * @return string HTML for the block region.
 | 
        
           |  |  | 4259 |      */
 | 
        
           |  |  | 4260 |     public function custom_block_region($regionname) {
 | 
        
           |  |  | 4261 |         if ($this->page->theme->get_block_render_method() === 'blocks') {
 | 
        
           |  |  | 4262 |             return $this->blocks($regionname);
 | 
        
           |  |  | 4263 |         } else {
 | 
        
           |  |  | 4264 |             return $this->blocks_for_region($regionname);
 | 
        
           |  |  | 4265 |         }
 | 
        
           |  |  | 4266 |     }
 | 
        
           |  |  | 4267 |   | 
        
           |  |  | 4268 |     /**
 | 
        
           |  |  | 4269 |      * Returns the CSS classes to apply to the body tag.
 | 
        
           |  |  | 4270 |      *
 | 
        
           |  |  | 4271 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4272 |      * @param array $additionalclasses Any additional classes to apply.
 | 
        
           |  |  | 4273 |      * @return string
 | 
        
           |  |  | 4274 |      */
 | 
        
           |  |  | 4275 |     public function body_css_classes(array $additionalclasses = array()) {
 | 
        
           |  |  | 4276 |         return $this->page->bodyclasses . ' ' . implode(' ', $additionalclasses);
 | 
        
           |  |  | 4277 |     }
 | 
        
           |  |  | 4278 |   | 
        
           |  |  | 4279 |     /**
 | 
        
           |  |  | 4280 |      * The ID attribute to apply to the body tag.
 | 
        
           |  |  | 4281 |      *
 | 
        
           |  |  | 4282 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4283 |      * @return string
 | 
        
           |  |  | 4284 |      */
 | 
        
           |  |  | 4285 |     public function body_id() {
 | 
        
           |  |  | 4286 |         return $this->page->bodyid;
 | 
        
           |  |  | 4287 |     }
 | 
        
           |  |  | 4288 |   | 
        
           |  |  | 4289 |     /**
 | 
        
           |  |  | 4290 |      * Returns HTML attributes to use within the body tag. This includes an ID and classes.
 | 
        
           |  |  | 4291 |      *
 | 
        
           |  |  | 4292 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4293 |      * @param string|array $additionalclasses Any additional classes to give the body tag,
 | 
        
           |  |  | 4294 |      * @return string
 | 
        
           |  |  | 4295 |      */
 | 
        
           |  |  | 4296 |     public function body_attributes($additionalclasses = array()) {
 | 
        
           |  |  | 4297 |         if (!is_array($additionalclasses)) {
 | 
        
           |  |  | 4298 |             $additionalclasses = explode(' ', $additionalclasses);
 | 
        
           |  |  | 4299 |         }
 | 
        
           |  |  | 4300 |         return ' id="'. $this->body_id().'" class="'.$this->body_css_classes($additionalclasses).'"';
 | 
        
           |  |  | 4301 |     }
 | 
        
           |  |  | 4302 |   | 
        
           |  |  | 4303 |     /**
 | 
        
           |  |  | 4304 |      * Gets HTML for the page heading.
 | 
        
           |  |  | 4305 |      *
 | 
        
           |  |  | 4306 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4307 |      * @param string $tag The tag to encase the heading in. h1 by default.
 | 
        
           |  |  | 4308 |      * @return string HTML.
 | 
        
           |  |  | 4309 |      */
 | 
        
           |  |  | 4310 |     public function page_heading($tag = 'h1') {
 | 
        
           |  |  | 4311 |         return html_writer::tag($tag, $this->page->heading);
 | 
        
           |  |  | 4312 |     }
 | 
        
           |  |  | 4313 |   | 
        
           |  |  | 4314 |     /**
 | 
        
           |  |  | 4315 |      * Gets the HTML for the page heading button.
 | 
        
           |  |  | 4316 |      *
 | 
        
           |  |  | 4317 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4318 |      * @return string HTML.
 | 
        
           |  |  | 4319 |      */
 | 
        
           |  |  | 4320 |     public function page_heading_button() {
 | 
        
           |  |  | 4321 |         return $this->page->button;
 | 
        
           |  |  | 4322 |     }
 | 
        
           |  |  | 4323 |   | 
        
           |  |  | 4324 |     /**
 | 
        
           |  |  | 4325 |      * Returns the Moodle docs link to use for this page.
 | 
        
           |  |  | 4326 |      *
 | 
        
           |  |  | 4327 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4328 |      * @param string $text
 | 
        
           |  |  | 4329 |      * @return string
 | 
        
           |  |  | 4330 |      */
 | 
        
           |  |  | 4331 |     public function page_doc_link($text = null) {
 | 
        
           |  |  | 4332 |         if ($text === null) {
 | 
        
           |  |  | 4333 |             $text = get_string('moodledocslink');
 | 
        
           |  |  | 4334 |         }
 | 
        
           |  |  | 4335 |         $path = page_get_doc_link_path($this->page);
 | 
        
           |  |  | 4336 |         if (!$path) {
 | 
        
           |  |  | 4337 |             return '';
 | 
        
           |  |  | 4338 |         }
 | 
        
           |  |  | 4339 |         return $this->doc_link($path, $text);
 | 
        
           |  |  | 4340 |     }
 | 
        
           |  |  | 4341 |   | 
        
           |  |  | 4342 |     /**
 | 
        
           |  |  | 4343 |      * Returns the HTML for the site support email link
 | 
        
           |  |  | 4344 |      *
 | 
        
           |  |  | 4345 |      * @param array $customattribs Array of custom attributes for the support email anchor tag.
 | 
        
           |  |  | 4346 |      * @param bool $embed Set to true if you want to embed the link in other inline content.
 | 
        
           |  |  | 4347 |      * @return string The html code for the support email link.
 | 
        
           |  |  | 4348 |      */
 | 
        
           |  |  | 4349 |     public function supportemail(array $customattribs = [], bool $embed = false): string {
 | 
        
           |  |  | 4350 |         global $CFG;
 | 
        
           |  |  | 4351 |   | 
        
           |  |  | 4352 |         // Do not provide a link to contact site support if it is unavailable to this user. This would be where the site has
 | 
        
           |  |  | 4353 |         // disabled support, or limited it to authenticated users and the current user is a guest or not logged in.
 | 
        
           |  |  | 4354 |         if (!isset($CFG->supportavailability) ||
 | 
        
           |  |  | 4355 |                 $CFG->supportavailability == CONTACT_SUPPORT_DISABLED ||
 | 
        
           |  |  | 4356 |                 ($CFG->supportavailability == CONTACT_SUPPORT_AUTHENTICATED && (!isloggedin() || isguestuser()))) {
 | 
        
           |  |  | 4357 |             return '';
 | 
        
           |  |  | 4358 |         }
 | 
        
           |  |  | 4359 |   | 
        
           |  |  | 4360 |         $label = get_string('contactsitesupport', 'admin');
 | 
        
           |  |  | 4361 |         $icon = $this->pix_icon('t/email', '');
 | 
        
           |  |  | 4362 |   | 
        
           |  |  | 4363 |         if (!$embed) {
 | 
        
           |  |  | 4364 |             $content = $icon . $label;
 | 
        
           |  |  | 4365 |         } else {
 | 
        
           |  |  | 4366 |             $content = $label;
 | 
        
           |  |  | 4367 |         }
 | 
        
           |  |  | 4368 |   | 
        
           |  |  | 4369 |         if (!empty($CFG->supportpage)) {
 | 
        
           |  |  | 4370 |             $attributes = ['href' => $CFG->supportpage, 'target' => 'blank'];
 | 
        
           |  |  | 4371 |             $content .= $this->pix_icon('i/externallink', '', 'moodle', ['class' => 'ml-1']);
 | 
        
           |  |  | 4372 |         } else {
 | 
        
           |  |  | 4373 |             $attributes = ['href' => $CFG->wwwroot . '/user/contactsitesupport.php'];
 | 
        
           |  |  | 4374 |         }
 | 
        
           |  |  | 4375 |   | 
        
           |  |  | 4376 |         $attributes += $customattribs;
 | 
        
           |  |  | 4377 |   | 
        
           |  |  | 4378 |         return html_writer::tag('a', $content, $attributes);
 | 
        
           |  |  | 4379 |     }
 | 
        
           |  |  | 4380 |   | 
        
           |  |  | 4381 |     /**
 | 
        
           |  |  | 4382 |      * Returns the services and support link for the help pop-up.
 | 
        
           |  |  | 4383 |      *
 | 
        
           |  |  | 4384 |      * @return string
 | 
        
           |  |  | 4385 |      */
 | 
        
           |  |  | 4386 |     public function services_support_link(): string {
 | 
        
           |  |  | 4387 |         global $CFG;
 | 
        
           |  |  | 4388 |   | 
        
           |  |  | 4389 |         if (during_initial_install() ||
 | 
        
           |  |  | 4390 |             (isset($CFG->showservicesandsupportcontent) && $CFG->showservicesandsupportcontent == false) ||
 | 
        
           |  |  | 4391 |             !is_siteadmin()) {
 | 
        
           |  |  | 4392 |             return '';
 | 
        
           |  |  | 4393 |         }
 | 
        
           |  |  | 4394 |   | 
        
           |  |  | 4395 |         $liferingicon = $this->pix_icon('t/life-ring', '', 'moodle', ['class' => 'fa fa-life-ring']);
 | 
        
           |  |  | 4396 |         $newwindowicon = $this->pix_icon('i/externallink', get_string('opensinnewwindow'), 'moodle', ['class' => 'ml-1']);
 | 
        
           |  |  | 4397 |         $link = !empty($CFG->servicespage)
 | 
        
           |  |  | 4398 |             ? $CFG->servicespage
 | 
        
           |  |  | 4399 |             : 'https://moodle.com/help/?utm_source=CTA-banner&utm_medium=platform&utm_campaign=name~Moodle4+cat~lms+mp~no';
 | 
        
           |  |  | 4400 |         $content = $liferingicon . get_string('moodleservicesandsupport') . $newwindowicon;
 | 
        
           |  |  | 4401 |   | 
        
           |  |  | 4402 |         return html_writer::tag('a', $content, ['target' => '_blank', 'href' => $link]);
 | 
        
           |  |  | 4403 |     }
 | 
        
           |  |  | 4404 |   | 
        
           |  |  | 4405 |     /**
 | 
        
           |  |  | 4406 |      * Helper function to decide whether to show the help popover header or not.
 | 
        
           |  |  | 4407 |      *
 | 
        
           |  |  | 4408 |      * @return bool
 | 
        
           |  |  | 4409 |      */
 | 
        
           |  |  | 4410 |     public function has_popover_links(): bool {
 | 
        
           |  |  | 4411 |         return !empty($this->services_support_link()) || !empty($this->page_doc_link()) || !empty($this->supportemail());
 | 
        
           |  |  | 4412 |     }
 | 
        
           |  |  | 4413 |   | 
        
           |  |  | 4414 |     /**
 | 
        
           |  |  | 4415 |      * Helper function to decide whether to show the communication link or not.
 | 
        
           |  |  | 4416 |      *
 | 
        
           |  |  | 4417 |      * @return bool
 | 
        
           |  |  | 4418 |      */
 | 
        
           |  |  | 4419 |     public function has_communication_links(): bool {
 | 
        
           |  |  | 4420 |         if (during_initial_install() || !core_communication\api::is_available()) {
 | 
        
           |  |  | 4421 |             return false;
 | 
        
           |  |  | 4422 |         }
 | 
        
           |  |  | 4423 |         return !empty($this->communication_link());
 | 
        
           |  |  | 4424 |     }
 | 
        
           |  |  | 4425 |   | 
        
           |  |  | 4426 |     /**
 | 
        
           |  |  | 4427 |      * Returns the communication link, complete with html.
 | 
        
           |  |  | 4428 |      *
 | 
        
           |  |  | 4429 |      * @return string
 | 
        
           |  |  | 4430 |      */
 | 
        
           |  |  | 4431 |     public function communication_link(): string {
 | 
        
           |  |  | 4432 |         $link = $this->communication_url() ?? '';
 | 
        
           |  |  | 4433 |         $commicon = $this->pix_icon('t/messages-o', '', 'moodle', ['class' => 'fa fa-comments']);
 | 
        
           |  |  | 4434 |         $newwindowicon = $this->pix_icon('i/externallink', get_string('opensinnewwindow'), 'moodle', ['class' => 'ml-1']);
 | 
        
           |  |  | 4435 |         $content = $commicon . get_string('communicationroomlink', 'course') . $newwindowicon;
 | 
        
           |  |  | 4436 |         $html = html_writer::tag('a', $content, ['target' => '_blank', 'href' => $link]);
 | 
        
           |  |  | 4437 |   | 
        
           |  |  | 4438 |         return !empty($link) ? $html : '';
 | 
        
           |  |  | 4439 |     }
 | 
        
           |  |  | 4440 |   | 
        
           |  |  | 4441 |     /**
 | 
        
           |  |  | 4442 |      * Returns the communication url for a given instance if it exists.
 | 
        
           |  |  | 4443 |      *
 | 
        
           |  |  | 4444 |      * @return string
 | 
        
           |  |  | 4445 |      */
 | 
        
           |  |  | 4446 |     public function communication_url(): string {
 | 
        
           |  |  | 4447 |         global $COURSE;
 | 
        
           |  |  | 4448 |         return \core_communication\helper::get_course_communication_url($COURSE);
 | 
        
           |  |  | 4449 |     }
 | 
        
           |  |  | 4450 |   | 
        
           |  |  | 4451 |     /**
 | 
        
           |  |  | 4452 |      * Returns the page heading menu.
 | 
        
           |  |  | 4453 |      *
 | 
        
           |  |  | 4454 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4455 |      * @return string HTML.
 | 
        
           |  |  | 4456 |      */
 | 
        
           |  |  | 4457 |     public function page_heading_menu() {
 | 
        
           |  |  | 4458 |         return $this->page->headingmenu;
 | 
        
           |  |  | 4459 |     }
 | 
        
           |  |  | 4460 |   | 
        
           |  |  | 4461 |     /**
 | 
        
           |  |  | 4462 |      * Returns the title to use on the page.
 | 
        
           |  |  | 4463 |      *
 | 
        
           |  |  | 4464 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4465 |      * @return string
 | 
        
           |  |  | 4466 |      */
 | 
        
           |  |  | 4467 |     public function page_title() {
 | 
        
           |  |  | 4468 |         return $this->page->title;
 | 
        
           |  |  | 4469 |     }
 | 
        
           |  |  | 4470 |   | 
        
           |  |  | 4471 |     /**
 | 
        
           |  |  | 4472 |      * Returns the moodle_url for the favicon.
 | 
        
           |  |  | 4473 |      *
 | 
        
           |  |  | 4474 |      * @since Moodle 2.5.1 2.6
 | 
        
           |  |  | 4475 |      * @return moodle_url The moodle_url for the favicon
 | 
        
           |  |  | 4476 |      */
 | 
        
           |  |  | 4477 |     public function favicon() {
 | 
        
           |  |  | 4478 |         $logo = null;
 | 
        
           |  |  | 4479 |         if (!during_initial_install()) {
 | 
        
           |  |  | 4480 |             $logo = get_config('core_admin', 'favicon');
 | 
        
           |  |  | 4481 |         }
 | 
        
           |  |  | 4482 |         if (empty($logo)) {
 | 
        
           |  |  | 4483 |             return $this->image_url('favicon', 'theme');
 | 
        
           |  |  | 4484 |         }
 | 
        
           |  |  | 4485 |   | 
        
           |  |  | 4486 |         // Use $CFG->themerev to prevent browser caching when the file changes.
 | 
        
           |  |  | 4487 |         return moodle_url::make_pluginfile_url(context_system::instance()->id, 'core_admin', 'favicon', '64x64/',
 | 
        
           |  |  | 4488 |             theme_get_revision(), $logo);
 | 
        
           |  |  | 4489 |     }
 | 
        
           |  |  | 4490 |   | 
        
           |  |  | 4491 |     /**
 | 
        
           |  |  | 4492 |      * Renders preferences groups.
 | 
        
           |  |  | 4493 |      *
 | 
        
           |  |  | 4494 |      * @param  preferences_groups $renderable The renderable
 | 
        
           |  |  | 4495 |      * @return string The output.
 | 
        
           |  |  | 4496 |      */
 | 
        
           |  |  | 4497 |     public function render_preferences_groups(preferences_groups $renderable) {
 | 
        
           |  |  | 4498 |         return $this->render_from_template('core/preferences_groups', $renderable);
 | 
        
           |  |  | 4499 |     }
 | 
        
           |  |  | 4500 |   | 
        
           |  |  | 4501 |     /**
 | 
        
           |  |  | 4502 |      * Renders preferences group.
 | 
        
           |  |  | 4503 |      *
 | 
        
           |  |  | 4504 |      * @param  preferences_group $renderable The renderable
 | 
        
           |  |  | 4505 |      * @return string The output.
 | 
        
           |  |  | 4506 |      */
 | 
        
           |  |  | 4507 |     public function render_preferences_group(preferences_group $renderable) {
 | 
        
           |  |  | 4508 |         $html = '';
 | 
        
           |  |  | 4509 |         $html .= html_writer::start_tag('div', array('class' => 'col-sm-4 preferences-group'));
 | 
        
           |  |  | 4510 |         $html .= $this->heading($renderable->title, 3);
 | 
        
           |  |  | 4511 |         $html .= html_writer::start_tag('ul');
 | 
        
           |  |  | 4512 |         foreach ($renderable->nodes as $node) {
 | 
        
           |  |  | 4513 |             if ($node->has_children()) {
 | 
        
           |  |  | 4514 |                 debugging('Preferences nodes do not support children', DEBUG_DEVELOPER);
 | 
        
           |  |  | 4515 |             }
 | 
        
           |  |  | 4516 |             $html .= html_writer::tag('li', $this->render($node));
 | 
        
           |  |  | 4517 |         }
 | 
        
           |  |  | 4518 |         $html .= html_writer::end_tag('ul');
 | 
        
           |  |  | 4519 |         $html .= html_writer::end_tag('div');
 | 
        
           |  |  | 4520 |         return $html;
 | 
        
           |  |  | 4521 |     }
 | 
        
           |  |  | 4522 |   | 
        
           |  |  | 4523 |     public function context_header($headerinfo = null, $headinglevel = 1) {
 | 
        
           |  |  | 4524 |         global $DB, $USER, $CFG, $SITE;
 | 
        
           |  |  | 4525 |         require_once($CFG->dirroot . '/user/lib.php');
 | 
        
           |  |  | 4526 |         $context = $this->page->context;
 | 
        
           |  |  | 4527 |         $heading = null;
 | 
        
           |  |  | 4528 |         $imagedata = null;
 | 
        
           |  |  | 4529 |         $subheader = null;
 | 
        
           |  |  | 4530 |         $userbuttons = null;
 | 
        
           |  |  | 4531 |   | 
        
           |  |  | 4532 |         // Make sure to use the heading if it has been set.
 | 
        
           |  |  | 4533 |         if (isset($headerinfo['heading'])) {
 | 
        
           |  |  | 4534 |             $heading = $headerinfo['heading'];
 | 
        
           |  |  | 4535 |         } else {
 | 
        
           |  |  | 4536 |             $heading = $this->page->heading;
 | 
        
           |  |  | 4537 |         }
 | 
        
           |  |  | 4538 |   | 
        
           |  |  | 4539 |         // The user context currently has images and buttons. Other contexts may follow.
 | 
        
           |  |  | 4540 |         if ((isset($headerinfo['user']) || $context->contextlevel == CONTEXT_USER) && $this->page->pagetype !== 'my-index') {
 | 
        
           |  |  | 4541 |             if (isset($headerinfo['user'])) {
 | 
        
           |  |  | 4542 |                 $user = $headerinfo['user'];
 | 
        
           |  |  | 4543 |             } else {
 | 
        
           |  |  | 4544 |                 // Look up the user information if it is not supplied.
 | 
        
           |  |  | 4545 |                 $user = $DB->get_record('user', array('id' => $context->instanceid));
 | 
        
           |  |  | 4546 |             }
 | 
        
           |  |  | 4547 |   | 
        
           |  |  | 4548 |             // If the user context is set, then use that for capability checks.
 | 
        
           |  |  | 4549 |             if (isset($headerinfo['usercontext'])) {
 | 
        
           |  |  | 4550 |                 $context = $headerinfo['usercontext'];
 | 
        
           |  |  | 4551 |             }
 | 
        
           |  |  | 4552 |   | 
        
           |  |  | 4553 |             // Only provide user information if the user is the current user, or a user which the current user can view.
 | 
        
           |  |  | 4554 |             // When checking user_can_view_profile(), either:
 | 
        
           |  |  | 4555 |             // If the page context is course, check the course context (from the page object) or;
 | 
        
           |  |  | 4556 |             // If page context is NOT course, then check across all courses.
 | 
        
           |  |  | 4557 |             $course = ($this->page->context->contextlevel == CONTEXT_COURSE) ? $this->page->course : null;
 | 
        
           |  |  | 4558 |   | 
        
           |  |  | 4559 |             if (user_can_view_profile($user, $course)) {
 | 
        
           |  |  | 4560 |                 // Use the user's full name if the heading isn't set.
 | 
        
           |  |  | 4561 |                 if (empty($heading)) {
 | 
        
           |  |  | 4562 |                     $heading = fullname($user);
 | 
        
           |  |  | 4563 |                 }
 | 
        
           |  |  | 4564 |   | 
        
           |  |  | 4565 |                 $imagedata = $this->user_picture($user, array('size' => 100));
 | 
        
           |  |  | 4566 |   | 
        
           |  |  | 4567 |                 // Check to see if we should be displaying a message button.
 | 
        
           |  |  | 4568 |                 if (!empty($CFG->messaging) && has_capability('moodle/site:sendmessage', $context)) {
 | 
        
           |  |  | 4569 |                     $userbuttons = array(
 | 
        
           |  |  | 4570 |                         'messages' => array(
 | 
        
           |  |  | 4571 |                             'buttontype' => 'message',
 | 
        
           |  |  | 4572 |                             'title' => get_string('message', 'message'),
 | 
        
           |  |  | 4573 |                             'url' => new moodle_url('/message/index.php', array('id' => $user->id)),
 | 
        
           |  |  | 4574 |                             'image' => 'message',
 | 
        
           |  |  | 4575 |                             'linkattributes' => \core_message\helper::messageuser_link_params($user->id),
 | 
        
           |  |  | 4576 |                             'page' => $this->page
 | 
        
           |  |  | 4577 |                         )
 | 
        
           |  |  | 4578 |                     );
 | 
        
           |  |  | 4579 |   | 
        
           |  |  | 4580 |                     if ($USER->id != $user->id) {
 | 
        
           |  |  | 4581 |                         $iscontact = \core_message\api::is_contact($USER->id, $user->id);
 | 
        
           | 11 | efrain | 4582 |                         $isrequested = \core_message\api::get_contact_requests_between_users($USER->id, $user->id);
 | 
        
           |  |  | 4583 |                         $contacturlaction = '';
 | 
        
           |  |  | 4584 |                         $linkattributes = \core_message\helper::togglecontact_link_params(
 | 
        
           |  |  | 4585 |                             $user,
 | 
        
           |  |  | 4586 |                             $iscontact,
 | 
        
           |  |  | 4587 |                             true,
 | 
        
           |  |  | 4588 |                             !empty($isrequested),
 | 
        
           |  |  | 4589 |                         );
 | 
        
           |  |  | 4590 |                         // If the user is not a contact.
 | 
        
           |  |  | 4591 |                         if (!$iscontact) {
 | 
        
           |  |  | 4592 |                             if ($isrequested) {
 | 
        
           |  |  | 4593 |                                 // We just need the first request.
 | 
        
           |  |  | 4594 |                                 $requests = array_shift($isrequested);
 | 
        
           |  |  | 4595 |                                 if ($requests->userid == $USER->id) {
 | 
        
           |  |  | 4596 |                                     // If the user has requested to be a contact.
 | 
        
           |  |  | 4597 |                                     $contacttitle = 'contactrequestsent';
 | 
        
           |  |  | 4598 |                                 } else {
 | 
        
           |  |  | 4599 |                                     // If the user has been requested to be a contact.
 | 
        
           |  |  | 4600 |                                     $contacttitle = 'waitingforcontactaccept';
 | 
        
           |  |  | 4601 |                                 }
 | 
        
           |  |  | 4602 |                                 $linkattributes = array_merge($linkattributes, [
 | 
        
           |  |  | 4603 |                                     'class' => 'disabled',
 | 
        
           |  |  | 4604 |                                     'tabindex' => '-1',
 | 
        
           |  |  | 4605 |                                 ]);
 | 
        
           |  |  | 4606 |                             } else {
 | 
        
           |  |  | 4607 |                                 // If the user is not a contact and has not requested to be a contact.
 | 
        
           |  |  | 4608 |                                 $contacttitle = 'addtoyourcontacts';
 | 
        
           |  |  | 4609 |                                 $contacturlaction = 'addcontact';
 | 
        
           |  |  | 4610 |                             }
 | 
        
           |  |  | 4611 |                             $contactimage = 'addcontact';
 | 
        
           |  |  | 4612 |                         } else {
 | 
        
           |  |  | 4613 |                             // If the user is a contact.
 | 
        
           |  |  | 4614 |                             $contacttitle = 'removefromyourcontacts';
 | 
        
           |  |  | 4615 |                             $contacturlaction = 'removecontact';
 | 
        
           |  |  | 4616 |                             $contactimage = 'removecontact';
 | 
        
           |  |  | 4617 |                         }
 | 
        
           | 1 | efrain | 4618 |                         $userbuttons['togglecontact'] = array(
 | 
        
           |  |  | 4619 |                                 'buttontype' => 'togglecontact',
 | 
        
           |  |  | 4620 |                                 'title' => get_string($contacttitle, 'message'),
 | 
        
           |  |  | 4621 |                                 'url' => new moodle_url('/message/index.php', array(
 | 
        
           |  |  | 4622 |                                         'user1' => $USER->id,
 | 
        
           |  |  | 4623 |                                         'user2' => $user->id,
 | 
        
           |  |  | 4624 |                                         $contacturlaction => $user->id,
 | 
        
           |  |  | 4625 |                                         'sesskey' => sesskey())
 | 
        
           |  |  | 4626 |                                 ),
 | 
        
           |  |  | 4627 |                                 'image' => $contactimage,
 | 
        
           | 11 | efrain | 4628 |                                 'linkattributes' => $linkattributes,
 | 
        
           | 1 | efrain | 4629 |                                 'page' => $this->page
 | 
        
           |  |  | 4630 |                             );
 | 
        
           |  |  | 4631 |                     }
 | 
        
           |  |  | 4632 |                 }
 | 
        
           |  |  | 4633 |             } else {
 | 
        
           |  |  | 4634 |                 $heading = null;
 | 
        
           |  |  | 4635 |             }
 | 
        
           |  |  | 4636 |         }
 | 
        
           |  |  | 4637 |   | 
        
           |  |  | 4638 |         // Return the heading wrapped in an sr-only element so it is only visible to screen-readers.
 | 
        
           |  |  | 4639 |         if (!empty($this->page->layout_options['nocontextheader'])) {
 | 
        
           |  |  | 4640 |             return html_writer::div($heading, 'sr-only');
 | 
        
           |  |  | 4641 |         }
 | 
        
           |  |  | 4642 |   | 
        
           |  |  | 4643 |         $contextheader = new context_header($heading, $headinglevel, $imagedata, $userbuttons);
 | 
        
           |  |  | 4644 |         return $this->render($contextheader);
 | 
        
           |  |  | 4645 |     }
 | 
        
           |  |  | 4646 |   | 
        
           |  |  | 4647 |     /**
 | 
        
           | 11 | efrain | 4648 |      * Renders the header bar.
 | 
        
           |  |  | 4649 |      *
 | 
        
           |  |  | 4650 |      * @param context_header $contextheader Header bar object.
 | 
        
           |  |  | 4651 |      * @return string HTML for the header bar.
 | 
        
           |  |  | 4652 |      */
 | 
        
           |  |  | 4653 |     protected function render_context_header(context_header $contextheader) {
 | 
        
           |  |  | 4654 |         $context = $contextheader->export_for_template($this);
 | 
        
           |  |  | 4655 |         return $this->render_from_template('core/context_header', $context);
 | 
        
           |  |  | 4656 |     }
 | 
        
           |  |  | 4657 |   | 
        
           |  |  | 4658 |     /**
 | 
        
           | 1 | efrain | 4659 |      * Renders the skip links for the page.
 | 
        
           |  |  | 4660 |      *
 | 
        
           |  |  | 4661 |      * @param array $links List of skip links.
 | 
        
           |  |  | 4662 |      * @return string HTML for the skip links.
 | 
        
           |  |  | 4663 |      */
 | 
        
           |  |  | 4664 |     public function render_skip_links($links) {
 | 
        
           |  |  | 4665 |         $context = [ 'links' => []];
 | 
        
           |  |  | 4666 |   | 
        
           |  |  | 4667 |         foreach ($links as $url => $text) {
 | 
        
           |  |  | 4668 |             $context['links'][] = [ 'url' => $url, 'text' => $text];
 | 
        
           |  |  | 4669 |         }
 | 
        
           |  |  | 4670 |   | 
        
           |  |  | 4671 |         return $this->render_from_template('core/skip_links', $context);
 | 
        
           |  |  | 4672 |     }
 | 
        
           |  |  | 4673 |   | 
        
           |  |  | 4674 |     /**
 | 
        
           |  |  | 4675 |      * Wrapper for header elements.
 | 
        
           |  |  | 4676 |      *
 | 
        
           |  |  | 4677 |      * @return string HTML to display the main header.
 | 
        
           |  |  | 4678 |      */
 | 
        
           |  |  | 4679 |     public function full_header() {
 | 
        
           |  |  | 4680 |         $pagetype = $this->page->pagetype;
 | 
        
           |  |  | 4681 |         $homepage = get_home_page();
 | 
        
           |  |  | 4682 |         $homepagetype = null;
 | 
        
           |  |  | 4683 |         // Add a special case since /my/courses is a part of the /my subsystem.
 | 
        
           |  |  | 4684 |         if ($homepage == HOMEPAGE_MY || $homepage == HOMEPAGE_MYCOURSES) {
 | 
        
           |  |  | 4685 |             $homepagetype = 'my-index';
 | 
        
           |  |  | 4686 |         } else if ($homepage == HOMEPAGE_SITE) {
 | 
        
           |  |  | 4687 |             $homepagetype = 'site-index';
 | 
        
           |  |  | 4688 |         }
 | 
        
           |  |  | 4689 |         if ($this->page->include_region_main_settings_in_header_actions() &&
 | 
        
           |  |  | 4690 |                 !$this->page->blocks->is_block_present('settings')) {
 | 
        
           |  |  | 4691 |             // Only include the region main settings if the page has requested it and it doesn't already have
 | 
        
           |  |  | 4692 |             // the settings block on it. The region main settings are included in the settings block and
 | 
        
           |  |  | 4693 |             // duplicating the content causes behat failures.
 | 
        
           |  |  | 4694 |             $this->page->add_header_action(html_writer::div(
 | 
        
           |  |  | 4695 |                 $this->region_main_settings_menu(),
 | 
        
           |  |  | 4696 |                 'd-print-none',
 | 
        
           |  |  | 4697 |                 ['id' => 'region-main-settings-menu']
 | 
        
           |  |  | 4698 |             ));
 | 
        
           |  |  | 4699 |         }
 | 
        
           |  |  | 4700 |   | 
        
           |  |  | 4701 |         $header = new stdClass();
 | 
        
           |  |  | 4702 |         $header->settingsmenu = $this->context_header_settings_menu();
 | 
        
           |  |  | 4703 |         $header->contextheader = $this->context_header();
 | 
        
           |  |  | 4704 |         $header->hasnavbar = empty($this->page->layout_options['nonavbar']);
 | 
        
           |  |  | 4705 |         $header->navbar = $this->navbar();
 | 
        
           |  |  | 4706 |         $header->pageheadingbutton = $this->page_heading_button();
 | 
        
           |  |  | 4707 |         $header->courseheader = $this->course_header();
 | 
        
           |  |  | 4708 |         $header->headeractions = $this->page->get_header_actions();
 | 
        
           |  |  | 4709 |         if (!empty($pagetype) && !empty($homepagetype) && $pagetype == $homepagetype) {
 | 
        
           |  |  | 4710 |             $header->welcomemessage = \core_user::welcome_message();
 | 
        
           |  |  | 4711 |         }
 | 
        
           |  |  | 4712 |         return $this->render_from_template('core/full_header', $header);
 | 
        
           |  |  | 4713 |     }
 | 
        
           |  |  | 4714 |   | 
        
           |  |  | 4715 |     /**
 | 
        
           |  |  | 4716 |      * This is an optional menu that can be added to a layout by a theme. It contains the
 | 
        
           |  |  | 4717 |      * menu for the course administration, only on the course main page.
 | 
        
           |  |  | 4718 |      *
 | 
        
           |  |  | 4719 |      * @return string
 | 
        
           |  |  | 4720 |      */
 | 
        
           |  |  | 4721 |     public function context_header_settings_menu() {
 | 
        
           |  |  | 4722 |         $context = $this->page->context;
 | 
        
           |  |  | 4723 |         $menu = new action_menu();
 | 
        
           |  |  | 4724 |   | 
        
           |  |  | 4725 |         $items = $this->page->navbar->get_items();
 | 
        
           |  |  | 4726 |         $currentnode = end($items);
 | 
        
           |  |  | 4727 |   | 
        
           |  |  | 4728 |         $showcoursemenu = false;
 | 
        
           |  |  | 4729 |         $showfrontpagemenu = false;
 | 
        
           |  |  | 4730 |         $showusermenu = false;
 | 
        
           |  |  | 4731 |   | 
        
           |  |  | 4732 |         // We are on the course home page.
 | 
        
           |  |  | 4733 |         if (($context->contextlevel == CONTEXT_COURSE) &&
 | 
        
           |  |  | 4734 |                 !empty($currentnode) &&
 | 
        
           |  |  | 4735 |                 ($currentnode->type == navigation_node::TYPE_COURSE || $currentnode->type == navigation_node::TYPE_SECTION)) {
 | 
        
           |  |  | 4736 |             $showcoursemenu = true;
 | 
        
           |  |  | 4737 |         }
 | 
        
           |  |  | 4738 |   | 
        
           |  |  | 4739 |         $courseformat = course_get_format($this->page->course);
 | 
        
           |  |  | 4740 |         // This is a single activity course format, always show the course menu on the activity main page.
 | 
        
           |  |  | 4741 |         if ($context->contextlevel == CONTEXT_MODULE &&
 | 
        
           |  |  | 4742 |                 !$courseformat->has_view_page()) {
 | 
        
           |  |  | 4743 |   | 
        
           |  |  | 4744 |             $this->page->navigation->initialise();
 | 
        
           |  |  | 4745 |             $activenode = $this->page->navigation->find_active_node();
 | 
        
           |  |  | 4746 |             // If the settings menu has been forced then show the menu.
 | 
        
           |  |  | 4747 |             if ($this->page->is_settings_menu_forced()) {
 | 
        
           |  |  | 4748 |                 $showcoursemenu = true;
 | 
        
           |  |  | 4749 |             } else if (!empty($activenode) && ($activenode->type == navigation_node::TYPE_ACTIVITY ||
 | 
        
           |  |  | 4750 |                             $activenode->type == navigation_node::TYPE_RESOURCE)) {
 | 
        
           |  |  | 4751 |   | 
        
           |  |  | 4752 |                 // We only want to show the menu on the first page of the activity. This means
 | 
        
           |  |  | 4753 |                 // the breadcrumb has no additional nodes.
 | 
        
           |  |  | 4754 |                 if ($currentnode && ($currentnode->key == $activenode->key && $currentnode->type == $activenode->type)) {
 | 
        
           |  |  | 4755 |                     $showcoursemenu = true;
 | 
        
           |  |  | 4756 |                 }
 | 
        
           |  |  | 4757 |             }
 | 
        
           |  |  | 4758 |         }
 | 
        
           |  |  | 4759 |   | 
        
           |  |  | 4760 |         // This is the site front page.
 | 
        
           |  |  | 4761 |         if ($context->contextlevel == CONTEXT_COURSE &&
 | 
        
           |  |  | 4762 |                 !empty($currentnode) &&
 | 
        
           |  |  | 4763 |                 $currentnode->key === 'home') {
 | 
        
           |  |  | 4764 |             $showfrontpagemenu = true;
 | 
        
           |  |  | 4765 |         }
 | 
        
           |  |  | 4766 |   | 
        
           |  |  | 4767 |         // This is the user profile page.
 | 
        
           |  |  | 4768 |         if ($context->contextlevel == CONTEXT_USER &&
 | 
        
           |  |  | 4769 |                 !empty($currentnode) &&
 | 
        
           |  |  | 4770 |                 ($currentnode->key === 'myprofile')) {
 | 
        
           |  |  | 4771 |             $showusermenu = true;
 | 
        
           |  |  | 4772 |         }
 | 
        
           |  |  | 4773 |   | 
        
           |  |  | 4774 |         if ($showfrontpagemenu) {
 | 
        
           |  |  | 4775 |             $settingsnode = $this->page->settingsnav->find('frontpage', navigation_node::TYPE_SETTING);
 | 
        
           |  |  | 4776 |             if ($settingsnode) {
 | 
        
           |  |  | 4777 |                 // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4778 |                 $skipped = $this->build_action_menu_from_navigation($menu, $settingsnode, false, true);
 | 
        
           |  |  | 4779 |   | 
        
           |  |  | 4780 |                 // We only add a list to the full settings menu if we didn't include every node in the short menu.
 | 
        
           |  |  | 4781 |                 if ($skipped) {
 | 
        
           |  |  | 4782 |                     $text = get_string('morenavigationlinks');
 | 
        
           |  |  | 4783 |                     $url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
 | 
        
           |  |  | 4784 |                     $link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
 | 
        
           |  |  | 4785 |                     $menu->add_secondary_action($link);
 | 
        
           |  |  | 4786 |                 }
 | 
        
           |  |  | 4787 |             }
 | 
        
           |  |  | 4788 |         } else if ($showcoursemenu) {
 | 
        
           |  |  | 4789 |             $settingsnode = $this->page->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE);
 | 
        
           |  |  | 4790 |             if ($settingsnode) {
 | 
        
           |  |  | 4791 |                 // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4792 |                 $skipped = $this->build_action_menu_from_navigation($menu, $settingsnode, false, true);
 | 
        
           |  |  | 4793 |   | 
        
           |  |  | 4794 |                 // We only add a list to the full settings menu if we didn't include every node in the short menu.
 | 
        
           |  |  | 4795 |                 if ($skipped) {
 | 
        
           |  |  | 4796 |                     $text = get_string('morenavigationlinks');
 | 
        
           |  |  | 4797 |                     $url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
 | 
        
           |  |  | 4798 |                     $link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
 | 
        
           |  |  | 4799 |                     $menu->add_secondary_action($link);
 | 
        
           |  |  | 4800 |                 }
 | 
        
           |  |  | 4801 |             }
 | 
        
           |  |  | 4802 |         } else if ($showusermenu) {
 | 
        
           |  |  | 4803 |             // Get the course admin node from the settings navigation.
 | 
        
           |  |  | 4804 |             $settingsnode = $this->page->settingsnav->find('useraccount', navigation_node::TYPE_CONTAINER);
 | 
        
           |  |  | 4805 |             if ($settingsnode) {
 | 
        
           |  |  | 4806 |                 // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4807 |                 $this->build_action_menu_from_navigation($menu, $settingsnode);
 | 
        
           |  |  | 4808 |             }
 | 
        
           |  |  | 4809 |         }
 | 
        
           |  |  | 4810 |   | 
        
           |  |  | 4811 |         return $this->render($menu);
 | 
        
           |  |  | 4812 |     }
 | 
        
           |  |  | 4813 |   | 
        
           |  |  | 4814 |     /**
 | 
        
           |  |  | 4815 |      * Take a node in the nav tree and make an action menu out of it.
 | 
        
           |  |  | 4816 |      * The links are injected in the action menu.
 | 
        
           |  |  | 4817 |      *
 | 
        
           |  |  | 4818 |      * @param action_menu $menu
 | 
        
           |  |  | 4819 |      * @param navigation_node $node
 | 
        
           |  |  | 4820 |      * @param boolean $indent
 | 
        
           |  |  | 4821 |      * @param boolean $onlytopleafnodes
 | 
        
           |  |  | 4822 |      * @return boolean nodesskipped - True if nodes were skipped in building the menu
 | 
        
           |  |  | 4823 |      */
 | 
        
           |  |  | 4824 |     protected function build_action_menu_from_navigation(action_menu $menu,
 | 
        
           |  |  | 4825 |             navigation_node $node,
 | 
        
           |  |  | 4826 |             $indent = false,
 | 
        
           |  |  | 4827 |             $onlytopleafnodes = false) {
 | 
        
           |  |  | 4828 |         $skipped = false;
 | 
        
           |  |  | 4829 |         // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4830 |         foreach ($node->children as $menuitem) {
 | 
        
           |  |  | 4831 |             if ($menuitem->display) {
 | 
        
           |  |  | 4832 |                 if ($onlytopleafnodes && $menuitem->children->count()) {
 | 
        
           |  |  | 4833 |                     $skipped = true;
 | 
        
           |  |  | 4834 |                     continue;
 | 
        
           |  |  | 4835 |                 }
 | 
        
           |  |  | 4836 |                 if ($menuitem->action) {
 | 
        
           |  |  | 4837 |                     if ($menuitem->action instanceof action_link) {
 | 
        
           |  |  | 4838 |                         $link = $menuitem->action;
 | 
        
           |  |  | 4839 |                         // Give preference to setting icon over action icon.
 | 
        
           |  |  | 4840 |                         if (!empty($menuitem->icon)) {
 | 
        
           |  |  | 4841 |                             $link->icon = $menuitem->icon;
 | 
        
           |  |  | 4842 |                         }
 | 
        
           |  |  | 4843 |                     } else {
 | 
        
           |  |  | 4844 |                         $link = new action_link($menuitem->action, $menuitem->text, null, null, $menuitem->icon);
 | 
        
           |  |  | 4845 |                     }
 | 
        
           |  |  | 4846 |                 } else {
 | 
        
           |  |  | 4847 |                     if ($onlytopleafnodes) {
 | 
        
           |  |  | 4848 |                         $skipped = true;
 | 
        
           |  |  | 4849 |                         continue;
 | 
        
           |  |  | 4850 |                     }
 | 
        
           |  |  | 4851 |                     $link = new action_link(new moodle_url('#'), $menuitem->text, null, ['disabled' => true], $menuitem->icon);
 | 
        
           |  |  | 4852 |                 }
 | 
        
           |  |  | 4853 |                 if ($indent) {
 | 
        
           |  |  | 4854 |                     $link->add_class('ml-4');
 | 
        
           |  |  | 4855 |                 }
 | 
        
           |  |  | 4856 |                 if (!empty($menuitem->classes)) {
 | 
        
           |  |  | 4857 |                     $link->add_class(implode(" ", $menuitem->classes));
 | 
        
           |  |  | 4858 |                 }
 | 
        
           |  |  | 4859 |   | 
        
           |  |  | 4860 |                 $menu->add_secondary_action($link);
 | 
        
           |  |  | 4861 |                 $skipped = $skipped || $this->build_action_menu_from_navigation($menu, $menuitem, true);
 | 
        
           |  |  | 4862 |             }
 | 
        
           |  |  | 4863 |         }
 | 
        
           |  |  | 4864 |         return $skipped;
 | 
        
           |  |  | 4865 |     }
 | 
        
           |  |  | 4866 |   | 
        
           |  |  | 4867 |     /**
 | 
        
           |  |  | 4868 |      * This is an optional menu that can be added to a layout by a theme. It contains the
 | 
        
           |  |  | 4869 |      * menu for the most specific thing from the settings block. E.g. Module administration.
 | 
        
           |  |  | 4870 |      *
 | 
        
           |  |  | 4871 |      * @return string
 | 
        
           |  |  | 4872 |      */
 | 
        
           |  |  | 4873 |     public function region_main_settings_menu() {
 | 
        
           |  |  | 4874 |         $context = $this->page->context;
 | 
        
           |  |  | 4875 |         $menu = new action_menu();
 | 
        
           |  |  | 4876 |   | 
        
           |  |  | 4877 |         if ($context->contextlevel == CONTEXT_MODULE) {
 | 
        
           |  |  | 4878 |   | 
        
           |  |  | 4879 |             $this->page->navigation->initialise();
 | 
        
           |  |  | 4880 |             $node = $this->page->navigation->find_active_node();
 | 
        
           |  |  | 4881 |             $buildmenu = false;
 | 
        
           |  |  | 4882 |             // If the settings menu has been forced then show the menu.
 | 
        
           |  |  | 4883 |             if ($this->page->is_settings_menu_forced()) {
 | 
        
           |  |  | 4884 |                 $buildmenu = true;
 | 
        
           |  |  | 4885 |             } else if (!empty($node) && ($node->type == navigation_node::TYPE_ACTIVITY ||
 | 
        
           |  |  | 4886 |                             $node->type == navigation_node::TYPE_RESOURCE)) {
 | 
        
           |  |  | 4887 |   | 
        
           |  |  | 4888 |                 $items = $this->page->navbar->get_items();
 | 
        
           |  |  | 4889 |                 $navbarnode = end($items);
 | 
        
           |  |  | 4890 |                 // We only want to show the menu on the first page of the activity. This means
 | 
        
           |  |  | 4891 |                 // the breadcrumb has no additional nodes.
 | 
        
           |  |  | 4892 |                 if ($navbarnode && ($navbarnode->key === $node->key && $navbarnode->type == $node->type)) {
 | 
        
           |  |  | 4893 |                     $buildmenu = true;
 | 
        
           |  |  | 4894 |                 }
 | 
        
           |  |  | 4895 |             }
 | 
        
           |  |  | 4896 |             if ($buildmenu) {
 | 
        
           |  |  | 4897 |                 // Get the course admin node from the settings navigation.
 | 
        
           |  |  | 4898 |                 $node = $this->page->settingsnav->find('modulesettings', navigation_node::TYPE_SETTING);
 | 
        
           |  |  | 4899 |                 if ($node) {
 | 
        
           |  |  | 4900 |                     // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4901 |                     $this->build_action_menu_from_navigation($menu, $node);
 | 
        
           |  |  | 4902 |                 }
 | 
        
           |  |  | 4903 |             }
 | 
        
           |  |  | 4904 |   | 
        
           |  |  | 4905 |         } else if ($context->contextlevel == CONTEXT_COURSECAT) {
 | 
        
           |  |  | 4906 |             // For course category context, show category settings menu, if we're on the course category page.
 | 
        
           |  |  | 4907 |             if ($this->page->pagetype === 'course-index-category') {
 | 
        
           |  |  | 4908 |                 $node = $this->page->settingsnav->find('categorysettings', navigation_node::TYPE_CONTAINER);
 | 
        
           |  |  | 4909 |                 if ($node) {
 | 
        
           |  |  | 4910 |                     // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4911 |                     $this->build_action_menu_from_navigation($menu, $node);
 | 
        
           |  |  | 4912 |                 }
 | 
        
           |  |  | 4913 |             }
 | 
        
           |  |  | 4914 |   | 
        
           |  |  | 4915 |         } else {
 | 
        
           |  |  | 4916 |             $items = $this->page->navbar->get_items();
 | 
        
           |  |  | 4917 |             $navbarnode = end($items);
 | 
        
           |  |  | 4918 |   | 
        
           |  |  | 4919 |             if ($navbarnode && ($navbarnode->key === 'participants')) {
 | 
        
           |  |  | 4920 |                 $node = $this->page->settingsnav->find('users', navigation_node::TYPE_CONTAINER);
 | 
        
           |  |  | 4921 |                 if ($node) {
 | 
        
           |  |  | 4922 |                     // Build an action menu based on the visible nodes from this navigation tree.
 | 
        
           |  |  | 4923 |                     $this->build_action_menu_from_navigation($menu, $node);
 | 
        
           |  |  | 4924 |                 }
 | 
        
           |  |  | 4925 |   | 
        
           |  |  | 4926 |             }
 | 
        
           |  |  | 4927 |         }
 | 
        
           |  |  | 4928 |         return $this->render($menu);
 | 
        
           |  |  | 4929 |     }
 | 
        
           |  |  | 4930 |   | 
        
           |  |  | 4931 |     /**
 | 
        
           |  |  | 4932 |      * Displays the list of tags associated with an entry
 | 
        
           |  |  | 4933 |      *
 | 
        
           |  |  | 4934 |      * @param array $tags list of instances of core_tag or stdClass
 | 
        
           |  |  | 4935 |      * @param string $label label to display in front, by default 'Tags' (get_string('tags')), set to null
 | 
        
           |  |  | 4936 |      *               to use default, set to '' (empty string) to omit the label completely
 | 
        
           |  |  | 4937 |      * @param string $classes additional classes for the enclosing div element
 | 
        
           |  |  | 4938 |      * @param int $limit limit the number of tags to display, if size of $tags is more than this limit the "more" link
 | 
        
           |  |  | 4939 |      *               will be appended to the end, JS will toggle the rest of the tags
 | 
        
           |  |  | 4940 |      * @param context $pagecontext specify if needed to overwrite the current page context for the view tag link
 | 
        
           |  |  | 4941 |      * @param bool $accesshidelabel if true, the label should have class="accesshide" added.
 | 
        
           |  |  | 4942 |      * @return string
 | 
        
           |  |  | 4943 |      */
 | 
        
           |  |  | 4944 |     public function tag_list($tags, $label = null, $classes = '', $limit = 10,
 | 
        
           |  |  | 4945 |             $pagecontext = null, $accesshidelabel = false) {
 | 
        
           |  |  | 4946 |         $list = new \core_tag\output\taglist($tags, $label, $classes, $limit, $pagecontext, $accesshidelabel);
 | 
        
           |  |  | 4947 |         return $this->render_from_template('core_tag/taglist', $list->export_for_template($this));
 | 
        
           |  |  | 4948 |     }
 | 
        
           |  |  | 4949 |   | 
        
           |  |  | 4950 |     /**
 | 
        
           |  |  | 4951 |      * Renders element for inline editing of any value
 | 
        
           |  |  | 4952 |      *
 | 
        
           |  |  | 4953 |      * @param \core\output\inplace_editable $element
 | 
        
           |  |  | 4954 |      * @return string
 | 
        
           |  |  | 4955 |      */
 | 
        
           |  |  | 4956 |     public function render_inplace_editable(\core\output\inplace_editable $element) {
 | 
        
           |  |  | 4957 |         return $this->render_from_template('core/inplace_editable', $element->export_for_template($this));
 | 
        
           |  |  | 4958 |     }
 | 
        
           |  |  | 4959 |   | 
        
           |  |  | 4960 |     /**
 | 
        
           |  |  | 4961 |      * Renders a bar chart.
 | 
        
           |  |  | 4962 |      *
 | 
        
           |  |  | 4963 |      * @param \core\chart_bar $chart The chart.
 | 
        
           |  |  | 4964 |      * @return string
 | 
        
           |  |  | 4965 |      */
 | 
        
           |  |  | 4966 |     public function render_chart_bar(\core\chart_bar $chart) {
 | 
        
           |  |  | 4967 |         return $this->render_chart($chart);
 | 
        
           |  |  | 4968 |     }
 | 
        
           |  |  | 4969 |   | 
        
           |  |  | 4970 |     /**
 | 
        
           |  |  | 4971 |      * Renders a line chart.
 | 
        
           |  |  | 4972 |      *
 | 
        
           |  |  | 4973 |      * @param \core\chart_line $chart The chart.
 | 
        
           |  |  | 4974 |      * @return string
 | 
        
           |  |  | 4975 |      */
 | 
        
           |  |  | 4976 |     public function render_chart_line(\core\chart_line $chart) {
 | 
        
           |  |  | 4977 |         return $this->render_chart($chart);
 | 
        
           |  |  | 4978 |     }
 | 
        
           |  |  | 4979 |   | 
        
           |  |  | 4980 |     /**
 | 
        
           |  |  | 4981 |      * Renders a pie chart.
 | 
        
           |  |  | 4982 |      *
 | 
        
           |  |  | 4983 |      * @param \core\chart_pie $chart The chart.
 | 
        
           |  |  | 4984 |      * @return string
 | 
        
           |  |  | 4985 |      */
 | 
        
           |  |  | 4986 |     public function render_chart_pie(\core\chart_pie $chart) {
 | 
        
           |  |  | 4987 |         return $this->render_chart($chart);
 | 
        
           |  |  | 4988 |     }
 | 
        
           |  |  | 4989 |   | 
        
           |  |  | 4990 |     /**
 | 
        
           |  |  | 4991 |      * Renders a chart.
 | 
        
           |  |  | 4992 |      *
 | 
        
           |  |  | 4993 |      * @param \core\chart_base $chart The chart.
 | 
        
           |  |  | 4994 |      * @param bool $withtable Whether to include a data table with the chart.
 | 
        
           |  |  | 4995 |      * @return string
 | 
        
           |  |  | 4996 |      */
 | 
        
           |  |  | 4997 |     public function render_chart(\core\chart_base $chart, $withtable = true) {
 | 
        
           |  |  | 4998 |         $chartdata = json_encode($chart);
 | 
        
           |  |  | 4999 |         return $this->render_from_template('core/chart', (object) [
 | 
        
           |  |  | 5000 |             'chartdata' => $chartdata,
 | 
        
           |  |  | 5001 |             'withtable' => $withtable
 | 
        
           |  |  | 5002 |         ]);
 | 
        
           |  |  | 5003 |     }
 | 
        
           |  |  | 5004 |   | 
        
           |  |  | 5005 |     /**
 | 
        
           |  |  | 5006 |      * Renders the login form.
 | 
        
           |  |  | 5007 |      *
 | 
        
           |  |  | 5008 |      * @param \core_auth\output\login $form The renderable.
 | 
        
           |  |  | 5009 |      * @return string
 | 
        
           |  |  | 5010 |      */
 | 
        
           |  |  | 5011 |     public function render_login(\core_auth\output\login $form) {
 | 
        
           |  |  | 5012 |         global $CFG, $SITE;
 | 
        
           |  |  | 5013 |   | 
        
           |  |  | 5014 |         $context = $form->export_for_template($this);
 | 
        
           |  |  | 5015 |   | 
        
           |  |  | 5016 |         $context->errorformatted = $this->error_text($context->error);
 | 
        
           |  |  | 5017 |         $url = $this->get_logo_url();
 | 
        
           |  |  | 5018 |         if ($url) {
 | 
        
           |  |  | 5019 |             $url = $url->out(false);
 | 
        
           |  |  | 5020 |         }
 | 
        
           |  |  | 5021 |         $context->logourl = $url;
 | 
        
           |  |  | 5022 |         $context->sitename = format_string($SITE->fullname, true,
 | 
        
           |  |  | 5023 |                 ['context' => context_course::instance(SITEID), "escape" => false]);
 | 
        
           |  |  | 5024 |   | 
        
           |  |  | 5025 |         return $this->render_from_template('core/loginform', $context);
 | 
        
           |  |  | 5026 |     }
 | 
        
           |  |  | 5027 |   | 
        
           |  |  | 5028 |     /**
 | 
        
           |  |  | 5029 |      * Renders an mform element from a template.
 | 
        
           |  |  | 5030 |      *
 | 
        
           |  |  | 5031 |      * @param HTML_QuickForm_element $element element
 | 
        
           |  |  | 5032 |      * @param bool $required if input is required field
 | 
        
           |  |  | 5033 |      * @param bool $advanced if input is an advanced field
 | 
        
           |  |  | 5034 |      * @param string $error error message to display
 | 
        
           |  |  | 5035 |      * @param bool $ingroup True if this element is rendered as part of a group
 | 
        
           |  |  | 5036 |      * @return mixed string|bool
 | 
        
           |  |  | 5037 |      */
 | 
        
           |  |  | 5038 |     public function mform_element($element, $required, $advanced, $error, $ingroup) {
 | 
        
           |  |  | 5039 |         $templatename = 'core_form/element-' . $element->getType();
 | 
        
           |  |  | 5040 |         if ($ingroup) {
 | 
        
           |  |  | 5041 |             $templatename .= "-inline";
 | 
        
           |  |  | 5042 |         }
 | 
        
           |  |  | 5043 |         try {
 | 
        
           |  |  | 5044 |             // We call this to generate a file not found exception if there is no template.
 | 
        
           |  |  | 5045 |             // We don't want to call export_for_template if there is no template.
 | 
        
           |  |  | 5046 |             core\output\mustache_template_finder::get_template_filepath($templatename);
 | 
        
           |  |  | 5047 |   | 
        
           |  |  | 5048 |             if ($element instanceof templatable) {
 | 
        
           |  |  | 5049 |                 $elementcontext = $element->export_for_template($this);
 | 
        
           |  |  | 5050 |   | 
        
           |  |  | 5051 |                 $helpbutton = '';
 | 
        
           |  |  | 5052 |                 if (method_exists($element, 'getHelpButton')) {
 | 
        
           |  |  | 5053 |                     $helpbutton = $element->getHelpButton();
 | 
        
           |  |  | 5054 |                 }
 | 
        
           |  |  | 5055 |                 $label = $element->getLabel();
 | 
        
           |  |  | 5056 |                 $text = '';
 | 
        
           |  |  | 5057 |                 if (method_exists($element, 'getText')) {
 | 
        
           |  |  | 5058 |                     // There currently exists code that adds a form element with an empty label.
 | 
        
           |  |  | 5059 |                     // If this is the case then set the label to the description.
 | 
        
           |  |  | 5060 |                     if (empty($label)) {
 | 
        
           |  |  | 5061 |                         $label = $element->getText();
 | 
        
           |  |  | 5062 |                     } else {
 | 
        
           |  |  | 5063 |                         $text = $element->getText();
 | 
        
           |  |  | 5064 |                     }
 | 
        
           |  |  | 5065 |                 }
 | 
        
           |  |  | 5066 |   | 
        
           |  |  | 5067 |                 // Generate the form element wrapper ids and names to pass to the template.
 | 
        
           |  |  | 5068 |                 // This differs between group and non-group elements.
 | 
        
           |  |  | 5069 |                 if ($element->getType() === 'group') {
 | 
        
           |  |  | 5070 |                     // Group element.
 | 
        
           |  |  | 5071 |                     // The id will be something like 'fgroup_id_NAME'. E.g. fgroup_id_mygroup.
 | 
        
           |  |  | 5072 |                     $elementcontext['wrapperid'] = $elementcontext['id'];
 | 
        
           |  |  | 5073 |   | 
        
           |  |  | 5074 |                     // Ensure group elements pass through the group name as the element name.
 | 
        
           |  |  | 5075 |                     $elementcontext['name'] = $elementcontext['groupname'];
 | 
        
           |  |  | 5076 |                 } else {
 | 
        
           |  |  | 5077 |                     // Non grouped element.
 | 
        
           |  |  | 5078 |                     // Creates an id like 'fitem_id_NAME'. E.g. fitem_id_mytextelement.
 | 
        
           |  |  | 5079 |                     $elementcontext['wrapperid'] = 'fitem_' . $elementcontext['id'];
 | 
        
           |  |  | 5080 |                 }
 | 
        
           |  |  | 5081 |   | 
        
           |  |  | 5082 |                 $context = array(
 | 
        
           |  |  | 5083 |                     'element' => $elementcontext,
 | 
        
           |  |  | 5084 |                     'label' => $label,
 | 
        
           |  |  | 5085 |                     'text' => $text,
 | 
        
           |  |  | 5086 |                     'required' => $required,
 | 
        
           |  |  | 5087 |                     'advanced' => $advanced,
 | 
        
           |  |  | 5088 |                     'helpbutton' => $helpbutton,
 | 
        
           |  |  | 5089 |                     'error' => $error
 | 
        
           |  |  | 5090 |                 );
 | 
        
           |  |  | 5091 |                 return $this->render_from_template($templatename, $context);
 | 
        
           |  |  | 5092 |             }
 | 
        
           |  |  | 5093 |         } catch (Exception $e) {
 | 
        
           |  |  | 5094 |             // No template for this element.
 | 
        
           |  |  | 5095 |             return false;
 | 
        
           |  |  | 5096 |         }
 | 
        
           |  |  | 5097 |     }
 | 
        
           |  |  | 5098 |   | 
        
           |  |  | 5099 |     /**
 | 
        
           |  |  | 5100 |      * Render the login signup form into a nice template for the theme.
 | 
        
           |  |  | 5101 |      *
 | 
        
           |  |  | 5102 |      * @param moodleform $form
 | 
        
           |  |  | 5103 |      * @return string
 | 
        
           |  |  | 5104 |      */
 | 
        
           |  |  | 5105 |     public function render_login_signup_form($form) {
 | 
        
           |  |  | 5106 |         global $SITE;
 | 
        
           |  |  | 5107 |   | 
        
           |  |  | 5108 |         $context = $form->export_for_template($this);
 | 
        
           |  |  | 5109 |         $url = $this->get_logo_url();
 | 
        
           |  |  | 5110 |         if ($url) {
 | 
        
           |  |  | 5111 |             $url = $url->out(false);
 | 
        
           |  |  | 5112 |         }
 | 
        
           |  |  | 5113 |         $context['logourl'] = $url;
 | 
        
           |  |  | 5114 |         $context['sitename'] = format_string($SITE->fullname, true,
 | 
        
           |  |  | 5115 |                 ['context' => context_course::instance(SITEID), "escape" => false]);
 | 
        
           |  |  | 5116 |   | 
        
           |  |  | 5117 |         return $this->render_from_template('core/signup_form_layout', $context);
 | 
        
           |  |  | 5118 |     }
 | 
        
           |  |  | 5119 |   | 
        
           |  |  | 5120 |     /**
 | 
        
           |  |  | 5121 |      * Render the verify age and location page into a nice template for the theme.
 | 
        
           |  |  | 5122 |      *
 | 
        
           |  |  | 5123 |      * @param \core_auth\output\verify_age_location_page $page The renderable
 | 
        
           |  |  | 5124 |      * @return string
 | 
        
           |  |  | 5125 |      */
 | 
        
           |  |  | 5126 |     protected function render_verify_age_location_page($page) {
 | 
        
           |  |  | 5127 |         $context = $page->export_for_template($this);
 | 
        
           |  |  | 5128 |   | 
        
           |  |  | 5129 |         return $this->render_from_template('core/auth_verify_age_location_page', $context);
 | 
        
           |  |  | 5130 |     }
 | 
        
           |  |  | 5131 |   | 
        
           |  |  | 5132 |     /**
 | 
        
           |  |  | 5133 |      * Render the digital minor contact information page into a nice template for the theme.
 | 
        
           |  |  | 5134 |      *
 | 
        
           |  |  | 5135 |      * @param \core_auth\output\digital_minor_page $page The renderable
 | 
        
           |  |  | 5136 |      * @return string
 | 
        
           |  |  | 5137 |      */
 | 
        
           |  |  | 5138 |     protected function render_digital_minor_page($page) {
 | 
        
           |  |  | 5139 |         $context = $page->export_for_template($this);
 | 
        
           |  |  | 5140 |   | 
        
           |  |  | 5141 |         return $this->render_from_template('core/auth_digital_minor_page', $context);
 | 
        
           |  |  | 5142 |     }
 | 
        
           |  |  | 5143 |   | 
        
           |  |  | 5144 |     /**
 | 
        
           |  |  | 5145 |      * Renders a progress bar.
 | 
        
           |  |  | 5146 |      *
 | 
        
           |  |  | 5147 |      * Do not use $OUTPUT->render($bar), instead use progress_bar::create().
 | 
        
           |  |  | 5148 |      *
 | 
        
           |  |  | 5149 |      * @param  progress_bar $bar The bar.
 | 
        
           |  |  | 5150 |      * @return string HTML fragment
 | 
        
           |  |  | 5151 |      */
 | 
        
           |  |  | 5152 |     public function render_progress_bar(progress_bar $bar) {
 | 
        
           |  |  | 5153 |         $data = $bar->export_for_template($this);
 | 
        
           |  |  | 5154 |         return $this->render_from_template('core/progress_bar', $data);
 | 
        
           |  |  | 5155 |     }
 | 
        
           |  |  | 5156 |   | 
        
           |  |  | 5157 |     /**
 | 
        
           |  |  | 5158 |      * Renders an update to a progress bar.
 | 
        
           |  |  | 5159 |      *
 | 
        
           |  |  | 5160 |      * Note: This does not cleanly map to a renderable class and should
 | 
        
           |  |  | 5161 |      * never be used directly.
 | 
        
           |  |  | 5162 |      *
 | 
        
           |  |  | 5163 |      * @param  string $id
 | 
        
           |  |  | 5164 |      * @param  float $percent
 | 
        
           |  |  | 5165 |      * @param  string $msg Message
 | 
        
           |  |  | 5166 |      * @param  string $estimate time remaining message
 | 
        
           |  |  | 5167 |      * @return string ascii fragment
 | 
        
           |  |  | 5168 |      */
 | 
        
           |  |  | 5169 |     public function render_progress_bar_update(string $id, float $percent, string $msg, string $estimate): string {
 | 
        
           |  |  | 5170 |         return html_writer::script(js_writer::function_call('updateProgressBar', [
 | 
        
           |  |  | 5171 |             $id,
 | 
        
           |  |  | 5172 |             round($percent, 1),
 | 
        
           |  |  | 5173 |             $msg,
 | 
        
           |  |  | 5174 |             $estimate,
 | 
        
           |  |  | 5175 |         ]));
 | 
        
           |  |  | 5176 |     }
 | 
        
           |  |  | 5177 |   | 
        
           |  |  | 5178 |     /**
 | 
        
           |  |  | 5179 |      * Renders element for a toggle-all checkbox.
 | 
        
           |  |  | 5180 |      *
 | 
        
           |  |  | 5181 |      * @param \core\output\checkbox_toggleall $element
 | 
        
           |  |  | 5182 |      * @return string
 | 
        
           |  |  | 5183 |      */
 | 
        
           |  |  | 5184 |     public function render_checkbox_toggleall(\core\output\checkbox_toggleall $element) {
 | 
        
           |  |  | 5185 |         return $this->render_from_template($element->get_template(), $element->export_for_template($this));
 | 
        
           |  |  | 5186 |     }
 | 
        
           |  |  | 5187 |   | 
        
           |  |  | 5188 |     /**
 | 
        
           |  |  | 5189 |      * Renders the tertiary nav for the participants page
 | 
        
           |  |  | 5190 |      *
 | 
        
           |  |  | 5191 |      * @param object $course The course we are operating within
 | 
        
           |  |  | 5192 |      * @param string|null $renderedbuttons Any additional buttons/content to be displayed in line with the nav
 | 
        
           |  |  | 5193 |      * @return string
 | 
        
           |  |  | 5194 |      */
 | 
        
           |  |  | 5195 |     public function render_participants_tertiary_nav(object $course, ?string $renderedbuttons = null) {
 | 
        
           |  |  | 5196 |         $actionbar = new \core\output\participants_action_bar($course, $this->page, $renderedbuttons);
 | 
        
           |  |  | 5197 |         $content = $this->render_from_template('core_course/participants_actionbar', $actionbar->export_for_template($this));
 | 
        
           |  |  | 5198 |         return $content ?: "";
 | 
        
           |  |  | 5199 |     }
 | 
        
           |  |  | 5200 |   | 
        
           |  |  | 5201 |     /**
 | 
        
           |  |  | 5202 |      * Renders release information in the footer popup
 | 
        
           |  |  | 5203 |      * @return ?string Moodle release info.
 | 
        
           |  |  | 5204 |      */
 | 
        
           |  |  | 5205 |     public function moodle_release() {
 | 
        
           |  |  | 5206 |         global $CFG;
 | 
        
           |  |  | 5207 |         if (!during_initial_install() && is_siteadmin()) {
 | 
        
           |  |  | 5208 |             return $CFG->release;
 | 
        
           |  |  | 5209 |         }
 | 
        
           |  |  | 5210 |     }
 | 
        
           |  |  | 5211 |   | 
        
           |  |  | 5212 |     /**
 | 
        
           |  |  | 5213 |      * Generate the add block button when editing mode is turned on and the user can edit blocks.
 | 
        
           |  |  | 5214 |      *
 | 
        
           |  |  | 5215 |      * @param string $region where new blocks should be added.
 | 
        
           |  |  | 5216 |      * @return string html for the add block button.
 | 
        
           |  |  | 5217 |      */
 | 
        
           |  |  | 5218 |     public function addblockbutton($region = ''): string {
 | 
        
           |  |  | 5219 |         $addblockbutton = '';
 | 
        
           |  |  | 5220 |         $regions = $this->page->blocks->get_regions();
 | 
        
           |  |  | 5221 |         if (count($regions) == 0) {
 | 
        
           |  |  | 5222 |             return '';
 | 
        
           |  |  | 5223 |         }
 | 
        
           |  |  | 5224 |         if (isset($this->page->theme->addblockposition) &&
 | 
        
           |  |  | 5225 |                 $this->page->user_is_editing() &&
 | 
        
           |  |  | 5226 |                 $this->page->user_can_edit_blocks() &&
 | 
        
           |  |  | 5227 |                 $this->page->pagelayout !== 'mycourses'
 | 
        
           |  |  | 5228 |         ) {
 | 
        
           |  |  | 5229 |             $params = ['bui_addblock' => '', 'sesskey' => sesskey()];
 | 
        
           |  |  | 5230 |             if (!empty($region)) {
 | 
        
           |  |  | 5231 |                 $params['bui_blockregion'] = $region;
 | 
        
           |  |  | 5232 |             }
 | 
        
           |  |  | 5233 |             $url = new moodle_url($this->page->url, $params);
 | 
        
           |  |  | 5234 |             $addblockbutton = $this->render_from_template('core/add_block_button',
 | 
        
           |  |  | 5235 |                 [
 | 
        
           |  |  | 5236 |                     'link' => $url->out(false),
 | 
        
           |  |  | 5237 |                     'escapedlink' => "?{$url->get_query_string(false)}",
 | 
        
           |  |  | 5238 |                     'pagehash' => $this->page->get_edited_page_hash(),
 | 
        
           |  |  | 5239 |                     'blockregion' => $region,
 | 
        
           |  |  | 5240 |                     // The following parameters are not used since Moodle 4.2 but are
 | 
        
           |  |  | 5241 |                     // still passed for backward-compatibility.
 | 
        
           |  |  | 5242 |                     'pageType' => $this->page->pagetype,
 | 
        
           |  |  | 5243 |                     'pageLayout' => $this->page->pagelayout,
 | 
        
           |  |  | 5244 |                     'subPage' => $this->page->subpage,
 | 
        
           |  |  | 5245 |                 ]
 | 
        
           |  |  | 5246 |             );
 | 
        
           |  |  | 5247 |         }
 | 
        
           |  |  | 5248 |         return $addblockbutton;
 | 
        
           |  |  | 5249 |     }
 | 
        
           |  |  | 5250 |   | 
        
           |  |  | 5251 |     /**
 | 
        
           |  |  | 5252 |      * Prepares an element for streaming output
 | 
        
           |  |  | 5253 |      *
 | 
        
           |  |  | 5254 |      * This must be used with NO_OUTPUT_BUFFERING set to true. After using this method
 | 
        
           |  |  | 5255 |      * any subsequent prints or echos to STDOUT result in the outputted content magically
 | 
        
           |  |  | 5256 |      * being appended inside that element rather than where the current html would be
 | 
        
           |  |  | 5257 |      * normally. This enables pages which take some time to render incremental content to
 | 
        
           |  |  | 5258 |      * first output a fully formed html page, including the footer, and to then stream
 | 
        
           |  |  | 5259 |      * into an element such as the main content div. This fixes a class of page layout
 | 
        
           |  |  | 5260 |      * bugs and reduces layout shift issues and was inspired by Facebook BigPipe.
 | 
        
           |  |  | 5261 |      *
 | 
        
           |  |  | 5262 |      * Some use cases such as a simple page which loads content via ajax could be swapped
 | 
        
           |  |  | 5263 |      * to this method wich saves another http request and its network latency resulting
 | 
        
           |  |  | 5264 |      * in both lower server load and better front end performance.
 | 
        
           |  |  | 5265 |      *
 | 
        
           |  |  | 5266 |      * You should consider giving the element you stream into a minimum height to further
 | 
        
           |  |  | 5267 |      * reduce layout shift as the content initally streams into the element.
 | 
        
           |  |  | 5268 |      *
 | 
        
           |  |  | 5269 |      * You can safely finish the output without closing the streamed element. You can also
 | 
        
           |  |  | 5270 |      * call this method again to swap the target of the streaming to a new element as
 | 
        
           |  |  | 5271 |      * often as you want.
 | 
        
           |  |  | 5272 |   | 
        
           |  |  | 5273 |      * https://www.youtube.com/watch?v=LLRig4s1_yA&t=1022s
 | 
        
           |  |  | 5274 |      * Watch this video segment to explain how and why this 'One Weird Trick' works.
 | 
        
           |  |  | 5275 |      *
 | 
        
           |  |  | 5276 |      * @param string $selector where new content should be appended
 | 
        
           |  |  | 5277 |      * @param string $element which contains the streamed content
 | 
        
           |  |  | 5278 |      * @return string html to be written
 | 
        
           |  |  | 5279 |      */
 | 
        
           |  |  | 5280 |     public function select_element_for_append(string $selector = '#region-main [role=main]', string $element = 'div') {
 | 
        
           |  |  | 5281 |   | 
        
           |  |  | 5282 |         if (!CLI_SCRIPT && !NO_OUTPUT_BUFFERING) {
 | 
        
           |  |  | 5283 |             throw new coding_exception('select_element_for_append used in a non-CLI script without setting NO_OUTPUT_BUFFERING.',
 | 
        
           |  |  | 5284 |                 DEBUG_DEVELOPER);
 | 
        
           |  |  | 5285 |         }
 | 
        
           |  |  | 5286 |   | 
        
           |  |  | 5287 |         // We are already streaming into this element so don't change anything.
 | 
        
           |  |  | 5288 |         if ($this->currentselector === $selector && $this->currentelement === $element) {
 | 
        
           |  |  | 5289 |             return;
 | 
        
           |  |  | 5290 |         }
 | 
        
           |  |  | 5291 |   | 
        
           |  |  | 5292 |         // If we have a streaming element close it before starting a new one.
 | 
        
           |  |  | 5293 |         $html = $this->close_element_for_append();
 | 
        
           |  |  | 5294 |   | 
        
           |  |  | 5295 |         $this->currentselector = $selector;
 | 
        
           |  |  | 5296 |         $this->currentelement = $element;
 | 
        
           |  |  | 5297 |   | 
        
           |  |  | 5298 |         // Create an unclosed element for the streamed content to append into.
 | 
        
           |  |  | 5299 |         $id = uniqid();
 | 
        
           |  |  | 5300 |         $html .= html_writer::start_tag($element, ['id' => $id]);
 | 
        
           |  |  | 5301 |         $html .= html_writer::tag('script', "document.querySelector('$selector').append(document.getElementById('$id'))");
 | 
        
           |  |  | 5302 |         $html .= "\n";
 | 
        
           |  |  | 5303 |         return $html;
 | 
        
           |  |  | 5304 |     }
 | 
        
           |  |  | 5305 |   | 
        
           |  |  | 5306 |     /**
 | 
        
           |  |  | 5307 |      * This closes any opened stream elements
 | 
        
           |  |  | 5308 |      *
 | 
        
           |  |  | 5309 |      * @return string html to be written
 | 
        
           |  |  | 5310 |      */
 | 
        
           |  |  | 5311 |     public function close_element_for_append() {
 | 
        
           |  |  | 5312 |         $html = '';
 | 
        
           |  |  | 5313 |         if ($this->currentselector !== '') {
 | 
        
           |  |  | 5314 |             $html .= html_writer::end_tag($this->currentelement);
 | 
        
           |  |  | 5315 |             $html .= "\n";
 | 
        
           |  |  | 5316 |             $this->currentelement = '';
 | 
        
           |  |  | 5317 |         }
 | 
        
           |  |  | 5318 |         return $html;
 | 
        
           |  |  | 5319 |     }
 | 
        
           |  |  | 5320 |   | 
        
           |  |  | 5321 |     /**
 | 
        
           |  |  | 5322 |      * A companion method to select_element_for_append
 | 
        
           |  |  | 5323 |      *
 | 
        
           |  |  | 5324 |      * This must be used with NO_OUTPUT_BUFFERING set to true.
 | 
        
           |  |  | 5325 |      *
 | 
        
           |  |  | 5326 |      * This is similar but instead of appending into the element it replaces
 | 
        
           |  |  | 5327 |      * the content in the element. Depending on the 3rd argument it can replace
 | 
        
           |  |  | 5328 |      * the innerHTML or the outerHTML which can be useful to completely remove
 | 
        
           |  |  | 5329 |      * the element if needed.
 | 
        
           |  |  | 5330 |      *
 | 
        
           |  |  | 5331 |      * @param string $selector where new content should be replaced
 | 
        
           |  |  | 5332 |      * @param string $html A chunk of well formed html
 | 
        
           |  |  | 5333 |      * @param bool $outer Wether it replaces the innerHTML or the outerHTML
 | 
        
           |  |  | 5334 |      * @return string html to be written
 | 
        
           |  |  | 5335 |      */
 | 
        
           |  |  | 5336 |     public function select_element_for_replace(string $selector, string $html, bool $outer = false) {
 | 
        
           |  |  | 5337 |   | 
        
           |  |  | 5338 |         if (!CLI_SCRIPT && !NO_OUTPUT_BUFFERING) {
 | 
        
           |  |  | 5339 |             throw new coding_exception('select_element_for_replace used in a non-CLI script without setting NO_OUTPUT_BUFFERING.',
 | 
        
           |  |  | 5340 |                 DEBUG_DEVELOPER);
 | 
        
           |  |  | 5341 |         }
 | 
        
           |  |  | 5342 |   | 
        
           |  |  | 5343 |         // Escape html for use inside a javascript string.
 | 
        
           |  |  | 5344 |         $html = addslashes_js($html);
 | 
        
           |  |  | 5345 |         $property = $outer ? 'outerHTML' : 'innerHTML';
 | 
        
           |  |  | 5346 |         $output = html_writer::tag('script', "document.querySelector('$selector').$property = '$html';");
 | 
        
           |  |  | 5347 |         $output .= "\n";
 | 
        
           |  |  | 5348 |         return $output;
 | 
        
           |  |  | 5349 |     }
 | 
        
           |  |  | 5350 | }
 | 
        
           |  |  | 5351 |   | 
        
           |  |  | 5352 | /**
 | 
        
           |  |  | 5353 |  * A renderer that generates output for command-line scripts.
 | 
        
           |  |  | 5354 |  *
 | 
        
           |  |  | 5355 |  * The implementation of this renderer is probably incomplete.
 | 
        
           |  |  | 5356 |  *
 | 
        
           |  |  | 5357 |  * @copyright 2009 Tim Hunt
 | 
        
           |  |  | 5358 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 5359 |  * @since Moodle 2.0
 | 
        
           |  |  | 5360 |  * @package core
 | 
        
           |  |  | 5361 |  * @category output
 | 
        
           |  |  | 5362 |  */
 | 
        
           |  |  | 5363 | class core_renderer_cli extends core_renderer {
 | 
        
           |  |  | 5364 |   | 
        
           |  |  | 5365 |     /**
 | 
        
           |  |  | 5366 |      * @var array $progressmaximums stores the largest percentage for a progress bar.
 | 
        
           |  |  | 5367 |      * @return string ascii fragment
 | 
        
           |  |  | 5368 |      */
 | 
        
           |  |  | 5369 |     private $progressmaximums = [];
 | 
        
           |  |  | 5370 |   | 
        
           |  |  | 5371 |     /**
 | 
        
           |  |  | 5372 |      * Returns the page header.
 | 
        
           |  |  | 5373 |      *
 | 
        
           |  |  | 5374 |      * @return string HTML fragment
 | 
        
           |  |  | 5375 |      */
 | 
        
           |  |  | 5376 |     public function header() {
 | 
        
           |  |  | 5377 |         return $this->page->heading . "\n";
 | 
        
           |  |  | 5378 |     }
 | 
        
           |  |  | 5379 |   | 
        
           |  |  | 5380 |     /**
 | 
        
           |  |  | 5381 |      * Renders a Check API result
 | 
        
           |  |  | 5382 |      *
 | 
        
           |  |  | 5383 |      * To aid in CLI consistency this status is NOT translated and the visual
 | 
        
           |  |  | 5384 |      * width is always exactly 10 chars.
 | 
        
           |  |  | 5385 |      *
 | 
        
           |  |  | 5386 |      * @param core\check\result $result
 | 
        
           |  |  | 5387 |      * @return string HTML fragment
 | 
        
           |  |  | 5388 |      */
 | 
        
           |  |  | 5389 |     protected function render_check_result(core\check\result $result) {
 | 
        
           |  |  | 5390 |         $status = $result->get_status();
 | 
        
           |  |  | 5391 |   | 
        
           |  |  | 5392 |         $labels = [
 | 
        
           |  |  | 5393 |             core\check\result::NA        => '      ' . cli_ansi_format('<colour:darkGray>' ) . ' NA ',
 | 
        
           |  |  | 5394 |             core\check\result::OK        => '      ' . cli_ansi_format('<colour:green>') . ' OK ',
 | 
        
           |  |  | 5395 |             core\check\result::INFO      => '    '   . cli_ansi_format('<colour:blue>' ) . ' INFO ',
 | 
        
           |  |  | 5396 |             core\check\result::UNKNOWN   => ' '      . cli_ansi_format('<colour:darkGray>' ) . ' UNKNOWN ',
 | 
        
           |  |  | 5397 |             core\check\result::WARNING   => ' '      . cli_ansi_format('<colour:black><bgcolour:yellow>') . ' WARNING ',
 | 
        
           |  |  | 5398 |             core\check\result::ERROR     => '   '    . cli_ansi_format('<bgcolour:red>') . ' ERROR ',
 | 
        
           |  |  | 5399 |             core\check\result::CRITICAL  => ''       . cli_ansi_format('<bgcolour:red>') . ' CRITICAL ',
 | 
        
           |  |  | 5400 |         ];
 | 
        
           |  |  | 5401 |         $string = $labels[$status] . cli_ansi_format('<colour:normal>');
 | 
        
           |  |  | 5402 |         return $string;
 | 
        
           |  |  | 5403 |     }
 | 
        
           |  |  | 5404 |   | 
        
           |  |  | 5405 |     /**
 | 
        
           |  |  | 5406 |      * Renders a Check API result
 | 
        
           |  |  | 5407 |      *
 | 
        
           |  |  | 5408 |      * @param core\check\result $result
 | 
        
           |  |  | 5409 |      * @return string fragment
 | 
        
           |  |  | 5410 |      */
 | 
        
           |  |  | 5411 |     public function check_result(core\check\result $result) {
 | 
        
           |  |  | 5412 |         return $this->render_check_result($result);
 | 
        
           |  |  | 5413 |     }
 | 
        
           |  |  | 5414 |   | 
        
           |  |  | 5415 |     /**
 | 
        
           |  |  | 5416 |      * Renders a progress bar.
 | 
        
           |  |  | 5417 |      *
 | 
        
           |  |  | 5418 |      * Do not use $OUTPUT->render($bar), instead use progress_bar::create().
 | 
        
           |  |  | 5419 |      *
 | 
        
           |  |  | 5420 |      * @param  progress_bar $bar The bar.
 | 
        
           |  |  | 5421 |      * @return string ascii fragment
 | 
        
           |  |  | 5422 |      */
 | 
        
           |  |  | 5423 |     public function render_progress_bar(progress_bar $bar) {
 | 
        
           |  |  | 5424 |         global $CFG;
 | 
        
           |  |  | 5425 |   | 
        
           |  |  | 5426 |         $size = 55; // The width of the progress bar in chars.
 | 
        
           |  |  | 5427 |         $ascii = "\n";
 | 
        
           |  |  | 5428 |   | 
        
           |  |  | 5429 |         if (stream_isatty(STDOUT)) {
 | 
        
           |  |  | 5430 |             require_once($CFG->libdir.'/clilib.php');
 | 
        
           |  |  | 5431 |   | 
        
           |  |  | 5432 |             $ascii .= "[" . str_repeat(' ', $size) . "] 0% \n";
 | 
        
           |  |  | 5433 |             return cli_ansi_format($ascii);
 | 
        
           |  |  | 5434 |         }
 | 
        
           |  |  | 5435 |   | 
        
           |  |  | 5436 |         $this->progressmaximums[$bar->get_id()] = 0;
 | 
        
           |  |  | 5437 |         $ascii .= '[';
 | 
        
           |  |  | 5438 |         return $ascii;
 | 
        
           |  |  | 5439 |     }
 | 
        
           |  |  | 5440 |   | 
        
           |  |  | 5441 |     /**
 | 
        
           |  |  | 5442 |      * Renders an update to a progress bar.
 | 
        
           |  |  | 5443 |      *
 | 
        
           |  |  | 5444 |      * Note: This does not cleanly map to a renderable class and should
 | 
        
           |  |  | 5445 |      * never be used directly.
 | 
        
           |  |  | 5446 |      *
 | 
        
           |  |  | 5447 |      * @param  string $id
 | 
        
           |  |  | 5448 |      * @param  float $percent
 | 
        
           |  |  | 5449 |      * @param  string $msg Message
 | 
        
           |  |  | 5450 |      * @param  string $estimate time remaining message
 | 
        
           |  |  | 5451 |      * @return string ascii fragment
 | 
        
           |  |  | 5452 |      */
 | 
        
           |  |  | 5453 |     public function render_progress_bar_update(string $id, float $percent, string $msg, string $estimate): string {
 | 
        
           |  |  | 5454 |         $size = 55; // The width of the progress bar in chars.
 | 
        
           |  |  | 5455 |         $ascii = '';
 | 
        
           |  |  | 5456 |   | 
        
           |  |  | 5457 |         // If we are rendering to a terminal then we can safely use ansii codes
 | 
        
           |  |  | 5458 |         // to move the cursor and redraw the complete progress bar each time
 | 
        
           |  |  | 5459 |         // it is updated.
 | 
        
           |  |  | 5460 |         if (stream_isatty(STDOUT)) {
 | 
        
           |  |  | 5461 |             $colour = $percent == 100 ? 'green' : 'blue';
 | 
        
           |  |  | 5462 |   | 
        
           |  |  | 5463 |             $done = $percent * $size * 0.01;
 | 
        
           |  |  | 5464 |             $whole = floor($done);
 | 
        
           |  |  | 5465 |             $bar = "<colour:$colour>";
 | 
        
           |  |  | 5466 |             $bar .= str_repeat('█', $whole);
 | 
        
           |  |  | 5467 |   | 
        
           |  |  | 5468 |             if ($whole < $size) {
 | 
        
           |  |  | 5469 |                 // By using unicode chars for partial blocks we can have higher
 | 
        
           |  |  | 5470 |                 // precision progress bar.
 | 
        
           |  |  | 5471 |                 $fraction = floor(($done - $whole) * 8);
 | 
        
           |  |  | 5472 |                 $bar .= core_text::substr(' â–â–Žâ–▌▋▊▉', $fraction, 1);
 | 
        
           |  |  | 5473 |   | 
        
           |  |  | 5474 |                 // Fill the rest of the empty bar.
 | 
        
           |  |  | 5475 |                 $bar .= str_repeat(' ', $size - $whole - 1);
 | 
        
           |  |  | 5476 |             }
 | 
        
           |  |  | 5477 |   | 
        
           |  |  | 5478 |             $bar .= '<colour:normal>';
 | 
        
           |  |  | 5479 |   | 
        
           |  |  | 5480 |             if ($estimate) {
 | 
        
           |  |  | 5481 |                 $estimate = "- $estimate";
 | 
        
           |  |  | 5482 |             }
 | 
        
           |  |  | 5483 |   | 
        
           |  |  | 5484 |             $ascii .= '<cursor:up>';
 | 
        
           |  |  | 5485 |             $ascii .= '<cursor:up>';
 | 
        
           |  |  | 5486 |             $ascii .= sprintf("[$bar] %3.1f%% %-22s\n", $percent, $estimate);
 | 
        
           |  |  | 5487 |             $ascii .= sprintf("%-80s\n", $msg);
 | 
        
           |  |  | 5488 |             return cli_ansi_format($ascii);
 | 
        
           |  |  | 5489 |         }
 | 
        
           |  |  | 5490 |   | 
        
           |  |  | 5491 |         // If we are not rendering to a tty, ie when piped to another command
 | 
        
           |  |  | 5492 |         // or on windows we need to progressively render the progress bar
 | 
        
           |  |  | 5493 |         // which can only ever go forwards.
 | 
        
           |  |  | 5494 |         $done = round($percent * $size * 0.01);
 | 
        
           |  |  | 5495 |         $delta = max(0, $done - $this->progressmaximums[$id]);
 | 
        
           |  |  | 5496 |   | 
        
           |  |  | 5497 |         $ascii .= str_repeat('#', $delta);
 | 
        
           |  |  | 5498 |         if ($percent >= 100 && $delta > 0) {
 | 
        
           |  |  | 5499 |             $ascii .= sprintf("] %3.1f%%", $percent) . "\n$msg\n";
 | 
        
           |  |  | 5500 |         }
 | 
        
           |  |  | 5501 |         $this->progressmaximums[$id] += $delta;
 | 
        
           |  |  | 5502 |         return $ascii;
 | 
        
           |  |  | 5503 |     }
 | 
        
           |  |  | 5504 |   | 
        
           |  |  | 5505 |     /**
 | 
        
           |  |  | 5506 |      * Returns a template fragment representing a Heading.
 | 
        
           |  |  | 5507 |      *
 | 
        
           |  |  | 5508 |      * @param string $text The text of the heading
 | 
        
           |  |  | 5509 |      * @param int $level The level of importance of the heading
 | 
        
           |  |  | 5510 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 5511 |      * @param string $id An optional ID
 | 
        
           |  |  | 5512 |      * @return string A template fragment for a heading
 | 
        
           |  |  | 5513 |      */
 | 
        
           |  |  | 5514 |     public function heading($text, $level = 2, $classes = 'main', $id = null) {
 | 
        
           |  |  | 5515 |         $text .= "\n";
 | 
        
           |  |  | 5516 |         switch ($level) {
 | 
        
           |  |  | 5517 |             case 1:
 | 
        
           |  |  | 5518 |                 return '=>' . $text;
 | 
        
           |  |  | 5519 |             case 2:
 | 
        
           |  |  | 5520 |                 return '-->' . $text;
 | 
        
           |  |  | 5521 |             default:
 | 
        
           |  |  | 5522 |                 return $text;
 | 
        
           |  |  | 5523 |         }
 | 
        
           |  |  | 5524 |     }
 | 
        
           |  |  | 5525 |   | 
        
           |  |  | 5526 |     /**
 | 
        
           |  |  | 5527 |      * Returns a template fragment representing a fatal error.
 | 
        
           |  |  | 5528 |      *
 | 
        
           |  |  | 5529 |      * @param string $message The message to output
 | 
        
           |  |  | 5530 |      * @param string $moreinfourl URL where more info can be found about the error
 | 
        
           |  |  | 5531 |      * @param string $link Link for the Continue button
 | 
        
           |  |  | 5532 |      * @param array $backtrace The execution backtrace
 | 
        
           |  |  | 5533 |      * @param string $debuginfo Debugging information
 | 
        
           |  |  | 5534 |      * @return string A template fragment for a fatal error
 | 
        
           |  |  | 5535 |      */
 | 
        
           |  |  | 5536 |     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = "") {
 | 
        
           |  |  | 5537 |         global $CFG;
 | 
        
           |  |  | 5538 |   | 
        
           |  |  | 5539 |         $output = "!!! $message !!!\n";
 | 
        
           |  |  | 5540 |   | 
        
           |  |  | 5541 |         if ($CFG->debugdeveloper) {
 | 
        
           |  |  | 5542 |             if (!empty($debuginfo)) {
 | 
        
           |  |  | 5543 |                 $output .= $this->notification($debuginfo, 'notifytiny');
 | 
        
           |  |  | 5544 |             }
 | 
        
           |  |  | 5545 |             if (!empty($backtrace)) {
 | 
        
           |  |  | 5546 |                 $output .= $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
 | 
        
           |  |  | 5547 |             }
 | 
        
           |  |  | 5548 |         }
 | 
        
           |  |  | 5549 |   | 
        
           |  |  | 5550 |         return $output;
 | 
        
           |  |  | 5551 |     }
 | 
        
           |  |  | 5552 |   | 
        
           |  |  | 5553 |     /**
 | 
        
           |  |  | 5554 |      * Returns a template fragment representing a notification.
 | 
        
           |  |  | 5555 |      *
 | 
        
           |  |  | 5556 |      * @param string $message The message to print out.
 | 
        
           |  |  | 5557 |      * @param string $type    The type of notification. See constants on \core\output\notification.
 | 
        
           |  |  | 5558 |      * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
 | 
        
           |  |  | 5559 |      * @return string A template fragment for a notification
 | 
        
           |  |  | 5560 |      */
 | 
        
           |  |  | 5561 |     public function notification($message, $type = null, $closebutton = true) {
 | 
        
           |  |  | 5562 |         $message = clean_text($message);
 | 
        
           |  |  | 5563 |         if ($type === 'notifysuccess' || $type === 'success') {
 | 
        
           |  |  | 5564 |             return "++ $message ++\n";
 | 
        
           |  |  | 5565 |         }
 | 
        
           |  |  | 5566 |         return "!! $message !!\n";
 | 
        
           |  |  | 5567 |     }
 | 
        
           |  |  | 5568 |   | 
        
           |  |  | 5569 |     /**
 | 
        
           |  |  | 5570 |      * There is no footer for a cli request, however we must override the
 | 
        
           |  |  | 5571 |      * footer method to prevent the default footer.
 | 
        
           |  |  | 5572 |      */
 | 
        
           |  |  | 5573 |     public function footer() {}
 | 
        
           |  |  | 5574 |   | 
        
           |  |  | 5575 |     /**
 | 
        
           |  |  | 5576 |      * Render a notification (that is, a status message about something that has
 | 
        
           |  |  | 5577 |      * just happened).
 | 
        
           |  |  | 5578 |      *
 | 
        
           |  |  | 5579 |      * @param \core\output\notification $notification the notification to print out
 | 
        
           |  |  | 5580 |      * @return string plain text output
 | 
        
           |  |  | 5581 |      */
 | 
        
           |  |  | 5582 |     public function render_notification(\core\output\notification $notification) {
 | 
        
           |  |  | 5583 |         return $this->notification($notification->get_message(), $notification->get_message_type());
 | 
        
           |  |  | 5584 |     }
 | 
        
           |  |  | 5585 | }
 | 
        
           |  |  | 5586 |   | 
        
           |  |  | 5587 |   | 
        
           |  |  | 5588 | /**
 | 
        
           |  |  | 5589 |  * A renderer that generates output for ajax scripts.
 | 
        
           |  |  | 5590 |  *
 | 
        
           |  |  | 5591 |  * This renderer prevents accidental sends back only json
 | 
        
           |  |  | 5592 |  * encoded error messages, all other output is ignored.
 | 
        
           |  |  | 5593 |  *
 | 
        
           |  |  | 5594 |  * @copyright 2010 Petr Skoda
 | 
        
           |  |  | 5595 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 5596 |  * @since Moodle 2.0
 | 
        
           |  |  | 5597 |  * @package core
 | 
        
           |  |  | 5598 |  * @category output
 | 
        
           |  |  | 5599 |  */
 | 
        
           |  |  | 5600 | class core_renderer_ajax extends core_renderer {
 | 
        
           |  |  | 5601 |   | 
        
           |  |  | 5602 |     /**
 | 
        
           |  |  | 5603 |      * Returns a template fragment representing a fatal error.
 | 
        
           |  |  | 5604 |      *
 | 
        
           |  |  | 5605 |      * @param string $message The message to output
 | 
        
           |  |  | 5606 |      * @param string $moreinfourl URL where more info can be found about the error
 | 
        
           |  |  | 5607 |      * @param string $link Link for the Continue button
 | 
        
           |  |  | 5608 |      * @param array $backtrace The execution backtrace
 | 
        
           |  |  | 5609 |      * @param string $debuginfo Debugging information
 | 
        
           |  |  | 5610 |      * @return string A template fragment for a fatal error
 | 
        
           |  |  | 5611 |      */
 | 
        
           |  |  | 5612 |     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = "") {
 | 
        
           |  |  | 5613 |         global $CFG;
 | 
        
           |  |  | 5614 |   | 
        
           |  |  | 5615 |         $this->page->set_context(null); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
 | 
        
           |  |  | 5616 |   | 
        
           |  |  | 5617 |         $e = new stdClass();
 | 
        
           |  |  | 5618 |         $e->error      = $message;
 | 
        
           |  |  | 5619 |         $e->errorcode  = $errorcode;
 | 
        
           |  |  | 5620 |         $e->stacktrace = NULL;
 | 
        
           |  |  | 5621 |         $e->debuginfo  = NULL;
 | 
        
           |  |  | 5622 |         $e->reproductionlink = NULL;
 | 
        
           |  |  | 5623 |         if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
 | 
        
           |  |  | 5624 |             $link = (string) $link;
 | 
        
           |  |  | 5625 |             if ($link) {
 | 
        
           |  |  | 5626 |                 $e->reproductionlink = $link;
 | 
        
           |  |  | 5627 |             }
 | 
        
           |  |  | 5628 |             if (!empty($debuginfo)) {
 | 
        
           |  |  | 5629 |                 $e->debuginfo = $debuginfo;
 | 
        
           |  |  | 5630 |             }
 | 
        
           |  |  | 5631 |             if (!empty($backtrace)) {
 | 
        
           |  |  | 5632 |                 $e->stacktrace = format_backtrace($backtrace, true);
 | 
        
           |  |  | 5633 |             }
 | 
        
           |  |  | 5634 |         }
 | 
        
           |  |  | 5635 |         $this->header();
 | 
        
           |  |  | 5636 |         return json_encode($e);
 | 
        
           |  |  | 5637 |     }
 | 
        
           |  |  | 5638 |   | 
        
           |  |  | 5639 |     /**
 | 
        
           |  |  | 5640 |      * Used to display a notification.
 | 
        
           |  |  | 5641 |      * For the AJAX notifications are discarded.
 | 
        
           |  |  | 5642 |      *
 | 
        
           |  |  | 5643 |      * @param string $message The message to print out.
 | 
        
           |  |  | 5644 |      * @param string $type    The type of notification. See constants on \core\output\notification.
 | 
        
           |  |  | 5645 |      * @param bool $closebutton Whether to show a close icon to remove the notification (default true).
 | 
        
           |  |  | 5646 |      */
 | 
        
           |  |  | 5647 |     public function notification($message, $type = null, $closebutton = true) {
 | 
        
           |  |  | 5648 |     }
 | 
        
           |  |  | 5649 |   | 
        
           |  |  | 5650 |     /**
 | 
        
           |  |  | 5651 |      * Used to display a redirection message.
 | 
        
           |  |  | 5652 |      * AJAX redirections should not occur and as such redirection messages
 | 
        
           |  |  | 5653 |      * are discarded.
 | 
        
           |  |  | 5654 |      *
 | 
        
           |  |  | 5655 |      * @param moodle_url|string $encodedurl
 | 
        
           |  |  | 5656 |      * @param string $message
 | 
        
           |  |  | 5657 |      * @param int $delay
 | 
        
           |  |  | 5658 |      * @param bool $debugdisableredirect
 | 
        
           |  |  | 5659 |      * @param string $messagetype The type of notification to show the message in.
 | 
        
           |  |  | 5660 |      *         See constants on \core\output\notification.
 | 
        
           |  |  | 5661 |      */
 | 
        
           |  |  | 5662 |     public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect,
 | 
        
           |  |  | 5663 |                                      $messagetype = \core\output\notification::NOTIFY_INFO) {}
 | 
        
           |  |  | 5664 |   | 
        
           |  |  | 5665 |     /**
 | 
        
           |  |  | 5666 |      * Prepares the start of an AJAX output.
 | 
        
           |  |  | 5667 |      */
 | 
        
           |  |  | 5668 |     public function header() {
 | 
        
           |  |  | 5669 |         // unfortunately YUI iframe upload does not support application/json
 | 
        
           |  |  | 5670 |         if (!empty($_FILES)) {
 | 
        
           |  |  | 5671 |             @header('Content-type: text/plain; charset=utf-8');
 | 
        
           |  |  | 5672 |             if (!core_useragent::supports_json_contenttype()) {
 | 
        
           |  |  | 5673 |                 @header('X-Content-Type-Options: nosniff');
 | 
        
           |  |  | 5674 |             }
 | 
        
           |  |  | 5675 |         } else if (!core_useragent::supports_json_contenttype()) {
 | 
        
           |  |  | 5676 |             @header('Content-type: text/plain; charset=utf-8');
 | 
        
           |  |  | 5677 |             @header('X-Content-Type-Options: nosniff');
 | 
        
           |  |  | 5678 |         } else {
 | 
        
           |  |  | 5679 |             @header('Content-type: application/json; charset=utf-8');
 | 
        
           |  |  | 5680 |         }
 | 
        
           |  |  | 5681 |   | 
        
           |  |  | 5682 |         // Headers to make it not cacheable and json
 | 
        
           |  |  | 5683 |         @header('Cache-Control: no-store, no-cache, must-revalidate');
 | 
        
           |  |  | 5684 |         @header('Cache-Control: post-check=0, pre-check=0', false);
 | 
        
           |  |  | 5685 |         @header('Pragma: no-cache');
 | 
        
           |  |  | 5686 |         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
 | 
        
           |  |  | 5687 |         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
 | 
        
           |  |  | 5688 |         @header('Accept-Ranges: none');
 | 
        
           |  |  | 5689 |     }
 | 
        
           |  |  | 5690 |   | 
        
           |  |  | 5691 |     /**
 | 
        
           |  |  | 5692 |      * There is no footer for an AJAX request, however we must override the
 | 
        
           |  |  | 5693 |      * footer method to prevent the default footer.
 | 
        
           |  |  | 5694 |      */
 | 
        
           |  |  | 5695 |     public function footer() {}
 | 
        
           |  |  | 5696 |   | 
        
           |  |  | 5697 |     /**
 | 
        
           |  |  | 5698 |      * No need for headers in an AJAX request... this should never happen.
 | 
        
           |  |  | 5699 |      * @param string $text
 | 
        
           |  |  | 5700 |      * @param int $level
 | 
        
           |  |  | 5701 |      * @param string $classes
 | 
        
           |  |  | 5702 |      * @param string $id
 | 
        
           |  |  | 5703 |      */
 | 
        
           |  |  | 5704 |     public function heading($text, $level = 2, $classes = 'main', $id = null) {}
 | 
        
           |  |  | 5705 | }
 | 
        
           |  |  | 5706 |   | 
        
           |  |  | 5707 |   | 
        
           |  |  | 5708 |   | 
        
           |  |  | 5709 | /**
 | 
        
           |  |  | 5710 |  * The maintenance renderer.
 | 
        
           |  |  | 5711 |  *
 | 
        
           |  |  | 5712 |  * The purpose of this renderer is to block out the core renderer methods that are not usable when the site
 | 
        
           |  |  | 5713 |  * is running a maintenance related task.
 | 
        
           |  |  | 5714 |  * It must always extend the core_renderer as we switch from the core_renderer to this renderer in a couple of places.
 | 
        
           |  |  | 5715 |  *
 | 
        
           |  |  | 5716 |  * @since Moodle 2.6
 | 
        
           |  |  | 5717 |  * @package core
 | 
        
           |  |  | 5718 |  * @category output
 | 
        
           |  |  | 5719 |  * @copyright 2013 Sam Hemelryk
 | 
        
           |  |  | 5720 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 5721 |  */
 | 
        
           |  |  | 5722 | class core_renderer_maintenance extends core_renderer {
 | 
        
           |  |  | 5723 |   | 
        
           |  |  | 5724 |     /**
 | 
        
           |  |  | 5725 |      * Initialises the renderer instance.
 | 
        
           |  |  | 5726 |      *
 | 
        
           |  |  | 5727 |      * @param moodle_page $page
 | 
        
           |  |  | 5728 |      * @param string $target
 | 
        
           |  |  | 5729 |      * @throws coding_exception
 | 
        
           |  |  | 5730 |      */
 | 
        
           |  |  | 5731 |     public function __construct(moodle_page $page, $target) {
 | 
        
           |  |  | 5732 |         if ($target !== RENDERER_TARGET_MAINTENANCE || $page->pagelayout !== 'maintenance') {
 | 
        
           |  |  | 5733 |             throw new coding_exception('Invalid request for the maintenance renderer.');
 | 
        
           |  |  | 5734 |         }
 | 
        
           |  |  | 5735 |         parent::__construct($page, $target);
 | 
        
           |  |  | 5736 |     }
 | 
        
           |  |  | 5737 |   | 
        
           |  |  | 5738 |     /**
 | 
        
           |  |  | 5739 |      * Does nothing. The maintenance renderer cannot produce blocks.
 | 
        
           |  |  | 5740 |      *
 | 
        
           |  |  | 5741 |      * @param block_contents $bc
 | 
        
           |  |  | 5742 |      * @param string $region
 | 
        
           |  |  | 5743 |      * @return string
 | 
        
           |  |  | 5744 |      */
 | 
        
           |  |  | 5745 |     public function block(block_contents $bc, $region) {
 | 
        
           |  |  | 5746 |         return '';
 | 
        
           |  |  | 5747 |     }
 | 
        
           |  |  | 5748 |   | 
        
           |  |  | 5749 |     /**
 | 
        
           |  |  | 5750 |      * Does nothing. The maintenance renderer cannot produce blocks.
 | 
        
           |  |  | 5751 |      *
 | 
        
           |  |  | 5752 |      * @param string $region
 | 
        
           |  |  | 5753 |      * @param array $classes
 | 
        
           |  |  | 5754 |      * @param string $tag
 | 
        
           |  |  | 5755 |      * @param boolean $fakeblocksonly
 | 
        
           |  |  | 5756 |      * @return string
 | 
        
           |  |  | 5757 |      */
 | 
        
           |  |  | 5758 |     public function blocks($region, $classes = array(), $tag = 'aside', $fakeblocksonly = false) {
 | 
        
           |  |  | 5759 |         return '';
 | 
        
           |  |  | 5760 |     }
 | 
        
           |  |  | 5761 |   | 
        
           |  |  | 5762 |     /**
 | 
        
           |  |  | 5763 |      * Does nothing. The maintenance renderer cannot produce blocks.
 | 
        
           |  |  | 5764 |      *
 | 
        
           |  |  | 5765 |      * @param string $region
 | 
        
           |  |  | 5766 |      * @param boolean $fakeblocksonly Output fake block only.
 | 
        
           |  |  | 5767 |      * @return string
 | 
        
           |  |  | 5768 |      */
 | 
        
           |  |  | 5769 |     public function blocks_for_region($region, $fakeblocksonly = false) {
 | 
        
           |  |  | 5770 |         return '';
 | 
        
           |  |  | 5771 |     }
 | 
        
           |  |  | 5772 |   | 
        
           |  |  | 5773 |     /**
 | 
        
           |  |  | 5774 |      * Does nothing. The maintenance renderer cannot produce a course content header.
 | 
        
           |  |  | 5775 |      *
 | 
        
           |  |  | 5776 |      * @param bool $onlyifnotcalledbefore
 | 
        
           |  |  | 5777 |      * @return string
 | 
        
           |  |  | 5778 |      */
 | 
        
           |  |  | 5779 |     public function course_content_header($onlyifnotcalledbefore = false) {
 | 
        
           |  |  | 5780 |         return '';
 | 
        
           |  |  | 5781 |     }
 | 
        
           |  |  | 5782 |   | 
        
           |  |  | 5783 |     /**
 | 
        
           |  |  | 5784 |      * Does nothing. The maintenance renderer cannot produce a course content footer.
 | 
        
           |  |  | 5785 |      *
 | 
        
           |  |  | 5786 |      * @param bool $onlyifnotcalledbefore
 | 
        
           |  |  | 5787 |      * @return string
 | 
        
           |  |  | 5788 |      */
 | 
        
           |  |  | 5789 |     public function course_content_footer($onlyifnotcalledbefore = false) {
 | 
        
           |  |  | 5790 |         return '';
 | 
        
           |  |  | 5791 |     }
 | 
        
           |  |  | 5792 |   | 
        
           |  |  | 5793 |     /**
 | 
        
           |  |  | 5794 |      * Does nothing. The maintenance renderer cannot produce a course header.
 | 
        
           |  |  | 5795 |      *
 | 
        
           |  |  | 5796 |      * @return string
 | 
        
           |  |  | 5797 |      */
 | 
        
           |  |  | 5798 |     public function course_header() {
 | 
        
           |  |  | 5799 |         return '';
 | 
        
           |  |  | 5800 |     }
 | 
        
           |  |  | 5801 |   | 
        
           |  |  | 5802 |     /**
 | 
        
           |  |  | 5803 |      * Does nothing. The maintenance renderer cannot produce a course footer.
 | 
        
           |  |  | 5804 |      *
 | 
        
           |  |  | 5805 |      * @return string
 | 
        
           |  |  | 5806 |      */
 | 
        
           |  |  | 5807 |     public function course_footer() {
 | 
        
           |  |  | 5808 |         return '';
 | 
        
           |  |  | 5809 |     }
 | 
        
           |  |  | 5810 |   | 
        
           |  |  | 5811 |     /**
 | 
        
           |  |  | 5812 |      * Does nothing. The maintenance renderer cannot produce a custom menu.
 | 
        
           |  |  | 5813 |      *
 | 
        
           |  |  | 5814 |      * @param string $custommenuitems
 | 
        
           |  |  | 5815 |      * @return string
 | 
        
           |  |  | 5816 |      */
 | 
        
           |  |  | 5817 |     public function custom_menu($custommenuitems = '') {
 | 
        
           |  |  | 5818 |         return '';
 | 
        
           |  |  | 5819 |     }
 | 
        
           |  |  | 5820 |   | 
        
           |  |  | 5821 |     /**
 | 
        
           |  |  | 5822 |      * Does nothing. The maintenance renderer cannot produce a file picker.
 | 
        
           |  |  | 5823 |      *
 | 
        
           |  |  | 5824 |      * @param array $options
 | 
        
           |  |  | 5825 |      * @return string
 | 
        
           |  |  | 5826 |      */
 | 
        
           |  |  | 5827 |     public function file_picker($options) {
 | 
        
           |  |  | 5828 |         return '';
 | 
        
           |  |  | 5829 |     }
 | 
        
           |  |  | 5830 |   | 
        
           |  |  | 5831 |     /**
 | 
        
           |  |  | 5832 |      * Overridden confirm message for upgrades.
 | 
        
           |  |  | 5833 |      *
 | 
        
           |  |  | 5834 |      * @param string $message The question to ask the user
 | 
        
           |  |  | 5835 |      * @param single_button|moodle_url|string $continue The single_button component representing the Continue answer.
 | 
        
           |  |  | 5836 |      * @param single_button|moodle_url|string $cancel The single_button component representing the Cancel answer.
 | 
        
           |  |  | 5837 |      * @param array $displayoptions optional extra display options
 | 
        
           |  |  | 5838 |      * @return string HTML fragment
 | 
        
           |  |  | 5839 |      */
 | 
        
           |  |  | 5840 |     public function confirm($message, $continue, $cancel, array $displayoptions = []) {
 | 
        
           |  |  | 5841 |         // We need plain styling of confirm boxes on upgrade because we don't know which stylesheet we have (it could be
 | 
        
           |  |  | 5842 |         // from any previous version of Moodle).
 | 
        
           |  |  | 5843 |         if ($continue instanceof single_button) {
 | 
        
           |  |  | 5844 |             $continue->type = single_button::BUTTON_PRIMARY;
 | 
        
           |  |  | 5845 |         } else if (is_string($continue)) {
 | 
        
           |  |  | 5846 |             $continue = new single_button(new moodle_url($continue), get_string('continue'), 'post',
 | 
        
           |  |  | 5847 |                 $displayoptions['type'] ?? single_button::BUTTON_PRIMARY);
 | 
        
           |  |  | 5848 |         } else if ($continue instanceof moodle_url) {
 | 
        
           |  |  | 5849 |             $continue = new single_button($continue, get_string('continue'), 'post',
 | 
        
           |  |  | 5850 |                 $displayoptions['type'] ?? single_button::BUTTON_PRIMARY);
 | 
        
           |  |  | 5851 |         } else {
 | 
        
           |  |  | 5852 |             throw new coding_exception('The continue param to $OUTPUT->confirm() must be either a URL' .
 | 
        
           |  |  | 5853 |                                        ' (string/moodle_url) or a single_button instance.');
 | 
        
           |  |  | 5854 |         }
 | 
        
           |  |  | 5855 |   | 
        
           |  |  | 5856 |         if ($cancel instanceof single_button) {
 | 
        
           |  |  | 5857 |             $output = '';
 | 
        
           |  |  | 5858 |         } else if (is_string($cancel)) {
 | 
        
           |  |  | 5859 |             $cancel = new single_button(new moodle_url($cancel), get_string('cancel'), 'get');
 | 
        
           |  |  | 5860 |         } else if ($cancel instanceof moodle_url) {
 | 
        
           |  |  | 5861 |             $cancel = new single_button($cancel, get_string('cancel'), 'get');
 | 
        
           |  |  | 5862 |         } else {
 | 
        
           |  |  | 5863 |             throw new coding_exception('The cancel param to $OUTPUT->confirm() must be either a URL' .
 | 
        
           |  |  | 5864 |                                        ' (string/moodle_url) or a single_button instance.');
 | 
        
           |  |  | 5865 |         }
 | 
        
           |  |  | 5866 |   | 
        
           |  |  | 5867 |         $output = $this->box_start('generalbox', 'notice');
 | 
        
           |  |  | 5868 |         $output .= html_writer::tag('h4', get_string('confirm'));
 | 
        
           |  |  | 5869 |         $output .= html_writer::tag('p', $message);
 | 
        
           |  |  | 5870 |         $output .= html_writer::tag('div', $this->render($cancel) . $this->render($continue), ['class' => 'buttons']);
 | 
        
           |  |  | 5871 |         $output .= $this->box_end();
 | 
        
           |  |  | 5872 |         return $output;
 | 
        
           |  |  | 5873 |     }
 | 
        
           |  |  | 5874 |   | 
        
           |  |  | 5875 |     /**
 | 
        
           |  |  | 5876 |      * Does nothing. The maintenance renderer does not support JS.
 | 
        
           |  |  | 5877 |      *
 | 
        
           |  |  | 5878 |      * @param block_contents $bc
 | 
        
           |  |  | 5879 |      */
 | 
        
           |  |  | 5880 |     public function init_block_hider_js(block_contents $bc) {
 | 
        
           |  |  | 5881 |         // Does nothing.
 | 
        
           |  |  | 5882 |     }
 | 
        
           |  |  | 5883 |   | 
        
           |  |  | 5884 |     /**
 | 
        
           |  |  | 5885 |      * Does nothing. The maintenance renderer cannot produce language menus.
 | 
        
           |  |  | 5886 |      *
 | 
        
           |  |  | 5887 |      * @return string
 | 
        
           |  |  | 5888 |      */
 | 
        
           |  |  | 5889 |     public function lang_menu() {
 | 
        
           |  |  | 5890 |         return '';
 | 
        
           |  |  | 5891 |     }
 | 
        
           |  |  | 5892 |   | 
        
           |  |  | 5893 |     /**
 | 
        
           |  |  | 5894 |      * Does nothing. The maintenance renderer has no need for login information.
 | 
        
           |  |  | 5895 |      *
 | 
        
           |  |  | 5896 |      * @param mixed $withlinks
 | 
        
           |  |  | 5897 |      * @return string
 | 
        
           |  |  | 5898 |      */
 | 
        
           |  |  | 5899 |     public function login_info($withlinks = null) {
 | 
        
           |  |  | 5900 |         return '';
 | 
        
           |  |  | 5901 |     }
 | 
        
           |  |  | 5902 |   | 
        
           |  |  | 5903 |     /**
 | 
        
           |  |  | 5904 |      * Secure login info.
 | 
        
           |  |  | 5905 |      *
 | 
        
           |  |  | 5906 |      * @return string
 | 
        
           |  |  | 5907 |      */
 | 
        
           |  |  | 5908 |     public function secure_login_info() {
 | 
        
           |  |  | 5909 |         return $this->login_info(false);
 | 
        
           |  |  | 5910 |     }
 | 
        
           |  |  | 5911 |   | 
        
           |  |  | 5912 |     /**
 | 
        
           |  |  | 5913 |      * Does nothing. The maintenance renderer cannot produce user pictures.
 | 
        
           |  |  | 5914 |      *
 | 
        
           |  |  | 5915 |      * @param stdClass $user
 | 
        
           |  |  | 5916 |      * @param array $options
 | 
        
           |  |  | 5917 |      * @return string
 | 
        
           |  |  | 5918 |      */
 | 
        
           |  |  | 5919 |     public function user_picture(stdClass $user, array $options = null) {
 | 
        
           |  |  | 5920 |         return '';
 | 
        
           |  |  | 5921 |     }
 | 
        
           |  |  | 5922 | }
 |