2009-08-10 06:22:04 +00:00
< ? php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Classes for rendering HTML output for Moodle .
*
2012-01-05 13:28:08 +13:00
* Please see { @ link http :// docs . moodle . org / en / Developement : How_Moodle_outputs_HTML }
2009-08-10 06:22:04 +00:00
* for an overview .
*
2012-01-05 13:28:08 +13:00
* Included in this file are the primary renderer classes :
* - renderer_base : The renderer outline class that all renderers
* should inherit from .
* - core_renderer : The standard HTML renderer .
* - core_renderer_cli : An adaption of the standard renderer for CLI scripts .
* - core_renderer_ajax : An adaption of the standard renderer for AJAX scripts .
* - plugin_renderer_base : A renderer class that should be extended by all
* plugin renderers .
*
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2010-07-25 13:35:05 +00:00
* @ copyright 2009 Tim Hunt
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2009-08-10 06:22:04 +00:00
*/
2021-02-06 01:18:24 +08:00
use core_completion\cm_completion_details ;
use core_course\output\activity_information ;
2010-07-25 13:35:05 +00:00
defined ( 'MOODLE_INTERNAL' ) || die ();
2009-08-10 06:22:04 +00:00
/**
* Simple base class for Moodle renderers .
*
* Tracks the xhtml_container_stack to use , which is passed in in the constructor .
*
* Also has methods to facilitate generating HTML output .
*
* @ copyright 2009 Tim Hunt
2012-01-05 13:28:08 +13:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
* @ since Moodle 2.0
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2009-08-10 06:22:04 +00:00
*/
2009-12-16 18:00:58 +00:00
class renderer_base {
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var xhtml_container_stack The xhtml_container_stack to use .
2012-01-05 13:28:08 +13:00
*/
2009-08-10 06:22:04 +00:00
protected $opencontainers ;
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var moodle_page The Moodle page the renderer has been created to assist with .
2012-01-05 13:28:08 +13:00
*/
2009-08-10 06:22:04 +00:00
protected $page ;
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var string The requested rendering target .
2012-01-05 13:28:08 +13:00
*/
2009-12-17 22:43:27 +00:00
protected $target ;
2009-08-10 06:22:04 +00:00
2015-02-22 15:24:44 +08:00
/**
* @ var Mustache_Engine $mustache The mustache template compiler
*/
private $mustache ;
/**
* Return an instance of the mustache class .
*
* @ since 2.9
* @ return Mustache_Engine
*/
protected function get_mustache () {
global $CFG ;
if ( $this -> mustache === null ) {
2018-07-03 13:01:24 +08:00
require_once ( " { $CFG -> libdir } /filelib.php " );
2015-02-22 15:24:44 +08:00
$themename = $this -> page -> theme -> name ;
$themerev = theme_get_revision ();
2018-06-27 11:04:33 +08:00
// Create new localcache directory.
2015-05-01 16:43:21 +08:00
$cachedir = make_localcache_directory ( " mustache/ $themerev / $themename " );
2018-06-27 11:04:33 +08:00
// Remove old localcache directories.
$mustachecachedirs = glob ( " { $CFG -> localcachedir } /mustache/* " , GLOB_ONLYDIR );
foreach ( $mustachecachedirs as $localcachedir ) {
$cachedrev = [];
preg_match ( " / \ /mustache \ /([0-9]+) $ / " , $localcachedir , $cachedrev );
$cachedrev = isset ( $cachedrev [ 1 ]) ? intval ( $cachedrev [ 1 ]) : 0 ;
if ( $cachedrev > 0 && $cachedrev < $themerev ) {
fulldelete ( $localcachedir );
}
}
2015-05-01 16:43:21 +08:00
$loader = new \core\output\mustache_filesystem_loader ();
2015-02-22 15:24:44 +08:00
$stringhelper = new \core\output\mustache_string_helper ();
MDL-52136 core: Add support for quoting variables in mustache helpers
This is required for when helpers include json-encoded variables as arguments.
As an example, imagine a template with content:
{{# str }} somekey, someidentifier, { "fullname": "{{ fullname }}" } {{/ str }}
If the fullname variable were to include the double-quote character (e.g.
John "Trevor" Doe) because of the way in which mustache renders content, it
would become:
{{# str }} somekey, someidentifier, { "fullname": "John "Trevor" Doe" } {{/ str }}
This results in an invalid JSON structure.
To work around this issue, the quote characters in the passed variable
must be escaped:
{{# str }} somekey, someidentifier, { "fullname": "John \"Trevor\" Doe" } {{/ str }}
Unfortunately, Mustache provides no way of doing so natively.
With this function, we can quote the text as appropriate:
{{# str }} somekey, someidentifier, { "fullname": {{# quote }}{{ fullname }}{{/ quote }} } {{/ str }}
This also handles the case where the quoted content includes the Mustache
delimeter ({{ or }}).
For example:
fullname = 'John "}}Trevor{{" Doe'
Ordinarily this would be rendered as:
{{# str }} somekey, someidentifier, { "fullname": "John "}}Trevor{{" Doe" } {{/ str }}
This rendering is both a JSON error, and also a mustache syntax error because of the mustache delimeters.
The quote helper also escapes these by wrapping them in change delimeter
tags:
{{# str }} somekey, someidentifier, { "fullname": "John "{{=<% %>=}}}}<%={{ }}=%>Trevor{{=<% %>=}}{{{{=<% %>=}}" Doe" } {{/ str }}
2016-02-12 12:42:41 +08:00
$quotehelper = new \core\output\mustache_quote_helper ();
2017-06-27 13:18:36 +08:00
$jshelper = new \core\output\mustache_javascript_helper ( $this -> page );
2015-02-22 15:24:44 +08:00
$pixhelper = new \core\output\mustache_pix_helper ( $this );
2017-02-17 01:15:46 +00:00
$shortentexthelper = new \core\output\mustache_shorten_text_helper ();
2017-02-21 05:44:57 +00:00
$userdatehelper = new \core\output\mustache_user_date_helper ();
2015-02-22 15:24:44 +08:00
// We only expose the variables that are exposed to JS templates.
$safeconfig = $this -> page -> requires -> get_config_for_javascript ( $this -> page , $this );
$helpers = array ( 'config' => $safeconfig ,
'str' => array ( $stringhelper , 'str' ),
MDL-52136 core: Add support for quoting variables in mustache helpers
This is required for when helpers include json-encoded variables as arguments.
As an example, imagine a template with content:
{{# str }} somekey, someidentifier, { "fullname": "{{ fullname }}" } {{/ str }}
If the fullname variable were to include the double-quote character (e.g.
John "Trevor" Doe) because of the way in which mustache renders content, it
would become:
{{# str }} somekey, someidentifier, { "fullname": "John "Trevor" Doe" } {{/ str }}
This results in an invalid JSON structure.
To work around this issue, the quote characters in the passed variable
must be escaped:
{{# str }} somekey, someidentifier, { "fullname": "John \"Trevor\" Doe" } {{/ str }}
Unfortunately, Mustache provides no way of doing so natively.
With this function, we can quote the text as appropriate:
{{# str }} somekey, someidentifier, { "fullname": {{# quote }}{{ fullname }}{{/ quote }} } {{/ str }}
This also handles the case where the quoted content includes the Mustache
delimeter ({{ or }}).
For example:
fullname = 'John "}}Trevor{{" Doe'
Ordinarily this would be rendered as:
{{# str }} somekey, someidentifier, { "fullname": "John "}}Trevor{{" Doe" } {{/ str }}
This rendering is both a JSON error, and also a mustache syntax error because of the mustache delimeters.
The quote helper also escapes these by wrapping them in change delimeter
tags:
{{# str }} somekey, someidentifier, { "fullname": "John "{{=<% %>=}}}}<%={{ }}=%>Trevor{{=<% %>=}}{{{{=<% %>=}}" Doe" } {{/ str }}
2016-02-12 12:42:41 +08:00
'quote' => array ( $quotehelper , 'quote' ),
2015-02-22 15:24:44 +08:00
'js' => array ( $jshelper , 'help' ),
2017-02-17 01:15:46 +00:00
'pix' => array ( $pixhelper , 'pix' ),
2017-03-08 11:54:24 +08:00
'shortentext' => array ( $shortentexthelper , 'shorten' ),
'userdate' => array ( $userdatehelper , 'transform' ),
);
2015-02-22 15:24:44 +08:00
2018-05-07 14:21:38 +08:00
$this -> mustache = new \core\output\mustache_engine ( array (
2015-02-22 15:24:44 +08:00
'cache' => $cachedir ,
'escape' => 's' ,
'loader' => $loader ,
2015-07-22 13:02:01 +08:00
'helpers' => $helpers ,
2018-05-07 14:21:38 +08:00
'pragmas' => [ Mustache_Engine :: PRAGMA_BLOCKS ],
// Don't allow the JavaScript helper to be executed from within another
// helper. If it's allowed it can be used by users to inject malicious
// JS into the page.
2020-09-16 13:17:04 +02:00
'disallowednestedhelpers' => [ 'js' ]));
2015-02-22 15:24:44 +08:00
}
return $this -> mustache ;
}
2009-08-10 06:22:04 +00:00
/**
* Constructor
2012-01-05 13:28:08 +13:00
*
2012-01-12 12:55:50 +13:00
* The constructor takes two arguments . The first is the page that the renderer
2012-01-05 13:28:08 +13:00
* has been created to assist with , and the second is the target .
* The target is an additional identifier that can be used to load different
* renderers for different options .
*
2009-08-10 06:22:04 +00:00
* @ param moodle_page $page the page we are doing output for .
2009-12-17 22:43:27 +00:00
* @ param string $target one of rendering target constants
2009-08-10 06:22:04 +00:00
*/
2009-12-17 22:43:27 +00:00
public function __construct ( moodle_page $page , $target ) {
2009-08-10 06:22:04 +00:00
$this -> opencontainers = $page -> opencontainers ;
$this -> page = $page ;
2009-12-17 22:43:27 +00:00
$this -> target = $target ;
2009-08-10 06:22:04 +00:00
}
2015-02-22 15:24:44 +08:00
/**
* Renders a template by name with the given context .
*
* The provided data needs to be array / stdClass made up of only simple types .
* Simple types are array , stdClass , bool , int , float , string
*
* @ since 2.9
* @ param array | stdClass $context Context containing data for the template .
* @ return string | boolean
*/
public function render_from_template ( $templatename , $context ) {
static $templatecache = array ();
$mustache = $this -> get_mustache ();
2016-06-16 07:47:53 +00:00
try {
// Grab a copy of the existing helper to be restored later.
2016-09-20 17:23:22 +08:00
$uniqidhelper = $mustache -> getHelper ( 'uniqid' );
2016-06-16 07:47:53 +00:00
} catch ( Mustache_Exception_UnknownHelperException $e ) {
// Helper doesn't exist.
2016-09-20 17:23:22 +08:00
$uniqidhelper = null ;
2016-06-16 07:47:53 +00:00
}
2015-02-22 15:24:44 +08:00
// Provide 1 random value that will not change within a template
// but will be different from template to template. This is useful for
// e.g. aria attributes that only work with id attributes and must be
// unique in a page.
$mustache -> addHelper ( 'uniqid' , new \core\output\mustache_uniqid_helper ());
if ( isset ( $templatecache [ $templatename ])) {
$template = $templatecache [ $templatename ];
} else {
try {
$template = $mustache -> loadTemplate ( $templatename );
$templatecache [ $templatename ] = $template ;
} catch ( Mustache_Exception_UnknownTemplateException $e ) {
throw new moodle_exception ( 'Unknown template: ' . $templatename );
}
}
2016-06-16 07:41:02 +00:00
2016-09-20 17:23:22 +08:00
$renderedtemplate = trim ( $template -> render ( $context ));
2016-06-16 07:41:02 +00:00
// If we had an existing uniqid helper then we need to restore it to allow
// handle nested calls of render_from_template.
2016-09-20 17:23:22 +08:00
if ( $uniqidhelper ) {
$mustache -> addHelper ( 'uniqid' , $uniqidhelper );
2016-06-16 07:41:02 +00:00
}
2016-09-20 17:23:22 +08:00
return $renderedtemplate ;
2015-02-22 15:24:44 +08:00
}
2009-08-10 06:22:04 +00:00
/**
2010-01-13 17:13:52 +00:00
* Returns rendered widget .
2012-01-05 13:28:08 +13:00
*
* The provided widget needs to be an object that extends the renderable
* interface .
2012-01-12 12:55:50 +13:00
* If will then be rendered by a method based upon the classname for the widget .
2012-01-05 13:28:08 +13:00
* For instance a widget of class ` crazywidget ` will be rendered by a protected
* render_crazywidget method of this renderer .
2018-04-05 09:36:48 +01:00
* If no render_crazywidget method exists and crazywidget implements templatable ,
* look for the 'crazywidget' template in the same component and render that .
2012-01-05 13:28:08 +13:00
*
2010-05-22 14:43:46 +00:00
* @ param renderable $widget instance with renderable interface
2010-01-13 17:13:52 +00:00
* @ return string
2009-08-10 06:22:04 +00:00
*/
2010-01-13 17:13:52 +00:00
public function render ( renderable $widget ) {
2018-04-05 09:36:48 +01:00
$classparts = explode ( '\\' , get_class ( $widget ));
2014-05-12 12:16:26 +08:00
// Strip namespaces.
2018-04-05 09:36:48 +01:00
$classname = array_pop ( $classparts );
2014-05-12 12:16:26 +08:00
// Remove _renderable suffixes
$classname = preg_replace ( '/_renderable$/' , '' , $classname );
$rendermethod = 'render_' . $classname ;
2010-01-13 17:13:52 +00:00
if ( method_exists ( $this , $rendermethod )) {
return $this -> $rendermethod ( $widget );
}
2018-04-05 09:36:48 +01:00
if ( $widget instanceof templatable ) {
$component = array_shift ( $classparts );
if ( ! $component ) {
$component = 'core' ;
}
$template = $component . '/' . $classname ;
$context = $widget -> export_for_template ( $this );
return $this -> render_from_template ( $template , $context );
}
2010-01-13 17:13:52 +00:00
throw new coding_exception ( 'Can not render widget, renderer method (' . $rendermethod . ') not found.' );
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Adds a JS action for the element with the provided id .
*
* This method adds a JS event for the provided component action to the page
* and then returns the id that the event has been attached to .
2012-02-14 18:24:09 +13:00
* If no id has been provided then a new ID is generated by { @ link html_writer :: random_id ()}
2012-01-05 13:28:08 +13:00
*
2012-02-14 18:24:09 +13:00
* @ param component_action $action
2010-02-11 15:44:06 +00:00
* @ param string $id
* @ return string id of element , either original submitted or random new if not supplied
2009-08-10 06:22:04 +00:00
*/
2012-01-05 13:28:08 +13:00
public function add_action_handler ( component_action $action , $id = null ) {
2010-02-11 15:44:06 +00:00
if ( ! $id ) {
$id = html_writer :: random_id ( $action -> event );
}
2010-01-19 22:01:12 +00:00
$this -> page -> requires -> event_handler ( " # $id " , $action -> event , $action -> jsfunction , $action -> jsfunctionargs );
2010-02-11 15:44:06 +00:00
return $id ;
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Returns true is output has already started , and false if not .
*
2010-01-13 17:13:52 +00:00
* @ return boolean true if the header has been printed .
2009-08-10 06:22:04 +00:00
*/
2010-01-13 17:13:52 +00:00
public function has_started () {
return $this -> page -> state >= moodle_page :: STATE_IN_BODY ;
2009-08-10 06:22:04 +00:00
}
/**
* Given an array or space - separated list of classes , prepares and returns the HTML class attribute value
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param mixed $classes Space - separated string or array of classes
* @ return string HTML class attribute value
*/
public static function prepare_classes ( $classes ) {
if ( is_array ( $classes )) {
return implode ( ' ' , array_unique ( $classes ));
}
return $classes ;
}
2017-04-07 17:22:06 +08:00
/**
* Return the direct URL for an image from the pix folder .
*
* Use this function sparingly and never for icons . For icons use pix_icon or the pix helper in a mustache template .
*
* @ deprecated since Moodle 3.3
* @ param string $imagename the name of the icon .
* @ param string $component specification of one plugin like in get_string ()
* @ return moodle_url
*/
2017-01-03 10:40:42 +08:00
public function pix_url ( $imagename , $component = 'moodle' ) {
2017-04-07 17:22:06 +08:00
debugging ( 'pix_url is deprecated. Use image_url for images and pix_icon for icons.' , DEBUG_DEVELOPER );
2017-01-03 10:40:42 +08:00
return $this -> page -> theme -> image_url ( $imagename , $component );
}
2009-08-10 06:22:04 +00:00
/**
2010-01-04 14:40:34 +00:00
* Return the moodle_url for an image .
2012-01-05 13:28:08 +13:00
*
2010-01-04 14:40:34 +00:00
* The exact image location and extension is determined
* automatically by searching for gif | png | jpg | jpeg , please
* note there can not be diferent images with the different
* extension . The imagename is for historical reasons
* a relative path name , it may be changed later for core
* images . It is recommended to not use subdirectories
* in plugin and theme pix directories .
2009-08-10 06:22:04 +00:00
*
2010-01-04 14:40:34 +00:00
* There are three types of images :
* 1 / theme images - stored in theme / mytheme / pix / ,
* use component 'theme'
* 2 / core images - stored in / pix / ,
* overridden via theme / mytheme / pix_core /
* 3 / plugin images - stored in mod / mymodule / pix ,
* overridden via theme / mytheme / pix_plugins / mod / mymodule / ,
2017-01-03 10:40:42 +08:00
* example : image_url ( 'comment' , 'mod_glossary' )
2010-01-04 14:40:34 +00:00
*
* @ param string $imagename the pathname of the image
* @ param string $component full plugin name ( aka component ) or 'theme'
2009-12-16 18:00:58 +00:00
* @ return moodle_url
2009-08-10 06:22:04 +00:00
*/
2017-01-03 10:40:42 +08:00
public function image_url ( $imagename , $component = 'moodle' ) {
return $this -> page -> theme -> image_url ( $imagename , $component );
2009-08-10 06:22:04 +00:00
}
2016-08-03 17:43:23 +08:00
/**
* Return the site ' s logo URL , if any .
*
* @ param int $maxwidth The maximum width , or null when the maximum width does not matter .
* @ param int $maxheight The maximum height , or null when the maximum height does not matter .
* @ return moodle_url | false
*/
2016-11-28 15:06:11 +08:00
public function get_logo_url ( $maxwidth = null , $maxheight = 200 ) {
2016-08-03 17:43:23 +08:00
global $CFG ;
$logo = get_config ( 'core_admin' , 'logo' );
if ( empty ( $logo )) {
return false ;
}
2016-11-28 15:06:11 +08:00
// 200px high is the default image size which should be displayed at 100px in the page to account for retina displays.
// It's not worth the overhead of detecting and serving 2 different images based on the device.
2016-08-03 17:43:23 +08:00
// Hide the requested size in the file path.
$filepath = (( int ) $maxwidth . 'x' . ( int ) $maxheight ) . '/' ;
// Use $CFG->themerev to prevent browser caching when the file changes.
return moodle_url :: make_pluginfile_url ( context_system :: instance () -> id , 'core_admin' , 'logo' , $filepath ,
theme_get_revision (), $logo );
}
/**
* Return the site ' s compact logo URL , if any .
*
* @ param int $maxwidth The maximum width , or null when the maximum width does not matter .
* @ param int $maxheight The maximum height , or null when the maximum height does not matter .
* @ return moodle_url | false
*/
2020-04-07 16:49:21 +02:00
public function get_compact_logo_url ( $maxwidth = 300 , $maxheight = 300 ) {
2016-08-03 17:43:23 +08:00
global $CFG ;
$logo = get_config ( 'core_admin' , 'logocompact' );
if ( empty ( $logo )) {
return false ;
}
// Hide the requested size in the file path.
$filepath = (( int ) $maxwidth . 'x' . ( int ) $maxheight ) . '/' ;
// Use $CFG->themerev to prevent browser caching when the file changes.
return moodle_url :: make_pluginfile_url ( context_system :: instance () -> id , 'core_admin' , 'logocompact' , $filepath ,
theme_get_revision (), $logo );
}
2019-02-13 16:29:22 +08:00
/**
* Whether we should display the logo in the navbar .
*
* We will when there are no main logos , and we have compact logo .
*
* @ return bool
*/
public function should_display_navbar_logo () {
$logo = $this -> get_compact_logo_url ();
return ! empty ( $logo ) && ! $this -> should_display_main_logo ();
}
/**
* Whether we should display the main logo .
*
2019-04-08 16:32:36 +08:00
* @ param int $headinglevel The heading level we want to check against .
2019-02-13 16:29:22 +08:00
* @ return bool
*/
public function should_display_main_logo ( $headinglevel = 1 ) {
// Only render the logo if we're on the front page or login page and the we have a logo.
$logo = $this -> get_logo_url ();
if ( $headinglevel == 1 && ! empty ( $logo )) {
2020-03-24 13:32:22 +00:00
if ( $this -> page -> pagelayout == 'frontpage' || $this -> page -> pagelayout == 'login' ) {
2019-02-13 16:29:22 +08:00
return true ;
}
}
return false ;
}
2009-08-10 06:22:04 +00:00
}
2009-12-17 22:43:27 +00:00
2009-12-17 14:51:36 +00:00
/**
* Basis for all plugin renderers .
*
2012-02-14 18:24:09 +13:00
* @ copyright Petr Skoda ( skodak )
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
* @ since Moodle 2.0
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2009-12-17 14:51:36 +00:00
*/
class plugin_renderer_base extends renderer_base {
2012-01-05 13:28:08 +13:00
2009-12-17 14:51:36 +00:00
/**
2012-01-12 12:55:50 +13:00
* @ var renderer_base | core_renderer A reference to the current renderer .
* The renderer provided here will be determined by the page but will in 90 %
2012-02-14 18:24:09 +13:00
* of cases by the { @ link core_renderer }
2009-12-17 14:51:36 +00:00
*/
protected $output ;
/**
2010-05-22 14:43:46 +00:00
* Constructor method , calls the parent constructor
2012-01-05 13:28:08 +13:00
*
2009-12-17 14:51:36 +00:00
* @ param moodle_page $page
2009-12-17 22:43:27 +00:00
* @ param string $target one of rendering target constants
2009-12-17 14:51:36 +00:00
*/
2009-12-17 22:43:27 +00:00
public function __construct ( moodle_page $page , $target ) {
2013-09-12 09:51:58 +12:00
if ( empty ( $target ) && $page -> pagelayout === 'maintenance' ) {
// If the page is using the maintenance layout then we're going to force the target to maintenance.
// This way we'll get a special maintenance renderer that is designed to block access to API's that are likely
// unavailable for this page layout.
$target = RENDERER_TARGET_MAINTENANCE ;
}
2009-12-17 22:43:27 +00:00
$this -> output = $page -> get_renderer ( 'core' , null , $target );
parent :: __construct ( $page , $target );
2009-12-17 14:51:36 +00:00
}
2009-12-17 14:54:04 +00:00
2010-01-13 17:13:52 +00:00
/**
2012-01-05 13:28:08 +13:00
* Renders the provided widget and returns the HTML to display it .
*
2010-05-22 20:16:12 +00:00
* @ param renderable $widget instance with renderable interface
2010-01-13 17:13:52 +00:00
* @ return string
*/
public function render ( renderable $widget ) {
2014-05-12 12:16:26 +08:00
$classname = get_class ( $widget );
// Strip namespaces.
$classname = preg_replace ( '/^.*\\\/' , '' , $classname );
2014-07-02 13:51:45 +12:00
// Keep a copy at this point, we may need to look for a deprecated method.
$deprecatedmethod = 'render_' . $classname ;
2014-05-12 12:16:26 +08:00
// Remove _renderable suffixes
2014-07-02 13:51:45 +12:00
$classname = preg_replace ( '/_renderable$/' , '' , $classname );
2014-05-12 12:16:26 +08:00
$rendermethod = 'render_' . $classname ;
2010-01-13 17:13:52 +00:00
if ( method_exists ( $this , $rendermethod )) {
return $this -> $rendermethod ( $widget );
}
2014-07-02 13:51:45 +12:00
if ( $rendermethod !== $deprecatedmethod && method_exists ( $this , $deprecatedmethod )) {
// This is exactly where we don't want to be.
// If you have arrived here you have a renderable component within your plugin that has the name
// blah_renderable, and you have a render method render_blah_renderable on your plugin.
// In 2.8 we revamped output, as part of this change we changed slightly how renderables got rendered
// and the _renderable suffix now gets removed when looking for a render method.
// You need to change your renderers render_blah_renderable to render_blah.
// Until you do this it will not be possible for a theme to override the renderer to override your method.
// Please do it ASAP.
static $debugged = array ();
if ( ! isset ( $debugged [ $deprecatedmethod ])) {
debugging ( sprintf ( 'Deprecated call. Please rename your renderables render method from %s to %s.' ,
$deprecatedmethod , $rendermethod ), DEBUG_DEVELOPER );
$debugged [ $deprecatedmethod ] = true ;
}
return $this -> $deprecatedmethod ( $widget );
}
2010-01-13 17:13:52 +00:00
// pass to core renderer if method not found here
2010-07-08 16:32:19 +00:00
return $this -> output -> render ( $widget );
2010-01-13 17:13:52 +00:00
}
2009-12-17 14:54:04 +00:00
/**
* Magic method used to pass calls otherwise meant for the standard renderer
2010-05-22 14:43:46 +00:00
* to it to ensure we don ' t go causing unnecessary grief .
2009-12-17 14:54:04 +00:00
*
* @ param string $method
* @ param array $arguments
* @ return mixed
*/
public function __call ( $method , $arguments ) {
2009-12-20 16:34:18 +00:00
if ( method_exists ( 'renderer_base' , $method )) {
2012-06-27 15:27:50 +01:00
throw new coding_exception ( 'Protected method called against ' . get_class ( $this ) . ' :: ' . $method );
2009-12-20 16:34:18 +00:00
}
2009-12-17 14:54:04 +00:00
if ( method_exists ( $this -> output , $method )) {
return call_user_func_array ( array ( $this -> output , $method ), $arguments );
} else {
2012-06-27 15:27:50 +01:00
throw new coding_exception ( 'Unknown method called against ' . get_class ( $this ) . ' :: ' . $method );
2009-12-17 14:54:04 +00:00
}
}
2009-12-17 14:51:36 +00:00
}
2009-08-10 06:22:04 +00:00
2009-12-17 22:43:27 +00:00
2009-08-10 06:22:04 +00:00
/**
2009-12-16 18:00:58 +00:00
* The standard implementation of the core_renderer interface .
2009-08-10 06:22:04 +00:00
*
* @ copyright 2009 Tim Hunt
2012-01-05 13:28:08 +13:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
* @ since Moodle 2.0
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2009-08-10 06:22:04 +00:00
*/
2009-12-16 18:00:58 +00:00
class core_renderer extends renderer_base {
2011-08-21 21:01:31 +02:00
/**
* Do NOT use , please use < ? php echo $OUTPUT -> main_content () ?>
* in layout files instead .
* @ deprecated
2012-02-14 18:24:09 +13:00
* @ var string used in { @ link core_renderer :: header ()} .
2011-08-21 21:01:31 +02:00
*/
2009-08-10 06:22:04 +00:00
const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]' ;
2012-01-05 13:28:08 +13:00
/**
2012-02-14 18:24:09 +13:00
* @ var string Used to pass information from { @ link core_renderer :: doctype ()} to
* { @ link core_renderer :: standard_head_html ()} .
2012-01-05 13:28:08 +13:00
*/
2009-08-10 06:22:04 +00:00
protected $contenttype ;
2012-01-05 13:28:08 +13:00
/**
2012-02-14 18:24:09 +13:00
* @ var string Used by { @ link core_renderer :: redirect_message ()} method to communicate
* with { @ link core_renderer :: header ()} .
2012-01-05 13:28:08 +13:00
*/
2009-08-10 06:22:04 +00:00
protected $metarefreshtag = '' ;
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var string Unique token for the closing HTML
2012-01-05 13:28:08 +13:00
*/
2011-08-21 21:01:31 +02:00
protected $unique_end_html_token ;
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var string Unique token for performance information
2012-01-05 13:28:08 +13:00
*/
2011-08-21 21:01:31 +02:00
protected $unique_performance_info_token ;
2012-01-05 13:28:08 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var string Unique token for the main content .
2012-01-05 13:28:08 +13:00
*/
2011-08-21 21:01:31 +02:00
protected $unique_main_content_token ;
2019-02-13 16:29:22 +08:00
/** @var custom_menu_item language The language menu if created */
protected $language = null ;
2011-08-21 21:01:31 +02:00
/**
* Constructor
2012-01-05 13:28:08 +13:00
*
2011-08-21 21:01:31 +02:00
* @ param moodle_page $page the page we are doing output for .
* @ param string $target one of rendering target constants
*/
public function __construct ( moodle_page $page , $target ) {
$this -> opencontainers = $page -> opencontainers ;
$this -> page = $page ;
$this -> target = $target ;
$this -> unique_end_html_token = '%%ENDHTML-' . sesskey () . '%%' ;
$this -> unique_performance_info_token = '%%PERFORMANCEINFO-' . sesskey () . '%%' ;
$this -> unique_main_content_token = '[MAIN CONTENT GOES HERE - ' . sesskey () . ']' ;
}
2009-08-10 06:22:04 +00:00
/**
* Get the DOCTYPE declaration that should be used with this page . Designed to
* be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2012-07-21 19:23:44 +02:00
* @ return string the DOCTYPE declaration that should be used .
2009-08-10 06:22:04 +00:00
*/
public function doctype () {
2012-07-21 19:23:44 +02:00
if ( $this -> page -> theme -> doctype === 'html5' ) {
$this -> contenttype = 'text/html; charset=utf-8' ;
return " <!DOCTYPE html> \n " ;
2009-08-10 06:22:04 +00:00
2012-07-21 19:23:44 +02:00
} else if ( $this -> page -> theme -> doctype === 'xhtml5' ) {
2009-08-10 06:22:04 +00:00
$this -> contenttype = 'application/xhtml+xml; charset=utf-8' ;
2012-07-21 19:23:44 +02:00
return " <!DOCTYPE html> \n " ;
2009-08-10 06:22:04 +00:00
} else {
2012-07-21 19:23:44 +02:00
// legacy xhtml 1.0
$this -> contenttype = 'text/html; charset=utf-8' ;
return ( '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . " \n " );
2009-08-10 06:22:04 +00:00
}
}
/**
* The attributes that should be added to the < html > tag . Designed to
* be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function htmlattributes () {
2012-07-21 19:23:44 +02:00
$return = get_html_lang ( true );
2016-10-06 22:27:58 +11:00
$attributes = array ();
2012-07-21 19:23:44 +02:00
if ( $this -> page -> theme -> doctype !== 'html5' ) {
2016-10-06 22:27:58 +11:00
$attributes [ 'xmlns' ] = 'http://www.w3.org/1999/xhtml' ;
2012-07-21 19:23:44 +02:00
}
2016-10-06 22:27:58 +11:00
// Give plugins an opportunity to add things like xml namespaces to the html element.
// This function should return an array of html attribute names => values.
$pluginswithfunction = get_plugins_with_function ( 'add_htmlattributes' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$newattrs = $function ();
unset ( $newattrs [ 'dir' ]);
unset ( $newattrs [ 'lang' ]);
unset ( $newattrs [ 'xmlns' ]);
unset ( $newattrs [ 'xml:lang' ]);
$attributes += $newattrs ;
}
}
foreach ( $attributes as $key => $val ) {
$val = s ( $val );
$return .= " $key = \" $val\ " " ;
}
2012-07-21 19:23:44 +02:00
return $return ;
2009-08-10 06:22:04 +00:00
}
/**
* The standard tags ( meta tags , links to stylesheets and JavaScript , etc . )
* that should be included in the < head > tag . Designed to be called in theme
* layout . php files .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function standard_head_html () {
2020-03-24 13:32:22 +00:00
global $CFG , $SESSION , $SITE ;
2013-09-01 23:52:58 +01:00
// Before we output any content, we need to ensure that certain
// page components are set up.
// Blocks must be set up early as they may require javascript which
// has to be included in the page header before output is created.
foreach ( $this -> page -> blocks -> get_regions () as $region ) {
$this -> page -> blocks -> ensure_content_created ( $region , $this );
}
2009-08-10 06:22:04 +00:00
$output = '' ;
2016-02-05 19:45:00 +11:00
2016-10-06 22:27:58 +11:00
// Give plugins an opportunity to add any head elements. The callback
// must always return a string containing valid html head content.
$pluginswithfunction = get_plugins_with_function ( 'before_standard_html_head' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$output .= $function ();
}
}
2016-02-05 19:45:00 +11:00
// Allow a url_rewrite plugin to setup any dynamic head content.
if ( isset ( $CFG -> urlrewriteclass ) && ! isset ( $CFG -> upgraderunning )) {
$class = $CFG -> urlrewriteclass ;
$output .= $class :: html_head_setup ();
}
2009-08-10 06:22:04 +00:00
$output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . " \n " ;
$output .= '<meta name="keywords" content="moodle, ' . $this -> page -> title . '" />' . " \n " ;
2012-02-14 18:24:09 +13:00
// This is only set by the {@link redirect()} method
2009-08-10 06:22:04 +00:00
$output .= $this -> metarefreshtag ;
// Check if a periodic refresh delay has been set and make sure we arn't
// already meta refreshing
if ( $this -> metarefreshtag == '' && $this -> page -> periodicrefreshdelay !== null ) {
$output .= '<meta http-equiv="refresh" content="' . $this -> page -> periodicrefreshdelay . ';url=' . $this -> page -> url -> out () . '" />' ;
}
2012-10-10 11:03:00 +01:00
// Set up help link popups for all links with the helptooltip class
2012-10-07 09:55:08 +01:00
$this -> page -> requires -> js_init_call ( 'M.util.help_popups.setup' );
2009-08-10 06:22:04 +00:00
$focus = $this -> page -> focuscontrol ;
if ( ! empty ( $focus )) {
if ( preg_match ( " #forms \ ['([a-zA-Z0-9]+)' \ ].elements \ ['([a-zA-Z0-9]+)' \ ]# " , $focus , $matches )) {
// This is a horrifically bad way to handle focus but it is passed in
// through messy formslib::moodleform
2009-08-28 08:47:31 +00:00
$this -> page -> requires -> js_function_call ( 'old_onload_focus' , array ( $matches [ 1 ], $matches [ 2 ]));
2009-08-10 06:22:04 +00:00
} else if ( strpos ( $focus , '.' ) !== false ) {
// Old style of focus, bad way to do it
debugging ( 'This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.' , DEBUG_DEVELOPER );
$this -> page -> requires -> js_function_call ( 'old_onload_focus' , explode ( '.' , $focus , 2 ));
} else {
// Focus element with given id
2009-08-28 08:47:31 +00:00
$this -> page -> requires -> js_function_call ( 'focuscontrol' , array ( $focus ));
2009-08-10 06:22:04 +00:00
}
}
2009-12-16 18:00:58 +00:00
// Get the theme stylesheet - this has to be always first CSS, this loads also styles.css from all plugins;
// any other custom CSS can not be overridden via themes and is highly discouraged
2010-01-05 00:33:15 +00:00
$urls = $this -> page -> theme -> css_urls ( $this -> page );
2009-12-16 18:00:58 +00:00
foreach ( $urls as $url ) {
2010-01-18 20:02:13 +00:00
$this -> page -> requires -> css_theme ( $url );
2009-12-16 18:00:58 +00:00
}
2010-01-05 23:22:16 +00:00
// Get the theme javascript head and footer
2012-11-05 14:03:04 +00:00
if ( $jsurl = $this -> page -> theme -> javascript_url ( true )) {
$this -> page -> requires -> js ( $jsurl , true );
}
if ( $jsurl = $this -> page -> theme -> javascript_url ( false )) {
$this -> page -> requires -> js ( $jsurl );
}
2010-01-13 17:13:52 +00:00
2009-08-10 06:22:04 +00:00
// Get any HTML from the page_requirements_manager.
2009-12-28 23:08:55 +00:00
$output .= $this -> page -> requires -> get_head_code ( $this -> page , $this );
2009-08-10 06:22:04 +00:00
// List alternate versions.
foreach ( $this -> page -> alternateversions as $type => $alt ) {
2010-01-13 17:13:52 +00:00
$output .= html_writer :: empty_tag ( 'link' , array ( 'rel' => 'alternate' ,
2009-08-10 06:22:04 +00:00
'type' => $type , 'title' => $alt -> title , 'href' => $alt -> url ));
}
2011-01-14 09:32:17 +01:00
2017-06-16 16:13:37 +01:00
// Add noindex tag if relevant page and setting applied.
$allowindexing = isset ( $CFG -> allowindexing ) ? $CFG -> allowindexing : 0 ;
$loginpages = array ( 'login-index' , 'login-signup' );
if ( $allowindexing == 2 || ( $allowindexing == 0 && in_array ( $this -> page -> pagetype , $loginpages ))) {
if ( ! isset ( $CFG -> additionalhtmlhead )) {
$CFG -> additionalhtmlhead = '' ;
}
$CFG -> additionalhtmlhead .= '<meta name="robots" content="noindex" />' ;
}
2010-12-07 08:48:38 +00:00
if ( ! empty ( $CFG -> additionalhtmlhead )) {
$output .= " \n " . $CFG -> additionalhtmlhead ;
}
2009-08-10 06:22:04 +00:00
2020-03-24 13:32:22 +00:00
if ( $this -> page -> pagelayout == 'frontpage' ) {
2019-02-13 16:29:22 +08:00
$summary = s ( strip_tags ( format_text ( $SITE -> summary , FORMAT_HTML )));
if ( ! empty ( $summary )) {
$output .= " <meta name= \" description \" content= \" $summary\ " /> \n " ;
}
}
2009-08-10 06:22:04 +00:00
return $output ;
}
/**
* The standard tags ( typically skip links ) that should be output just inside
* the start of the < body > tag . Designed to be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function standard_top_of_body_html () {
2010-12-07 08:48:38 +00:00
global $CFG ;
2016-07-15 17:02:22 +08:00
$output = $this -> page -> requires -> get_top_of_body_code ( $this );
2016-06-08 13:16:07 +08:00
if ( $this -> page -> pagelayout !== 'embedded' && ! empty ( $CFG -> additionalhtmltopofbody )) {
2010-12-07 08:48:38 +00:00
$output .= " \n " . $CFG -> additionalhtmltopofbody ;
}
2016-10-06 22:27:58 +11:00
2018-10-16 11:27:50 +08:00
// Give subsystems an opportunity to inject extra html content. The callback
// must always return a string containing valid html.
foreach ( \core_component :: get_core_subsystems () as $name => $path ) {
if ( $path ) {
$output .= component_callback ( $name , 'before_standard_top_of_body_html' , [], '' );
}
}
2016-10-06 22:27:58 +11:00
// Give plugins an opportunity to inject extra html content. The callback
// must always return a string containing valid html.
$pluginswithfunction = get_plugins_with_function ( 'before_standard_top_of_body_html' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$output .= $function ();
}
}
2013-01-21 16:51:28 +01:00
$output .= $this -> maintenance_warning ();
2016-10-06 22:27:58 +11:00
2013-01-21 16:51:28 +01:00
return $output ;
}
/**
* Scheduled maintenance warning message .
*
* Note : This is a nasty hack to display maintenance notice , this should be moved
* to some general notification area once we have it .
*
* @ return string
*/
public function maintenance_warning () {
global $CFG ;
$output = '' ;
if ( isset ( $CFG -> maintenance_later ) and $CFG -> maintenance_later > time ()) {
2013-11-20 09:05:01 +08:00
$timeleft = $CFG -> maintenance_later - time ();
// If timeleft less than 30 sec, set the class on block to error to highlight.
2016-10-27 10:22:36 +08:00
$errorclass = ( $timeleft < 30 ) ? 'alert-error alert-danger' : 'alert-warning' ;
2020-01-27 15:06:29 +01:00
$output .= $this -> box_start ( $errorclass . ' moodle-has-zindex maintenancewarning m-3 alert' );
2013-11-20 09:05:01 +08:00
$a = new stdClass ();
2016-10-13 14:50:07 -04:00
$a -> hour = ( int )( $timeleft / 3600 );
$a -> min = ( int )(( $timeleft / 60 ) % 60 );
2013-11-20 09:05:01 +08:00
$a -> sec = ( int )( $timeleft % 60 );
2016-10-13 14:50:07 -04:00
if ( $a -> hour > 0 ) {
$output .= get_string ( 'maintenancemodeisscheduledlong' , 'admin' , $a );
} else {
$output .= get_string ( 'maintenancemodeisscheduled' , 'admin' , $a );
}
2013-01-21 16:51:28 +01:00
$output .= $this -> box_end ();
2013-11-20 09:05:01 +08:00
$this -> page -> requires -> yui_module ( 'moodle-core-maintenancemodetimer' , 'M.core.maintenancemodetimer' ,
array ( array ( 'timeleftinsec' => $timeleft )));
$this -> page -> requires -> strings_for_js (
2016-10-13 14:50:07 -04:00
array ( 'maintenancemodeisscheduled' , 'maintenancemodeisscheduledlong' , 'sitemaintenance' ),
2013-11-20 09:05:01 +08:00
'admin' );
2013-01-21 16:51:28 +01:00
}
2010-12-07 08:48:38 +00:00
return $output ;
2009-08-10 06:22:04 +00:00
}
/**
2021-07-01 15:57:29 +02:00
* content that should be output in the footer area
2009-08-10 06:22:04 +00:00
* of the page . Designed to be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function standard_footer_html () {
2010-11-20 13:22:27 +01:00
global $CFG , $SCRIPT ;
2009-08-10 06:22:04 +00:00
2017-06-20 12:03:22 +08:00
$output = '' ;
2013-04-24 12:50:45 +02:00
if ( during_initial_install ()) {
// Debugging info can not work before install is finished,
// in any case we do not want any links during installation!
2017-06-20 12:03:22 +08:00
return $output ;
}
// Give plugins an opportunity to add any footer elements.
// The callback must always return a string containing valid html footer content.
$pluginswithfunction = get_plugins_with_function ( 'standard_footer_html' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$output .= $function ();
}
2013-04-24 12:50:45 +02:00
}
2020-06-05 19:48:07 +10:00
if ( core_userfeedback :: can_give_feedback ()) {
2020-05-03 16:04:05 +10:00
$output .= html_writer :: div (
$this -> render_from_template ( 'core/userfeedback_footer_link' , [ 'url' => core_userfeedback :: make_link () -> out ( false )])
);
}
2011-05-31 14:28:03 +08:00
if ( $this -> page -> devicetypeinuse == 'legacy' ) {
2010-04-21 04:21:53 +00:00
// The legacy theme is in use print the notification
$output .= html_writer :: tag ( 'div' , get_string ( 'legacythemeinuse' ), array ( 'class' => 'legacythemeinuse' ));
}
2011-05-31 14:25:52 +08:00
2011-05-31 14:28:03 +08:00
// Get links to switch device types (only shown for users not on a default device)
2011-05-31 14:25:52 +08:00
$output .= $this -> theme_switch_links ();
2021-07-01 15:57:29 +02:00
return $output ;
}
/**
* Performance information and validation links for debugging .
*
* @ return string HTML fragment .
*/
public function debug_footer_html () {
global $CFG ;
$output = '' ;
// This function is normally called from a layout.php file
// but some of the content won't be known until later, so we return a placeholder
// for now. This will be replaced with the real content in the footer.
$output .= $this -> unique_performance_info_token ;
2009-08-10 06:22:04 +00:00
if ( ! empty ( $CFG -> debugpageinfo )) {
2018-05-21 22:55:20 +02:00
$output .= '<div class="performanceinfo pageinfo">' . get_string ( 'pageinfodebugsummary' , 'core_admin' ,
$this -> page -> debug_summary ()) . '</div>' ;
2009-08-10 06:22:04 +00:00
}
2012-07-25 16:25:55 +08:00
if ( debugging ( null , DEBUG_DEVELOPER ) and has_capability ( 'moodle/site:config' , context_system :: instance ())) { // Only in developer mode
2010-11-20 13:22:27 +01:00
// Add link to profiling report if necessary
if ( function_exists ( 'profiling_is_running' ) && profiling_is_running ()) {
$txt = get_string ( 'profiledscript' , 'admin' );
$title = get_string ( 'profiledscriptview' , 'admin' );
2011-09-19 18:54:30 +02:00
$url = $CFG -> wwwroot . '/admin/tool/profiling/index.php?script=' . urlencode ( $SCRIPT );
2010-11-20 13:22:27 +01:00
$link = '<a title="' . $title . '" href="' . $url . '">' . $txt . '</a>' ;
$output .= '<div class="profilingfooter">' . $link . '</div>' ;
}
2013-07-29 16:03:58 +01:00
$purgeurl = new moodle_url ( '/admin/purgecaches.php' , array ( 'confirm' => 1 ,
'sesskey' => sesskey (), 'returnurl' => $this -> page -> url -> out_as_local_url ( false )));
$output .= '<div class="purgecaches">' .
html_writer :: link ( $purgeurl , get_string ( 'purgecaches' , 'admin' )) . '</div>' ;
2010-07-18 07:17:01 +00:00
}
2009-08-10 06:22:04 +00:00
if ( ! empty ( $CFG -> debugvalidators )) {
2020-03-24 14:20:45 +00:00
// NOTE: this is not a nice hack, $this->page->url is not always accurate and
// $FULLME neither, it is not a bug if it fails. --skodak.
2019-03-29 11:39:23 +08:00
$output .= ' < div class = " validators " >< ul class = " list-unstyled ml-1 " >
2009-08-10 06:22:04 +00:00
< li >< a href = " http://validator.w3.org/check?verbose=1&ss=1&uri=' . urlencode(qualified_me()) . ' " > Validate HTML </ a ></ li >
< li >< a href = " http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&url1=' . urlencode(qualified_me()) . ' " > Section 508 Check </ a ></ li >
< li >< a href = " http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=0&warnp2n3e=1&url1=' . urlencode(qualified_me()) . ' " > WCAG 1 ( 2 , 3 ) Check </ a ></ li >
</ ul ></ div > ' ;
}
return $output ;
}
2011-08-21 21:01:31 +02:00
/**
* Returns standard main content placeholder .
* Designed to be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2011-08-21 21:01:31 +02:00
* @ return string HTML fragment .
*/
public function main_content () {
2012-10-19 11:17:48 +08:00
// This is here because it is the only place we can inject the "main" role over the entire main content area
// without requiring all theme's to manually do it, and without creating yet another thing people need to
// remember in the theme.
// This is an unfortunate hack. DO NO EVER add anything more here.
// DO NOT add classes.
// DO NOT add an id.
return '<div role="main">' . $this -> unique_main_content_token . '</div>' ;
2011-08-21 21:01:31 +02:00
}
2021-02-06 01:18:24 +08:00
/**
* Returns information about an activity .
*
* @ param cm_info $cminfo The course module information .
* @ param cm_completion_details $completiondetails The completion details for this activity module .
* @ param array $activitydates The dates for this activity module .
* @ return string the activity information HTML .
* @ throws coding_exception
*/
public function activity_information ( cm_info $cminfo , cm_completion_details $completiondetails , array $activitydates ) : string {
2021-04-15 23:10:03 +08:00
if ( ! $completiondetails -> has_completion () && empty ( $activitydates )) {
// No need to render the activity information when there's no completion info and activity dates to show.
return '' ;
}
2021-02-06 01:18:24 +08:00
$activityinfo = new activity_information ( $cminfo , $completiondetails , $activitydates );
$renderer = $this -> page -> get_renderer ( 'core' , 'course' );
return $renderer -> render ( $activityinfo );
}
2017-07-26 17:28:01 +08:00
/**
* Returns standard navigation between activities in a course .
*
* @ return string the navigation HTML .
*/
public function activity_navigation () {
// First we should check if we want to add navigation.
$context = $this -> page -> context ;
2017-07-28 17:33:16 +08:00
if (( $this -> page -> pagelayout !== 'incourse' && $this -> page -> pagelayout !== 'frametop' )
|| $context -> contextlevel != CONTEXT_MODULE ) {
2017-07-26 17:28:01 +08:00
return '' ;
}
// If the activity is in stealth mode, show no links.
if ( $this -> page -> cm -> is_stealth ()) {
return '' ;
}
$course = $this -> page -> cm -> get_course ();
2021-09-07 13:22:10 +08:00
$courseformat = course_get_format ( $course );
// If the theme implements course index and the current course format uses course index and the current
// page layout is not 'frametop' (this layout does not support course index), show no links.
if ( $this -> page -> theme -> usescourseindex && $courseformat -> uses_course_index () &&
$this -> page -> pagelayout !== 'frametop' ) {
return '' ;
}
// Get a list of all the activities in the course.
2017-07-26 17:28:01 +08:00
$modules = get_fast_modinfo ( $course -> id ) -> get_cms ();
// Put the modules into an array in order by the position they are shown in the course.
$mods = [];
2017-07-31 10:44:39 +08:00
$activitylist = [];
2017-07-26 17:28:01 +08:00
foreach ( $modules as $module ) {
// Only add activities the user can access, aren't in stealth mode and have a url (eg. mod_label does not).
if ( ! $module -> uservisible || $module -> is_stealth () || empty ( $module -> url )) {
continue ;
}
$mods [ $module -> id ] = $module ;
2017-07-31 10:44:39 +08:00
2017-08-02 16:58:23 +08:00
// No need to add the current module to the list for the activity dropdown menu.
if ( $module -> id == $this -> page -> cm -> id ) {
continue ;
}
2017-07-31 10:44:39 +08:00
// Module name.
2017-08-02 16:58:23 +08:00
$modname = $module -> get_formatted_name ();
2017-07-31 10:44:39 +08:00
// Display the hidden text if necessary.
if ( ! $module -> visible ) {
$modname .= ' ' . get_string ( 'hiddenwithbrackets' );
}
// Module URL.
$linkurl = new moodle_url ( $module -> url , array ( 'forceview' => 1 ));
// Add module URL (as key) and name (as value) to the activity list array.
$activitylist [ $linkurl -> out ( false )] = $modname ;
2017-07-26 17:28:01 +08:00
}
$nummods = count ( $mods );
// If there is only one mod then do nothing.
if ( $nummods == 1 ) {
return '' ;
}
// Get an array of just the course module ids used to get the cmid value based on their position in the course.
$modids = array_keys ( $mods );
// Get the position in the array of the course module we are viewing.
$position = array_search ( $this -> page -> cm -> id , $modids );
$prevmod = null ;
$nextmod = null ;
// Check if we have a previous mod to show.
if ( $position > 0 ) {
$prevmod = $mods [ $modids [ $position - 1 ]];
}
// Check if we have a next mod to show.
if ( $position < ( $nummods - 1 )) {
$nextmod = $mods [ $modids [ $position + 1 ]];
}
2017-07-31 10:44:39 +08:00
$activitynav = new \core_course\output\activity_navigation ( $prevmod , $nextmod , $activitylist );
2017-07-26 17:28:01 +08:00
$renderer = $this -> page -> get_renderer ( 'core' , 'course' );
return $renderer -> render ( $activitynav );
}
2009-08-10 06:22:04 +00:00
/**
* The standard tags ( typically script tags that are not needed earlier ) that
2014-01-23 15:35:53 +00:00
* should be output after everything else . Designed to be called in theme layout . php files .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function standard_end_of_body_html () {
2014-01-23 15:35:53 +00:00
global $CFG ;
2012-02-14 18:24:09 +13:00
// This function is normally called from a layout.php file in {@link core_renderer::header()}
2009-08-10 06:22:04 +00:00
// but some of the content won't be known until later, so we return a placeholder
2012-02-14 18:24:09 +13:00
// for now. This will be replaced with the real content in {@link core_renderer::footer()}.
2014-01-23 15:35:53 +00:00
$output = '' ;
2016-06-08 13:16:07 +08:00
if ( $this -> page -> pagelayout !== 'embedded' && ! empty ( $CFG -> additionalhtmlfooter )) {
2014-01-23 15:35:53 +00:00
$output .= " \n " . $CFG -> additionalhtmlfooter ;
}
$output .= $this -> unique_end_html_token ;
return $output ;
2009-08-10 06:22:04 +00:00
}
2018-11-15 14:32:24 +08:00
/**
* The standard HTML that should be output just before the < footer > tag .
* Designed to be called in theme layout . php files .
*
* @ return string HTML fragment .
*/
public function standard_after_main_region_html () {
global $CFG ;
$output = '' ;
if ( $this -> page -> pagelayout !== 'embedded' && ! empty ( $CFG -> additionalhtmlbottomofbody )) {
$output .= " \n " . $CFG -> additionalhtmlbottomofbody ;
}
// Give subsystems an opportunity to inject extra html content. The callback
// must always return a string containing valid html.
foreach ( \core_component :: get_core_subsystems () as $name => $path ) {
if ( $path ) {
$output .= component_callback ( $name , 'standard_after_main_region_html' , [], '' );
}
}
// Give plugins an opportunity to inject extra html content. The callback
// must always return a string containing valid html.
$pluginswithfunction = get_plugins_with_function ( 'standard_after_main_region_html' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$output .= $function ();
}
}
return $output ;
}
2009-08-10 06:22:04 +00:00
/**
* Return the standard string that says whether you are logged in ( and switched
* roles / logged in as another user ) .
2012-09-03 07:25:30 +02:00
* @ param bool $withlinks if false , then don ' t include any links in the HTML produced .
* If not set , the default is the nologinlinks option from the theme config . php file ,
* and if that is not set , then links are included .
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
2012-09-03 07:25:30 +02:00
public function login_info ( $withlinks = null ) {
2010-09-17 08:00:29 +00:00
global $USER , $CFG , $DB , $SESSION ;
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
if ( during_initial_install ()) {
return '' ;
}
2009-12-30 15:19:55 +00:00
2012-09-03 07:25:30 +02:00
if ( is_null ( $withlinks )) {
$withlinks = empty ( $this -> page -> layout_options [ 'nologinlinks' ]);
}
2009-12-29 17:26:29 +00:00
$course = $this -> page -> course ;
2013-09-08 08:38:52 +02:00
if ( \core\session\manager :: is_loggedinas ()) {
$realuser = \core\session\manager :: get_realuser ();
2020-04-04 10:29:29 +02:00
$fullname = fullname ( $realuser );
2012-09-03 07:25:30 +02:00
if ( $withlinks ) {
2013-04-02 15:51:41 +08:00
$loginastitle = get_string ( 'loginas' );
$realuserinfo = " [<a href= \" $CFG->wwwroot /course/loginas.php?id= $course->id &sesskey= " . sesskey () . " \" " ;
$realuserinfo .= " title = \" " . $loginastitle . " \" > $fullname </a>] " ;
2012-09-03 07:25:30 +02:00
} else {
$realuserinfo = " [ $fullname ] " ;
}
2009-12-29 17:26:29 +00:00
} else {
$realuserinfo = '' ;
}
2009-12-30 15:19:55 +00:00
2014-12-18 08:40:57 +08:00
$loginpage = $this -> is_login_page ();
2009-12-29 17:26:29 +00:00
$loginurl = get_login_url ();
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
if ( empty ( $course -> id )) {
// $course->id is not defined during installation
return '' ;
2010-03-31 07:41:31 +00:00
} else if ( isloggedin ()) {
2012-07-25 16:25:55 +08:00
$context = context_course :: instance ( $course -> id );
2009-12-30 15:19:55 +00:00
2020-04-04 10:29:29 +02:00
$fullname = fullname ( $USER );
2010-05-04 13:04:35 +00:00
// Since Moodle 2.0 this link always goes to the public profile page (not the course profile page)
2012-09-03 07:25:30 +02:00
if ( $withlinks ) {
2013-04-02 15:32:38 +08:00
$linktitle = get_string ( 'viewprofile' );
$username = " <a href= \" $CFG->wwwroot /user/profile.php?id= $USER->id\ " title = \ " $linktitle\ " > $fullname </ a > " ;
2012-09-03 07:25:30 +02:00
} else {
$username = $fullname ;
}
2009-12-29 17:26:29 +00:00
if ( is_mnet_remote_user ( $USER ) and $idprovider = $DB -> get_record ( 'mnet_host' , array ( 'id' => $USER -> mnethostid ))) {
2012-09-03 07:25:30 +02:00
if ( $withlinks ) {
$username .= " from <a href= \" { $idprovider -> wwwroot } \" > { $idprovider -> name } </a> " ;
} else {
$username .= " from { $idprovider -> name } " ;
}
2009-12-29 17:26:29 +00:00
}
2010-08-25 08:56:07 +00:00
if ( isguestuser ()) {
2010-10-16 17:43:04 +00:00
$loggedinas = $realuserinfo . get_string ( 'loggedinasguest' );
2012-09-03 07:25:30 +02:00
if ( ! $loginpage && $withlinks ) {
2010-10-16 17:43:04 +00:00
$loggedinas .= " (<a href= \" $loginurl\ " > " .get_string('login').'</a>)';
}
2010-08-27 01:44:25 +00:00
} else if ( is_role_switched ( $course -> id )) { // Has switched roles
2009-12-29 17:26:29 +00:00
$rolename = '' ;
if ( $role = $DB -> get_record ( 'role' , array ( 'id' => $USER -> access [ 'rsw' ][ $context -> path ]))) {
2013-03-17 21:13:28 +01:00
$rolename = ': ' . role_get_name ( $role , $context );
2009-12-29 17:26:29 +00:00
}
2012-09-03 07:25:30 +02:00
$loggedinas = get_string ( 'loggedinas' , 'moodle' , $username ) . $rolename ;
if ( $withlinks ) {
2012-12-07 18:13:22 +01:00
$url = new moodle_url ( '/course/switchrole.php' , array ( 'id' => $course -> id , 'sesskey' => sesskey (), 'switchrole' => 0 , 'returnurl' => $this -> page -> url -> out_as_local_url ( false )));
2014-05-09 16:19:14 +01:00
$loggedinas .= ' (' . html_writer :: tag ( 'a' , get_string ( 'switchrolereturn' ), array ( 'href' => $url )) . ')' ;
2012-09-03 07:25:30 +02:00
}
2009-12-29 17:26:29 +00:00
} else {
2012-09-03 07:25:30 +02:00
$loggedinas = $realuserinfo . get_string ( 'loggedinas' , 'moodle' , $username );
if ( $withlinks ) {
$loggedinas .= " (<a href= \" $CFG->wwwroot /login/logout.php?sesskey= " . sesskey () . " \" > " . get_string ( 'logout' ) . '</a>)' ;
}
2009-12-29 17:26:29 +00:00
}
} else {
2010-10-16 17:43:04 +00:00
$loggedinas = get_string ( 'loggedinnot' , 'moodle' );
2012-09-03 07:25:30 +02:00
if ( ! $loginpage && $withlinks ) {
2010-10-16 17:43:04 +00:00
$loggedinas .= " (<a href= \" $loginurl\ " > " .get_string('login').'</a>)';
}
2009-12-29 17:26:29 +00:00
}
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
$loggedinas = '<div class="logininfo">' . $loggedinas . '</div>' ;
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
if ( isset ( $SESSION -> justloggedin )) {
unset ( $SESSION -> justloggedin );
if ( ! empty ( $CFG -> displayloginfailures )) {
2010-08-25 08:56:07 +00:00
if ( ! isguestuser ()) {
2014-02-21 16:25:09 +08:00
// Include this file only when required.
require_once ( $CFG -> dirroot . '/user/lib.php' );
if ( $count = user_count_login_failures ( $USER )) {
2014-04-02 14:18:25 +08:00
$loggedinas .= '<div class="loginfailures">' ;
2014-02-21 16:25:09 +08:00
$a = new stdClass ();
$a -> attempts = $count ;
$loggedinas .= get_string ( 'failedloginattempts' , '' , $a );
2012-07-25 16:25:55 +08:00
if ( file_exists ( " $CFG->dirroot /report/log/index.php " ) and has_capability ( 'report/log:view' , context_system :: instance ())) {
2014-07-04 14:48:10 +01:00
$loggedinas .= ' (' . html_writer :: link ( new moodle_url ( '/report/log/index.php' , array ( 'chooselog' => 1 ,
'id' => 0 , 'modid' => 'site_errors' )), get_string ( 'logs' )) . ')' ;
2009-12-29 17:26:29 +00:00
}
$loggedinas .= '</div>' ;
}
}
}
}
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
return $loggedinas ;
2009-08-10 06:22:04 +00:00
}
2014-12-18 08:40:57 +08:00
/**
* Check whether the current page is a login page .
*
* @ since Moodle 2.9
* @ return bool
*/
protected function is_login_page () {
// This is a real bit of a hack, but its a rarety that we need to do something like this.
// In fact the login pages should be only these two pages and as exposing this as an option for all pages
// could lead to abuse (or at least unneedingly complex code) the hack is the way to go.
return in_array (
$this -> page -> url -> out_as_local_url ( false , array ()),
array (
'/login/index.php' ,
'/login/forgot_password.php' ,
)
);
}
2009-08-10 06:22:04 +00:00
/**
* Return the 'back' link that normally appears in the footer .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment .
*/
public function home_link () {
global $CFG , $SITE ;
if ( $this -> page -> pagetype == 'site-index' ) {
// Special case for site home page - please do not remove
return '<div class="sitelink">' .
2020-04-08 12:24:02 +02:00
'<a title="Moodle" class="d-inline-block aalink" href="http://moodle.org/">' .
2019-05-02 14:29:11 +02:00
'<img src="' . $this -> image_url ( 'moodlelogo_grayhat' ) . '" alt="' . get_string ( 'moodlelogo' ) . '" /></a></div>' ;
2009-08-10 06:22:04 +00:00
} else if ( ! empty ( $CFG -> target_release ) && $CFG -> target_release != $CFG -> release ) {
// Special case for during install/upgrade.
return '<div class="sitelink">' .
2009-11-17 03:06:39 +00:00
'<a title="Moodle" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
2019-05-02 14:29:11 +02:00
'<img src="' . $this -> image_url ( 'moodlelogo_grayhat' ) . '" alt="' . get_string ( 'moodlelogo' ) . '" /></a></div>' ;
2009-08-10 06:22:04 +00:00
} else if ( $this -> page -> course -> id == $SITE -> id || strpos ( $this -> page -> pagetype , 'course-view' ) === 0 ) {
return '<div class="homelink"><a href="' . $CFG -> wwwroot . '/">' .
get_string ( 'home' ) . '</a></div>' ;
} else {
return '<div class="homelink"><a href="' . $CFG -> wwwroot . '/course/view.php?id=' . $this -> page -> course -> id . '">' .
2011-09-07 11:46:28 +12:00
format_string ( $this -> page -> course -> shortname , true , array ( 'context' => $this -> page -> context )) . '</a></div>' ;
2009-08-10 06:22:04 +00:00
}
}
/**
* Redirects the user by any means possible given the current state
*
* This function should not be called directly , it should always be called using
* the redirect function in lib / weblib . php
*
* The redirect function should really only be called before page output has started
* however it will allow itself to be called during the state STATE_IN_BODY
*
* @ param string $encodedurl The URL to send to encoded if required
* @ param string $message The message to display to the user if any
* @ param int $delay The delay before redirecting a user , if $message has been
* set this is a requirement and defaults to 3 , set to 0 no delay
* @ param boolean $debugdisableredirect this redirect has been disabled for
* debugging purposes . Display a message that explains , and don ' t
* trigger the redirect .
2016-02-16 08:49:42 +08:00
* @ param string $messagetype The type of notification to show the message in .
* See constants on \core\output\notification .
2009-08-10 06:22:04 +00:00
* @ return string The HTML to display to the user before dying , may contain
* meta refresh , javascript refresh , and may have set header redirects
*/
2016-02-16 08:49:42 +08:00
public function redirect_message ( $encodedurl , $message , $delay , $debugdisableredirect ,
$messagetype = \core\output\notification :: NOTIFY_INFO ) {
2009-08-10 06:22:04 +00:00
global $CFG ;
$url = str_replace ( '&' , '&' , $encodedurl );
switch ( $this -> page -> state ) {
case moodle_page :: STATE_BEFORE_HEADER :
// No output yet it is safe to delivery the full arsenal of redirect methods
if ( ! $debugdisableredirect ) {
// Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
$this -> metarefreshtag = '<meta http-equiv="refresh" content="' . $delay . '; url=' . $encodedurl . '" />' . " \n " ;
2010-02-06 14:45:17 +00:00
$this -> page -> requires -> js_function_call ( 'document.location.replace' , array ( $url ), false , ( $delay + 3 ));
2009-08-10 06:22:04 +00:00
}
$output = $this -> header ();
break ;
case moodle_page :: STATE_PRINTING_HEADER :
// We should hopefully never get here
throw new coding_exception ( 'You cannot redirect while printing the page header' );
break ;
case moodle_page :: STATE_IN_BODY :
// We really shouldn't be here but we can deal with this
debugging ( " You should really redirect before you start page output " );
if ( ! $debugdisableredirect ) {
2010-02-06 14:45:17 +00:00
$this -> page -> requires -> js_function_call ( 'document.location.replace' , array ( $url ), false , $delay );
2009-08-10 06:22:04 +00:00
}
$output = $this -> opencontainers -> pop_all_but_last ();
break ;
case moodle_page :: STATE_DONE :
// Too late to be calling redirect now
throw new coding_exception ( 'You cannot redirect after the entire page has been generated' );
break ;
}
2016-02-16 08:49:42 +08:00
$output .= $this -> notification ( $message , $messagetype );
2010-06-14 05:16:56 +00:00
$output .= '<div class="continuebutton">(<a href="' . $encodedurl . '">' . get_string ( 'continue' ) . '</a>)</div>' ;
2009-08-10 06:22:04 +00:00
if ( $debugdisableredirect ) {
2015-02-06 23:02:45 +02:00
$output .= '<p><strong>' . get_string ( 'erroroutput' , 'error' ) . '</strong></p>' ;
2009-08-10 06:22:04 +00:00
}
$output .= $this -> footer ();
return $output ;
}
/**
* Start output by sending the HTTP headers , and printing the HTML < head >
* and the start of the < body >.
*
2020-03-24 13:32:22 +00:00
* To control what is printed , you should set properties on $PAGE .
2009-08-10 06:22:04 +00:00
*
* @ return string HTML that you must output this , preferably immediately .
*/
2009-09-01 03:47:07 +00:00
public function header () {
2016-11-28 12:53:55 +08:00
global $USER , $CFG , $SESSION ;
2009-08-10 06:22:04 +00:00
2016-10-06 22:27:58 +11:00
// Give plugins an opportunity touch things before the http headers are sent
// such as adding additional headers. The return value is ignored.
$pluginswithfunction = get_plugins_with_function ( 'before_http_headers' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
$function ();
}
}
2013-09-08 08:38:52 +02:00
if ( \core\session\manager :: is_loggedinas ()) {
2010-08-24 08:50:53 +00:00
$this -> page -> add_body_class ( 'userloggedinas' );
}
2016-11-28 12:53:55 +08:00
if ( isset ( $SESSION -> justloggedin ) && ! empty ( $CFG -> displayloginfailures )) {
require_once ( $CFG -> dirroot . '/user/lib.php' );
// Set second parameter to false as we do not want reset the counter, the same message appears on footer.
if ( $count = user_count_login_failures ( $USER , false )) {
$this -> page -> add_body_class ( 'loginfailures' );
}
}
2014-09-16 13:41:41 +08:00
// If the user is logged in, and we're not in initial install,
// check to see if the user is role-switched and add the appropriate
// CSS class to the body element.
if ( ! during_initial_install () && isloggedin () && is_role_switched ( $this -> page -> course -> id )) {
$this -> page -> add_body_class ( 'userswitchedrole' );
}
2013-03-19 11:47:31 +01:00
// Give themes a chance to init/alter the page object.
$this -> page -> theme -> init_page ( $this -> page );
2009-08-10 06:22:04 +00:00
$this -> page -> set_state ( moodle_page :: STATE_PRINTING_HEADER );
2009-12-16 18:00:58 +00:00
// Find the appropriate page layout file, based on $this->page->pagelayout.
$layoutfile = $this -> page -> theme -> layout_file ( $this -> page -> pagelayout );
// Render the layout using the layout file.
$rendered = $this -> render_page_layout ( $layoutfile );
2009-10-03 08:54:30 +00:00
2009-12-16 18:00:58 +00:00
// Slice the rendered output into header and footer.
2011-08-21 21:01:31 +02:00
$cutpos = strpos ( $rendered , $this -> unique_main_content_token );
if ( $cutpos === false ) {
$cutpos = strpos ( $rendered , self :: MAIN_CONTENT_TOKEN );
$token = self :: MAIN_CONTENT_TOKEN ;
} else {
$token = $this -> unique_main_content_token ;
}
2009-08-10 06:22:04 +00:00
if ( $cutpos === false ) {
2011-08-21 21:01:31 +02:00
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.' );
2009-08-10 06:22:04 +00:00
}
2009-12-16 18:00:58 +00:00
$header = substr ( $rendered , 0 , $cutpos );
2011-08-21 21:01:31 +02:00
$footer = substr ( $rendered , $cutpos + strlen ( $token ));
2009-08-10 06:22:04 +00:00
if ( empty ( $this -> contenttype )) {
2009-12-16 18:00:58 +00:00
debugging ( 'The page layout file did not call $OUTPUT->doctype()' );
2009-10-03 08:54:30 +00:00
$header = $this -> doctype () . $header ;
2009-08-10 06:22:04 +00:00
}
2012-10-18 09:38:36 +08:00
// If this theme version is below 2.4 release and this is a course view page
if (( ! isset ( $this -> page -> theme -> settings -> version ) || $this -> page -> theme -> settings -> version < 2012101500 ) &&
$this -> page -> pagelayout === 'course' && $this -> page -> url -> compare ( new moodle_url ( '/course/view.php' ), URL_MATCH_BASE )) {
// check if course content header/footer have not been output during render of theme layout
$coursecontentheader = $this -> course_content_header ( true );
$coursecontentfooter = $this -> course_content_footer ( true );
if ( ! empty ( $coursecontentheader )) {
// display debug message and add header and footer right above and below main content
// Please note that course header and footer (to be displayed above and below the whole page)
// are not displayed in this case at all.
// Besides the content header and footer are not displayed on any other course page
debugging ( 'The current theme is not optimised for 2.4, the course-specific header and footer defined in course format will not be output' , DEBUG_DEVELOPER );
$header .= $coursecontentheader ;
$footer = $coursecontentfooter . $footer ;
}
}
2009-08-10 06:22:04 +00:00
send_headers ( $this -> contenttype , $this -> page -> cacheable );
2009-10-03 08:54:30 +00:00
2009-08-10 06:22:04 +00:00
$this -> opencontainers -> push ( 'header/footer' , $footer );
$this -> page -> set_state ( moodle_page :: STATE_IN_BODY );
2009-10-03 08:54:30 +00:00
2010-06-23 09:10:53 +00:00
return $header . $this -> skip_link_target ( 'maincontent' );
2009-08-10 06:22:04 +00:00
}
/**
2009-12-16 18:00:58 +00:00
* Renders and outputs the page layout file .
2012-01-05 13:28:08 +13:00
*
* This is done by preparing the normal globals available to a script , and
* then including the layout file provided by the current theme for the
* requested layout .
*
2009-12-16 18:00:58 +00:00
* @ param string $layoutfile The name of the layout file
2009-08-10 06:22:04 +00:00
* @ return string HTML code
*/
2009-12-16 18:00:58 +00:00
protected function render_page_layout ( $layoutfile ) {
2009-12-23 17:44:17 +00:00
global $CFG , $SITE , $USER ;
2009-08-10 06:22:04 +00:00
// The next lines are a bit tricky. The point is, here we are in a method
// of a renderer class, and this object may, or may not, be the same as
2009-12-16 18:00:58 +00:00
// the global $OUTPUT object. When rendering the page layout file, we want to use
2009-08-10 06:22:04 +00:00
// this object. However, people writing Moodle code expect the current
// renderer to be called $OUTPUT, not $this, so define a variable called
// $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
$OUTPUT = $this ;
$PAGE = $this -> page ;
$COURSE = $this -> page -> course ;
ob_start ();
2009-12-16 18:00:58 +00:00
include ( $layoutfile );
$rendered = ob_get_contents ();
2009-08-10 06:22:04 +00:00
ob_end_clean ();
2009-12-16 18:00:58 +00:00
return $rendered ;
2009-08-10 06:22:04 +00:00
}
/**
* Outputs the page ' s footer
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
public function footer () {
2020-03-24 13:32:22 +00:00
global $CFG , $DB ;
2009-11-03 11:02:07 +00:00
2020-04-30 10:53:58 +01:00
$output = '' ;
2016-10-06 22:27:58 +11:00
// Give plugins an opportunity to touch the page before JS is finalized.
$pluginswithfunction = get_plugins_with_function ( 'before_footer' , 'lib.php' );
foreach ( $pluginswithfunction as $plugins ) {
foreach ( $plugins as $function ) {
2020-04-30 10:53:58 +01:00
$extrafooter = $function ();
if ( is_string ( $extrafooter )) {
$output .= $extrafooter ;
}
2016-10-06 22:27:58 +11:00
}
}
2020-04-30 10:53:58 +01:00
$output .= $this -> container_end_all ( true );
2010-05-19 08:05:36 +00:00
2009-08-10 06:22:04 +00:00
$footer = $this -> opencontainers -> pop ( 'header/footer' );
2009-11-07 08:52:56 +00:00
if ( debugging () and $DB and $DB -> is_transaction_started ()) {
2009-11-09 11:33:14 +00:00
// TODO: MDL-20625 print warning - transaction will be rolled back
2009-11-07 08:52:56 +00:00
}
2009-08-10 06:22:04 +00:00
// Provide some performance info if required
$performanceinfo = '' ;
if ( defined ( 'MDL_PERF' ) || ( ! empty ( $CFG -> perfdebug ) and $CFG -> perfdebug > 7 )) {
$perf = get_performance_info ();
if ( defined ( 'MDL_PERFTOFOOT' ) || debugging () || $CFG -> perfdebug > 7 ) {
$performanceinfo = $perf [ 'html' ];
}
}
2013-11-14 11:55:03 +08:00
// We always want performance data when running a performance test, even if the user is redirected to another page.
if ( MDL_PERF_TEST && strpos ( $footer , $this -> unique_performance_info_token ) === false ) {
$footer = $this -> unique_performance_info_token . $footer ;
}
2011-08-21 21:01:31 +02:00
$footer = str_replace ( $this -> unique_performance_info_token , $performanceinfo , $footer );
2009-08-10 06:22:04 +00:00
2020-03-24 13:32:22 +00:00
// Only show notifications when the current page has a context id.
if ( ! empty ( $this -> page -> context -> id )) {
2016-04-18 16:38:36 +08:00
$this -> page -> requires -> js_call_amd ( 'core/notification' , 'init' , array (
2020-03-24 13:32:22 +00:00
$this -> page -> context -> id ,
2020-05-27 14:15:39 +10:00
\core\notification :: fetch_as_array ( $this )
2016-04-18 16:38:36 +08:00
));
}
2011-08-21 21:01:31 +02:00
$footer = str_replace ( $this -> unique_end_html_token , $this -> page -> requires -> get_end_code (), $footer );
2009-08-10 06:22:04 +00:00
$this -> page -> set_state ( moodle_page :: STATE_DONE );
return $output . $footer ;
}
2009-12-23 18:05:58 +00:00
/**
* Close all but the last open container . This is useful in places like error
* handling , where you want to close all the open containers ( apart from < body > )
* before outputting the error message .
2012-01-05 13:28:08 +13:00
*
2009-12-23 18:05:58 +00:00
* @ param bool $shouldbenone assert that the stack should be empty now - causes a
* developer debug warning if it isn ' t .
* @ return string the HTML required to close any open containers inside < body >.
*/
public function container_end_all ( $shouldbenone = false ) {
return $this -> opencontainers -> pop_all_but_last ( $shouldbenone );
}
2012-10-18 09:38:36 +08:00
/**
* Returns course - specific information to be output immediately above content on any course page
* ( for the current course )
*
* @ param bool $onlyifnotcalledbefore output content only if it has not been output before
* @ return string
*/
public function course_content_header ( $onlyifnotcalledbefore = false ) {
global $CFG ;
static $functioncalled = false ;
if ( $functioncalled && $onlyifnotcalledbefore ) {
// we have already output the content header
return '' ;
}
2016-02-16 08:48:39 +08:00
// Output any session notification.
$notifications = \core\notification :: fetch ();
$bodynotifications = '' ;
foreach ( $notifications as $notification ) {
$bodynotifications .= $this -> render_from_template (
$notification -> get_template_name (),
$notification -> export_for_template ( $this )
);
}
$output = html_writer :: span ( $bodynotifications , 'notifications' , array ( 'id' => 'user-notifications' ));
if ( $this -> page -> course -> id == SITEID ) {
// return immediately and do not include /course/lib.php if not necessary
return $output ;
}
2012-10-18 09:38:36 +08:00
require_once ( $CFG -> dirroot . '/course/lib.php' );
$functioncalled = true ;
$courseformat = course_get_format ( $this -> page -> course );
if (( $obj = $courseformat -> course_content_header ()) !== null ) {
2016-02-16 08:48:39 +08:00
$output .= html_writer :: div ( $courseformat -> get_renderer ( $this -> page ) -> render ( $obj ), 'course-content-header' );
2012-10-18 09:38:36 +08:00
}
2016-02-16 08:48:39 +08:00
return $output ;
2012-10-18 09:38:36 +08:00
}
/**
* Returns course - specific information to be output immediately below content on any course page
* ( for the current course )
*
* @ param bool $onlyifnotcalledbefore output content only if it has not been output before
* @ return string
*/
public function course_content_footer ( $onlyifnotcalledbefore = false ) {
global $CFG ;
if ( $this -> page -> course -> id == SITEID ) {
// return immediately and do not include /course/lib.php if not necessary
return '' ;
}
static $functioncalled = false ;
if ( $functioncalled && $onlyifnotcalledbefore ) {
// we have already output the content footer
return '' ;
}
$functioncalled = true ;
require_once ( $CFG -> dirroot . '/course/lib.php' );
$courseformat = course_get_format ( $this -> page -> course );
if (( $obj = $courseformat -> course_content_footer ()) !== null ) {
2013-06-07 10:38:39 +12:00
return html_writer :: div ( $courseformat -> get_renderer ( $this -> page ) -> render ( $obj ), 'course-content-footer' );
2012-10-18 09:38:36 +08:00
}
return '' ;
}
/**
* Returns course - specific information to be output on any course page in the header area
* ( for the current course )
*
* @ return string
*/
public function course_header () {
global $CFG ;
if ( $this -> page -> course -> id == SITEID ) {
// return immediately and do not include /course/lib.php if not necessary
return '' ;
}
require_once ( $CFG -> dirroot . '/course/lib.php' );
$courseformat = course_get_format ( $this -> page -> course );
if (( $obj = $courseformat -> course_header ()) !== null ) {
return $courseformat -> get_renderer ( $this -> page ) -> render ( $obj );
}
return '' ;
}
/**
* Returns course - specific information to be output on any course page in the footer area
* ( for the current course )
*
* @ return string
*/
public function course_footer () {
global $CFG ;
if ( $this -> page -> course -> id == SITEID ) {
// return immediately and do not include /course/lib.php if not necessary
return '' ;
}
require_once ( $CFG -> dirroot . '/course/lib.php' );
$courseformat = course_get_format ( $this -> page -> course );
if (( $obj = $courseformat -> course_footer ()) !== null ) {
return $courseformat -> get_renderer ( $this -> page ) -> render ( $obj );
}
return '' ;
}
2019-04-30 15:06:37 +02:00
/**
* Get the course pattern datauri to show on a course card .
*
* The datauri is an encoded svg that can be passed as a url .
* @ param int $id Id to use when generating the pattern
* @ return string datauri
*/
public function get_generated_image_for_id ( $id ) {
$color = $this -> get_generated_color_for_id ( $id );
$pattern = new \core_geopattern ();
$pattern -> setColor ( $color );
$pattern -> patternbyid ( $id );
return $pattern -> datauri ();
}
/**
* Get the course color to show on a course card .
*
* @ param int $id Id to use when generating the color .
* @ return string hex color code .
*/
public function get_generated_color_for_id ( $id ) {
2019-06-20 15:26:18 +02:00
$colornumbers = range ( 1 , 10 );
$basecolors = [];
foreach ( $colornumbers as $number ) {
$basecolors [] = get_config ( 'core_admin' , 'coursecolor' . $number );
}
2019-04-30 15:06:37 +02:00
$color = $basecolors [ $id % 10 ];
return $color ;
}
2009-12-29 17:26:29 +00:00
/**
* Returns lang menu or '' , this method also checks forcing of languages in courses .
2012-01-05 13:28:08 +13:00
*
2013-04-15 11:05:46 +10:00
* This function calls { @ link core_renderer :: render_single_select ()} to actually display the language menu .
*
2012-01-05 13:28:08 +13:00
* @ return string The lang menu HTML or empty string
2009-12-29 17:26:29 +00:00
*/
public function lang_menu () {
global $CFG ;
if ( empty ( $CFG -> langmenu )) {
return '' ;
}
if ( $this -> page -> course != SITEID and ! empty ( $this -> page -> course -> lang )) {
// do not show lang menu if language forced
return '' ;
}
$currlang = current_language ();
2010-04-14 09:45:49 +00:00
$langs = get_string_manager () -> get_list_of_translations ();
2009-12-30 15:19:55 +00:00
2009-12-29 17:26:29 +00:00
if ( count ( $langs ) < 2 ) {
return '' ;
}
2010-02-09 17:39:13 +00:00
$s = new single_select ( $this -> page -> url , 'lang' , $langs , $currlang , null );
$s -> label = get_accesshide ( get_string ( 'language' ));
$s -> class = 'langmenu' ;
return $this -> render ( $s );
2009-12-29 17:26:29 +00:00
}
2009-08-10 06:22:04 +00:00
/**
* Output the row of editing icons for a block , as defined by the controls array .
2012-01-05 13:28:08 +13:00
*
2012-02-14 18:24:09 +13:00
* @ param array $controls an array like { @ link block_contents :: $controls } .
2013-07-03 13:46:01 +12:00
* @ param string $blockid The ID given to the block .
2012-01-05 13:28:08 +13:00
* @ return string HTML fragment .
2009-08-10 06:22:04 +00:00
*/
2013-07-03 13:46:01 +12:00
public function block_controls ( $actions , $blockid = null ) {
2013-07-18 15:09:12 +12:00
global $CFG ;
2013-07-03 13:46:01 +12:00
if ( empty ( $actions )) {
return '' ;
}
2013-07-08 11:44:32 +12:00
$menu = new action_menu ( $actions );
2013-07-03 13:46:01 +12:00
if ( $blockid !== null ) {
$menu -> set_owner_selector ( '#' . $blockid );
}
2013-10-31 15:54:16 +08:00
$menu -> set_constraint ( '.block-region' );
2013-07-03 13:46:01 +12:00
$menu -> attributes [ 'class' ] .= ' block-control-actions commands' ;
return $this -> render ( $menu );
}
2018-07-24 16:28:27 +08:00
/**
* Returns the HTML for a basic textarea field .
*
* @ param string $name Name to use for the textarea element
* @ param string $id The id to use fort he textarea element
* @ param string $value Initial content to display in the textarea
* @ param int $rows Number of rows to display
* @ param int $cols Number of columns to display
* @ return string the HTML to display
*/
public function print_textarea ( $name , $id , $value , $rows , $cols ) {
editors_head_setup ();
$editor = editors_get_preferred_editor ( FORMAT_HTML );
$editor -> set_text ( $value );
$editor -> use_editor ( $id , []);
$context = [
'id' => $id ,
'name' => $name ,
'value' => $value ,
'rows' => $rows ,
'cols' => $cols
];
2020-03-24 13:32:22 +00:00
return $this -> render_from_template ( 'core_form/editor_textarea' , $context );
2018-07-24 16:28:27 +08:00
}
2013-07-03 13:46:01 +12:00
/**
* Renders an action menu component .
*
* @ param action_menu $menu
* @ return string HTML
*/
public function render_action_menu ( action_menu $menu ) {
2019-02-13 16:29:22 +08:00
// We don't want the class icon there!
foreach ( $menu -> get_secondary_actions () as $action ) {
if ( $action instanceof \action_menu_link && $action -> has_class ( 'icon' )) {
$action -> attributes [ 'class' ] = preg_replace ( '/(^|\s+)icon(\s+|$)/i' , '' , $action -> attributes [ 'class' ]);
}
}
if ( $menu -> is_empty ()) {
return '' ;
}
2016-09-06 20:28:41 +08:00
$context = $menu -> export_for_template ( $this );
2019-02-13 16:29:22 +08:00
2016-09-06 20:28:41 +08:00
return $this -> render_from_template ( 'core/action_menu' , $context );
2009-08-10 06:22:04 +00:00
}
2013-07-08 11:44:32 +12:00
/**
MDL-67818 check: Added Check API and refactored security checks
AMOS BEGIN
MOV [check_noauth_details,report_security],[check_noauth_details,auth_none]
MOV [check_noauth_error,report_security],[check_noauth_error,auth_none]
MOV [check_noauth_name,report_security],[check_noauth_name,auth_none]
MOV [check_noauth_ok,report_security],[check_noauth_ok,auth_none]
AMOS END
2020-04-02 17:23:42 +11:00
* Renders a Check API result
*
* @ param result $result
* @ return string HTML fragment
*/
2020-04-07 11:58:37 +10:00
protected function render_check_result ( core\check\result $result ) {
MDL-67818 check: Added Check API and refactored security checks
AMOS BEGIN
MOV [check_noauth_details,report_security],[check_noauth_details,auth_none]
MOV [check_noauth_error,report_security],[check_noauth_error,auth_none]
MOV [check_noauth_name,report_security],[check_noauth_name,auth_none]
MOV [check_noauth_ok,report_security],[check_noauth_ok,auth_none]
AMOS END
2020-04-02 17:23:42 +11:00
return $this -> render_from_template ( $result -> get_template_name (), $result -> export_for_template ( $this ));
}
/**
* Renders a Check API result
*
* @ param result $result
* @ return string HTML fragment
*/
2020-04-07 11:58:37 +10:00
public function check_result ( core\check\result $result ) {
return $this -> render_check_result ( $result );
MDL-67818 check: Added Check API and refactored security checks
AMOS BEGIN
MOV [check_noauth_details,report_security],[check_noauth_details,auth_none]
MOV [check_noauth_error,report_security],[check_noauth_error,auth_none]
MOV [check_noauth_name,report_security],[check_noauth_name,auth_none]
MOV [check_noauth_ok,report_security],[check_noauth_ok,auth_none]
AMOS END
2020-04-02 17:23:42 +11:00
}
/**
2013-07-18 10:42:11 +12:00
* Renders an action_menu_link item .
2013-07-08 11:44:32 +12:00
*
2013-07-18 10:42:11 +12:00
* @ param action_menu_link $action
2013-07-08 11:44:32 +12:00
* @ return string HTML fragment
*/
2013-07-18 10:42:11 +12:00
protected function render_action_menu_link ( action_menu_link $action ) {
2016-09-06 20:28:41 +08:00
return $this -> render_from_template ( 'core/action_menu_link' , $action -> export_for_template ( $this ));
2013-07-08 11:44:32 +12:00
}
2013-10-30 16:29:56 +08:00
/**
* Renders a primary action_menu_filler item .
*
* @ param action_menu_link_filler $action
* @ return string HTML fragment
*/
protected function render_action_menu_filler ( action_menu_filler $action ) {
return html_writer :: span ( ' ' , 'filler' );
}
2013-07-08 11:44:32 +12:00
/**
2013-07-18 10:42:11 +12:00
* Renders a primary action_menu_link item .
2013-07-08 11:44:32 +12:00
*
2013-07-18 10:42:11 +12:00
* @ param action_menu_link_primary $action
2013-07-08 11:44:32 +12:00
* @ return string HTML fragment
*/
2013-07-18 10:42:11 +12:00
protected function render_action_menu_link_primary ( action_menu_link_primary $action ) {
return $this -> render_action_menu_link ( $action );
2013-07-08 11:44:32 +12:00
}
/**
2013-07-18 10:42:11 +12:00
* Renders a secondary action_menu_link item .
2013-07-08 11:44:32 +12:00
*
2013-07-18 10:42:11 +12:00
* @ param action_menu_link_secondary $action
2013-07-08 11:44:32 +12:00
* @ return string HTML fragment
*/
2013-07-18 10:42:11 +12:00
protected function render_action_menu_link_secondary ( action_menu_link_secondary $action ) {
return $this -> render_action_menu_link ( $action );
2013-07-08 11:44:32 +12:00
}
2009-08-10 06:22:04 +00:00
/**
* Prints a nice side block with an optional header .
*
* @ param block_contents $bc HTML for the content
* @ param string $region the region the block is appearing in .
* @ return string the HTML to be output .
*/
2012-01-05 13:28:08 +13:00
public function block ( block_contents $bc , $region ) {
2009-08-10 06:22:04 +00:00
$bc = clone ( $bc ); // Avoid messing up the object passed in.
2010-02-17 18:36:26 +00:00
if ( empty ( $bc -> blockinstanceid ) || ! strip_tags ( $bc -> title )) {
$bc -> collapsible = block_contents :: NOT_HIDEABLE ;
}
2012-10-11 10:41:05 +08:00
2019-02-13 16:29:22 +08:00
$id = ! empty ( $bc -> attributes [ 'id' ]) ? $bc -> attributes [ 'id' ] : uniqid ( 'block-' );
$context = new stdClass ();
$context -> skipid = $bc -> skipid ;
2020-02-19 21:20:01 +11:00
$context -> blockinstanceid = $bc -> blockinstanceid ? : uniqid ( 'fakeid-' );
2019-02-13 16:29:22 +08:00
$context -> dockable = $bc -> dockable ;
$context -> id = $id ;
$context -> hidden = $bc -> collapsible == block_contents :: HIDDEN ;
$context -> skiptitle = strip_tags ( $bc -> title );
$context -> showskiplink = ! empty ( $context -> skiptitle );
$context -> arialabel = $bc -> arialabel ;
$context -> ariarole = ! empty ( $bc -> attributes [ 'role' ]) ? $bc -> attributes [ 'role' ] : 'complementary' ;
2019-05-02 17:17:22 +12:00
$context -> class = $bc -> attributes [ 'class' ];
2019-02-13 16:29:22 +08:00
$context -> type = $bc -> attributes [ 'data-block' ];
$context -> title = $bc -> title ;
$context -> content = $bc -> content ;
$context -> annotation = $bc -> annotation ;
$context -> footer = $bc -> footer ;
$context -> hascontrols = ! empty ( $bc -> controls );
if ( $context -> hascontrols ) {
$context -> controls = $this -> block_controls ( $bc -> controls , $id );
2009-08-10 06:22:04 +00:00
}
2010-05-19 08:05:36 +00:00
2019-02-13 16:29:22 +08:00
return $this -> render_from_template ( 'core/block' , $context );
2009-08-10 06:22:04 +00:00
}
/**
* Render the contents of a block_list .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param array $icons the icon for each item .
* @ param array $items the content of each item .
* @ return string HTML
*/
public function list_block_contents ( $icons , $items ) {
$row = 0 ;
$lis = array ();
foreach ( $items as $key => $string ) {
2010-01-13 17:13:52 +00:00
$item = html_writer :: start_tag ( 'li' , array ( 'class' => 'r' . $row ));
2009-08-25 19:09:04 +00:00
if ( ! empty ( $icons [ $key ])) { //test if the content has an assigned icon
2010-02-18 18:15:56 +00:00
$item .= html_writer :: tag ( 'div' , $icons [ $key ], array ( 'class' => 'icon column c0' ));
2009-08-10 06:22:04 +00:00
}
2010-02-18 18:15:56 +00:00
$item .= html_writer :: tag ( 'div' , $string , array ( 'class' => 'column c1' ));
2010-01-13 17:13:52 +00:00
$item .= html_writer :: end_tag ( 'li' );
2009-08-10 06:22:04 +00:00
$lis [] = $item ;
$row = 1 - $row ; // Flip even/odd.
}
2010-10-25 07:32:51 +00:00
return html_writer :: tag ( 'ul' , implode ( " \n " , $lis ), array ( 'class' => 'unlist' ));
2009-08-10 06:22:04 +00:00
}
/**
* Output all the blocks in a particular region .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $region the name of a region on this page .
2020-12-09 12:02:20 +11:00
* @ param boolean $fakeblocksonly Output fake block only .
2009-08-10 06:22:04 +00:00
* @ return string the HTML to be output .
*/
2020-12-09 12:02:20 +11:00
public function blocks_for_region ( $region , $fakeblocksonly = false ) {
2009-08-10 06:22:04 +00:00
$blockcontents = $this -> page -> blocks -> get_content_for_region ( $region , $this );
2013-03-11 14:56:35 +08:00
$lastblock = null ;
$zones = array ();
2019-03-01 18:44:57 +00:00
foreach ( $blockcontents as $bc ) {
if ( $bc instanceof block_contents ) {
$zones [] = $bc -> title ;
}
2013-03-11 14:56:35 +08:00
}
2009-08-10 06:22:04 +00:00
$output = '' ;
2013-03-11 14:56:35 +08:00
2009-08-10 06:22:04 +00:00
foreach ( $blockcontents as $bc ) {
if ( $bc instanceof block_contents ) {
2020-12-09 12:02:20 +11:00
if ( $fakeblocksonly && ! $bc -> is_fake ()) {
// Skip rendering real blocks if we only want to show fake blocks.
continue ;
}
2009-08-10 06:22:04 +00:00
$output .= $this -> block ( $bc , $region );
2013-03-11 14:56:35 +08:00
$lastblock = $bc -> title ;
2009-08-10 06:22:04 +00:00
} else if ( $bc instanceof block_move_target ) {
2020-08-25 10:04:25 +10:00
if ( ! $fakeblocksonly ) {
$output .= $this -> block_move_target ( $bc , $zones , $lastblock , $region );
}
2009-08-10 06:22:04 +00:00
} else {
throw new coding_exception ( 'Unexpected type of thing (' . get_class ( $bc ) . ') found in list of block contents.' );
}
}
return $output ;
}
/**
* Output a place where the block that is currently being moved can be dropped .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param block_move_target $target with the necessary details .
2013-03-11 14:56:35 +08:00
* @ param array $zones array of areas where the block can be moved to
* @ param string $previous the block located before the area currently being rendered .
2014-01-06 12:36:50 +01:00
* @ param string $region the name of the region
2009-08-10 06:22:04 +00:00
* @ return string the HTML to be output .
*/
2014-01-06 12:36:50 +01:00
public function block_move_target ( $target , $zones , $previous , $region ) {
2013-03-27 10:16:09 +08:00
if ( $previous == null ) {
2014-01-06 12:36:50 +01:00
if ( empty ( $zones )) {
// There are no zones, probably because there are no blocks.
$regions = $this -> page -> theme -> get_all_block_regions ();
$position = get_string ( 'moveblockinregion' , 'block' , $regions [ $region ]);
} else {
$position = get_string ( 'moveblockbefore' , 'block' , $zones [ 0 ]);
}
2013-03-11 14:56:35 +08:00
} else {
$position = get_string ( 'moveblockafter' , 'block' , $previous );
}
return html_writer :: tag ( 'a' , html_writer :: tag ( 'span' , $position , array ( 'class' => 'accesshide' )), array ( 'href' => $target -> url , 'class' => 'blockmovetarget' ));
2009-08-10 06:22:04 +00:00
}
2010-01-14 22:40:33 +00:00
/**
2010-05-22 14:43:46 +00:00
* Renders a special html link with attached action
2010-01-14 22:40:33 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_action_link ()} instead .
*
2010-01-14 22:40:33 +00:00
* @ param string | moodle_url $url
* @ param string $text HTML fragment
* @ param component_action $action
2010-01-14 22:51:10 +00:00
* @ param array $attributes associative array of html link attributes + disabled
2013-07-05 10:45:05 +12:00
* @ param pix_icon optional pix icon to render with the link
2012-01-05 13:28:08 +13:00
* @ return string HTML fragment
2010-01-14 22:40:33 +00:00
*/
2013-07-05 10:45:05 +12:00
public function action_link ( $url , $text , component_action $action = null , array $attributes = null , $icon = null ) {
2010-01-14 22:40:33 +00:00
if ( ! ( $url instanceof moodle_url )) {
$url = new moodle_url ( $url );
}
2013-07-05 10:45:05 +12:00
$link = new action_link ( $url , $text , $action , $attributes , $icon );
2010-01-14 22:40:33 +00:00
2010-01-15 09:01:59 +00:00
return $this -> render ( $link );
2010-01-14 22:40:33 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Renders an action_link object .
*
* The provided link is renderer and the HTML returned . At the same time the
2012-02-14 18:24:09 +13:00
* associated actions are setup in JS by { @ link core_renderer :: add_action_handler ()}
2012-01-05 13:28:08 +13:00
*
2010-01-14 22:40:33 +00:00
* @ param action_link $link
* @ return string HTML fragment
*/
protected function render_action_link ( action_link $link ) {
2016-09-06 20:20:35 +08:00
return $this -> render_from_template ( 'core/action_link' , $link -> export_for_template ( $this ));
2010-01-14 22:40:33 +00:00
}
2010-02-16 16:24:49 +00:00
/**
2012-01-05 13:28:08 +13:00
* Renders an action_icon .
*
2012-02-14 18:24:09 +13:00
* This function uses the { @ link core_renderer :: action_link ()} method for the
2012-01-05 13:28:08 +13:00
* most part . What it does different is prepare the icon as HTML and use it
* as the link text .
2010-02-16 16:24:49 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : If you want to change how action links and / or icons are rendered ,
* consider overriding function { @ link core_renderer :: render_action_link ()} and
* { @ link core_renderer :: render_pix_icon ()} .
*
2010-02-16 16:24:49 +00:00
* @ param string | moodle_url $url A string URL or moodel_url
* @ param pix_icon $pixicon
* @ param component_action $action
* @ param array $attributes associative array of html link attributes + disabled
* @ param bool $linktext show title next to image in link
* @ return string HTML fragment
*/
public function action_icon ( $url , pix_icon $pixicon , component_action $action = null , array $attributes = null , $linktext = false ) {
if ( ! ( $url instanceof moodle_url )) {
$url = new moodle_url ( $url );
}
$attributes = ( array ) $attributes ;
2010-02-24 06:08:52 +00:00
if ( empty ( $attributes [ 'class' ])) {
2010-02-16 16:24:49 +00:00
// let ppl override the class via $options
$attributes [ 'class' ] = 'action-icon' ;
}
$icon = $this -> render ( $pixicon );
if ( $linktext ) {
$text = $pixicon -> attributes [ 'alt' ];
} else {
$text = '' ;
}
return $this -> action_link ( $url , $text . $icon , $action , $attributes );
}
2009-08-10 06:22:04 +00:00
/**
2009-09-01 14:15:49 +00:00
* Print a message along with button choices for Continue / Cancel
*
2010-02-11 10:32:39 +00:00
* If a string or moodle_url is given instead of a single_button , method defaults to post .
2009-09-01 14:15:49 +00:00
*
2009-08-10 06:22:04 +00:00
* @ param string $message The question to ask the user
2010-01-14 19:18:04 +00:00
* @ param single_button | moodle_url | string $continue The single_button component representing the Continue answer . Can also be a moodle_url or string URL
* @ param single_button | moodle_url | string $cancel The single_button component representing the Cancel answer . Can also be a moodle_url or string URL
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
public function confirm ( $message , $continue , $cancel ) {
2010-01-14 22:44:24 +00:00
if ( $continue instanceof single_button ) {
2010-01-14 22:51:10 +00:00
// ok
2016-11-01 16:07:22 +08:00
$continue -> primary = true ;
2010-01-14 22:44:24 +00:00
} else if ( is_string ( $continue )) {
2016-11-01 16:07:22 +08:00
$continue = new single_button ( new moodle_url ( $continue ), get_string ( 'continue' ), 'post' , true );
2010-01-14 22:44:24 +00:00
} else if ( $continue instanceof moodle_url ) {
2016-11-01 16:07:22 +08:00
$continue = new single_button ( $continue , get_string ( 'continue' ), 'post' , true );
2009-08-10 06:22:04 +00:00
} else {
2010-02-11 10:32:39 +00:00
throw new coding_exception ( 'The continue param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.' );
2009-08-10 06:22:04 +00:00
}
2010-01-14 22:44:24 +00:00
if ( $cancel instanceof single_button ) {
2010-01-14 22:51:10 +00:00
// ok
2010-01-14 22:44:24 +00:00
} else if ( is_string ( $cancel )) {
$cancel = new single_button ( new moodle_url ( $cancel ), get_string ( 'cancel' ), 'get' );
} else if ( $cancel instanceof moodle_url ) {
2010-01-03 17:20:49 +00:00
$cancel = new single_button ( $cancel , get_string ( 'cancel' ), 'get' );
2009-08-10 06:22:04 +00:00
} else {
2010-02-11 10:32:39 +00:00
throw new coding_exception ( 'The cancel param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.' );
2009-08-10 06:22:04 +00:00
}
2018-09-17 15:58:34 +08:00
$attributes = [
'role' => 'alertdialog' ,
'aria-labelledby' => 'modal-header' ,
'aria-describedby' => 'modal-body' ,
'aria-modal' => 'true'
];
$output = $this -> box_start ( 'generalbox modal modal-dialog modal-in-page show' , 'notice' , $attributes );
2016-11-01 16:07:22 +08:00
$output .= $this -> box_start ( 'modal-content' , 'modal-content' );
2020-01-27 15:06:29 +01:00
$output .= $this -> box_start ( 'modal-header px-3' , 'modal-header' );
2016-11-01 16:07:22 +08:00
$output .= html_writer :: tag ( 'h4' , get_string ( 'confirm' ));
$output .= $this -> box_end ();
2018-09-17 15:58:34 +08:00
$attributes = [
'role' => 'alert' ,
'data-aria-autofocus' => 'true'
];
$output .= $this -> box_start ( 'modal-body' , 'modal-body' , $attributes );
2010-02-18 18:15:56 +00:00
$output .= html_writer :: tag ( 'p' , $message );
2016-11-01 16:07:22 +08:00
$output .= $this -> box_end ();
$output .= $this -> box_start ( 'modal-footer' , 'modal-footer' );
2010-02-18 18:15:56 +00:00
$output .= html_writer :: tag ( 'div' , $this -> render ( $continue ) . $this -> render ( $cancel ), array ( 'class' => 'buttons' ));
2009-08-10 06:22:04 +00:00
$output .= $this -> box_end ();
2016-11-01 16:07:22 +08:00
$output .= $this -> box_end ();
$output .= $this -> box_end ();
2009-08-10 06:22:04 +00:00
return $output ;
}
2010-01-03 10:28:29 +00:00
/**
2010-01-14 19:18:04 +00:00
* Returns a form with a single button .
2010-01-03 10:28:29 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_single_button ()} instead .
*
2010-01-14 19:18:04 +00:00
* @ param string | moodle_url $url
2010-01-03 10:28:29 +00:00
* @ param string $label button text
* @ param string $method get or post submit method
2010-01-14 19:18:04 +00:00
* @ param array $options associative array { disabled , title , etc . }
2010-01-03 10:28:29 +00:00
* @ return string HTML fragment
*/
2010-01-14 19:18:04 +00:00
public function single_button ( $url , $label , $method = 'post' , array $options = null ) {
2010-01-14 22:40:33 +00:00
if ( ! ( $url instanceof moodle_url )) {
$url = new moodle_url ( $url );
2010-01-14 19:18:04 +00:00
}
2010-01-14 22:40:33 +00:00
$button = new single_button ( $url , $label , $method );
2010-01-14 19:18:04 +00:00
foreach (( array ) $options as $key => $value ) {
2019-10-20 18:14:48 +02:00
if ( property_exists ( $button , $key )) {
2010-01-14 19:18:04 +00:00
$button -> $key = $value ;
2019-09-03 15:30:06 +08:00
} else {
$button -> set_attribute ( $key , $value );
2010-01-14 19:18:04 +00:00
}
2010-01-03 10:28:29 +00:00
}
2010-01-14 19:18:04 +00:00
return $this -> render ( $button );
2010-01-03 10:28:29 +00:00
}
2009-08-10 06:22:04 +00:00
/**
2012-01-05 13:28:08 +13:00
* Renders a single button widget .
*
* This will return HTML to display a form containing a single button .
*
2010-01-14 19:18:04 +00:00
* @ param single_button $button
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
2010-01-14 19:18:04 +00:00
protected function render_single_button ( single_button $button ) {
2019-02-13 16:29:22 +08:00
return $this -> render_from_template ( 'core/single_button' , $button -> export_for_template ( $this ));
2009-08-10 06:22:04 +00:00
}
2010-02-09 17:39:13 +00:00
/**
2010-02-10 10:23:09 +00:00
* Returns a form with a single select widget .
2012-01-05 13:28:08 +13:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_single_select ()} instead .
*
2010-02-09 17:39:13 +00:00
* @ param moodle_url $url form action target , includes hidden fields
* @ param string $name name of selection field - the changing parameter in url
* @ param array $options list of options
* @ param string $selected selected element
* @ param array $nothing
2010-02-10 09:37:50 +00:00
* @ param string $formid
2015-04-09 11:12:16 +12:00
* @ param array $attributes other attributes for the single select
2010-02-09 17:39:13 +00:00
* @ return string HTML fragment
*/
2015-04-09 11:12:16 +12:00
public function single_select ( $url , $name , array $options , $selected = '' ,
$nothing = array ( '' => 'choosedots' ), $formid = null , $attributes = array ()) {
2010-02-09 17:39:13 +00:00
if ( ! ( $url instanceof moodle_url )) {
$url = new moodle_url ( $url );
}
2010-02-10 09:37:50 +00:00
$select = new single_select ( $url , $name , $options , $selected , $nothing , $formid );
2010-02-09 17:39:13 +00:00
2015-04-09 11:12:16 +12:00
if ( array_key_exists ( 'label' , $attributes )) {
$select -> set_label ( $attributes [ 'label' ]);
unset ( $attributes [ 'label' ]);
}
$select -> attributes = $attributes ;
2010-02-09 17:39:13 +00:00
return $this -> render ( $select );
}
2016-04-02 16:10:29 +11:00
/**
* Returns a dataformat selection and download form
*
* @ param string $label A text label
* @ param moodle_url | string $base The download page url
* @ param string $name The query param which will hold the type of the download
* @ param array $params Extra params sent to the download page
* @ return string HTML fragment
*/
public function download_dataformat_selector ( $label , $base , $name = 'dataformat' , $params = array ()) {
$formats = core_plugin_manager :: instance () -> get_plugins_of_type ( 'dataformat' );
$options = array ();
foreach ( $formats as $format ) {
if ( $format -> is_enabled ()) {
$options [] = array (
'value' => $format -> name ,
2016-04-21 14:48:01 +08:00
'label' => get_string ( 'dataformat' , $format -> component ),
2016-04-02 16:10:29 +11:00
);
}
}
$hiddenparams = array ();
foreach ( $params as $key => $value ) {
$hiddenparams [] = array (
'name' => $key ,
'value' => $value ,
);
}
$data = array (
'label' => $label ,
'base' => $base ,
'name' => $name ,
'params' => $hiddenparams ,
'options' => $options ,
'sesskey' => sesskey (),
'submit' => get_string ( 'download' ),
);
return $this -> render_from_template ( 'core/dataformat_selector' , $data );
}
2010-02-09 17:39:13 +00:00
/**
* Internal implementation of single_select rendering
2012-01-05 13:28:08 +13:00
*
2010-02-09 17:39:13 +00:00
* @ param single_select $select
* @ return string HTML fragment
*/
protected function render_single_select ( single_select $select ) {
2016-10-28 14:41:35 +01:00
return $this -> render_from_template ( 'core/single_select' , $select -> export_for_template ( $this ));
2010-02-10 10:22:25 +00:00
}
/**
2010-02-10 10:23:09 +00:00
* Returns a form with a url select widget .
2012-01-05 13:28:08 +13:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_url_select ()} instead .
*
2010-02-10 10:22:25 +00:00
* @ param array $urls list of urls - array ( '/course/view.php?id=1' => 'Frontpage' , .... )
* @ param string $selected selected element
* @ param array $nothing
* @ param string $formid
* @ return string HTML fragment
*/
2012-01-05 13:28:08 +13:00
public function url_select ( array $urls , $selected , $nothing = array ( '' => 'choosedots' ), $formid = null ) {
2010-02-10 10:22:25 +00:00
$select = new url_select ( $urls , $selected , $nothing , $formid );
return $this -> render ( $select );
}
/**
2010-02-10 10:23:09 +00:00
* Internal implementation of url_select rendering
2012-01-05 13:28:08 +13:00
*
* @ param url_select $select
2010-02-10 10:22:25 +00:00
* @ return string HTML fragment
*/
protected function render_url_select ( url_select $select ) {
2016-10-28 14:41:35 +01:00
return $this -> render_from_template ( 'core/url_select' , $select -> export_for_template ( $this ));
2010-02-09 17:39:13 +00:00
}
2009-08-10 06:22:04 +00:00
/**
* Returns a string containing a link to the user documentation .
* Also contains an icon by default . Shown to teachers and admin only .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $path The page link after doc root and language , no leading slash .
* @ param string $text The text to be displayed for the link
2012-10-07 09:55:08 +01:00
* @ param boolean $forcepopup Whether to force a popup regardless of the value of $CFG -> doctonewwindow
2020-02-28 16:31:12 +11:00
* @ param array $attributes htm attributes
2010-05-22 14:43:46 +00:00
* @ return string
2009-08-10 06:22:04 +00:00
*/
2020-02-28 16:31:12 +11:00
public function doc_link ( $path , $text = '' , $forcepopup = false , array $attributes = []) {
2010-01-02 13:17:54 +00:00
global $CFG ;
2021-07-01 15:57:29 +02:00
$icon = $this -> pix_icon ( 'book' , '' , 'moodle' , array ( 'class' => 'iconhelp icon-pre' , 'role' => 'presentation' ));
2010-01-02 13:17:54 +00:00
2020-02-28 16:31:12 +11:00
$attributes [ 'href' ] = new moodle_url ( get_docs_url ( $path ));
2012-10-07 09:55:08 +01:00
if ( ! empty ( $CFG -> doctonewwindow ) || $forcepopup ) {
$attributes [ 'class' ] = 'helplinkpopup' ;
2009-08-10 06:22:04 +00:00
}
2010-03-28 09:05:47 +00:00
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'a' , $icon . $text , $attributes );
2009-08-10 06:22:04 +00:00
}
2017-01-19 14:06:07 +08:00
/**
2017-02-16 13:42:03 +08:00
* Return HTML for an image_icon .
2017-01-19 14:06:07 +08:00
*
* Theme developers : DO NOT OVERRIDE ! Please override function
2017-02-16 13:42:03 +08:00
* { @ link core_renderer :: render_image_icon ()} instead .
2017-01-19 14:06:07 +08:00
*
* @ param string $pix short pix name
* @ param string $alt mandatory alt attribute
* @ param string $component standard compoennt name like 'moodle' , 'mod_forum' , etc .
2020-02-28 16:31:12 +11:00
* @ param array $attributes htm attributes
2017-01-19 14:06:07 +08:00
* @ return string HTML fragment
*/
2017-02-16 13:42:03 +08:00
public function image_icon ( $pix , $alt , $component = 'moodle' , array $attributes = null ) {
$icon = new image_icon ( $pix , $alt , $component , $attributes );
2017-01-19 14:06:07 +08:00
return $this -> render ( $icon );
}
/**
* Renders a pix_icon widget and returns the HTML to display it .
*
2017-03-01 14:09:40 +08:00
* @ param image_icon $icon
2017-01-19 14:06:07 +08:00
* @ return string HTML fragment
*/
2017-02-16 13:42:03 +08:00
protected function render_image_icon ( image_icon $icon ) {
2017-02-02 12:59:55 +08:00
$system = \core\output\icon_system :: instance ( \core\output\icon_system :: STANDARD );
2017-01-19 14:06:07 +08:00
return $system -> render_pix_icon ( $this , $icon );
}
2010-02-11 14:59:00 +00:00
/**
2012-01-05 13:28:08 +13:00
* Return HTML for a pix_icon .
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_pix_icon ()} instead .
*
2010-02-11 14:59:00 +00:00
* @ param string $pix short pix name
* @ param string $alt mandatory alt attribute
2010-09-19 19:09:48 +00:00
* @ param string $component standard compoennt name like 'moodle' , 'mod_forum' , etc .
2010-02-11 14:59:00 +00:00
* @ param array $attributes htm lattributes
* @ return string HTML fragment
*/
public function pix_icon ( $pix , $alt , $component = 'moodle' , array $attributes = null ) {
$icon = new pix_icon ( $pix , $alt , $component , $attributes );
return $this -> render ( $icon );
}
/**
2012-01-05 13:28:08 +13:00
* Renders a pix_icon widget and returns the HTML to display it .
*
2010-02-11 14:59:00 +00:00
* @ param pix_icon $icon
* @ return string HTML fragment
*/
2010-02-16 17:33:13 +00:00
protected function render_pix_icon ( pix_icon $icon ) {
2017-01-03 10:40:42 +08:00
$system = \core\output\icon_system :: instance ();
return $system -> render_pix_icon ( $this , $icon );
2010-02-11 14:59:00 +00:00
}
2010-10-23 18:39:23 +00:00
/**
2012-01-05 13:28:08 +13:00
* Return HTML to display an emoticon icon .
*
2010-10-23 18:39:23 +00:00
* @ param pix_emoticon $emoticon
* @ return string HTML fragment
*/
protected function render_pix_emoticon ( pix_emoticon $emoticon ) {
2017-03-01 14:09:40 +08:00
$system = \core\output\icon_system :: instance ( \core\output\icon_system :: STANDARD );
return $system -> render_pix_icon ( $this , $emoticon );
2010-10-23 18:39:23 +00:00
}
2010-03-16 05:57:51 +00:00
/**
2012-01-05 13:28:08 +13:00
* Produces the html that represents this rating in the UI
*
* @ param rating $rating the page object on which this rating will appear
* @ return string
*/
2010-03-16 05:57:51 +00:00
function render_rating ( rating $rating ) {
2010-03-17 09:12:13 +00:00
global $CFG , $USER ;
2010-03-16 05:57:51 +00:00
2011-05-11 13:32:36 +08:00
if ( $rating -> settings -> aggregationmethod == RATING_AGGREGATE_NONE ) {
2010-04-22 05:15:23 +00:00
return null ; //ratings are turned off
}
2011-05-11 13:32:36 +08:00
$ratingmanager = new rating_manager ();
// Initialise the JavaScript so ratings can be done by AJAX.
$ratingmanager -> initialise_rating_javascript ( $this -> page );
2010-03-16 05:57:51 +00:00
2010-04-22 05:15:23 +00:00
$strrate = get_string ( " rate " , " rating " );
$ratinghtml = '' ; //the string we'll return
2011-05-11 13:32:36 +08:00
// permissions check - can they view the aggregate?
if ( $rating -> user_can_view_aggregate ()) {
2010-03-16 05:57:51 +00:00
2011-05-11 13:32:36 +08:00
$aggregatelabel = $ratingmanager -> get_aggregate_label ( $rating -> settings -> aggregationmethod );
2019-09-03 09:54:10 +08:00
$aggregatelabel = html_writer :: tag ( 'span' , $aggregatelabel , array ( 'class' => 'rating-aggregate-label' ));
2011-05-11 13:32:36 +08:00
$aggregatestr = $rating -> get_aggregate_string ();
2010-03-16 05:57:51 +00:00
2011-12-05 14:57:36 +08:00
$aggregatehtml = html_writer :: tag ( 'span' , $aggregatestr , array ( 'id' => 'ratingaggregate' . $rating -> itemid , 'class' => 'ratingaggregate' )) . ' ' ;
2011-05-11 13:32:36 +08:00
if ( $rating -> count > 0 ) {
2011-12-05 14:57:36 +08:00
$countstr = " ( { $rating -> count } ) " ;
2010-04-23 09:44:19 +00:00
} else {
2011-12-05 14:57:36 +08:00
$countstr = '-' ;
2010-04-23 09:44:19 +00:00
}
2011-12-05 14:57:36 +08:00
$aggregatehtml .= html_writer :: tag ( 'span' , $countstr , array ( 'id' => " ratingcount { $rating -> itemid } " , 'class' => 'ratingcount' )) . ' ' ;
2010-04-22 05:15:23 +00:00
2010-04-23 09:44:19 +00:00
if ( $rating -> settings -> permissions -> viewall && $rating -> settings -> pluginpermissions -> viewall ) {
2011-05-11 13:32:36 +08:00
$nonpopuplink = $rating -> get_view_ratings_url ();
$popuplink = $rating -> get_view_ratings_url ( true );
2010-03-16 05:57:51 +00:00
2010-04-23 09:44:19 +00:00
$action = new popup_action ( 'click' , $popuplink , 'ratings' , array ( 'height' => 400 , 'width' => 600 ));
2019-09-03 09:54:10 +08:00
$aggregatehtml = $this -> action_link ( $nonpopuplink , $aggregatehtml , $action );
2010-03-16 05:57:51 +00:00
}
2019-09-03 09:54:10 +08:00
$ratinghtml .= html_writer :: tag ( 'span' , $aggregatelabel . $aggregatehtml , array ( 'class' => 'rating-aggregate-container' ));
2010-04-23 09:44:19 +00:00
}
2010-03-16 05:57:51 +00:00
2010-04-23 09:44:19 +00:00
$formstart = null ;
2011-05-11 13:32:36 +08:00
// if the item doesn't belong to the current user, the user has permission to rate
// and we're within the assessable period
if ( $rating -> user_can_rate ()) {
2010-05-17 07:31:22 +00:00
2011-05-11 13:32:36 +08:00
$rateurl = $rating -> get_rate_url ();
$inputs = $rateurl -> params ();
2010-05-17 07:31:22 +00:00
2011-05-11 13:32:36 +08:00
//start the rating form
$formattrs = array (
'id' => " postrating { $rating -> itemid } " ,
'class' => 'postratingform' ,
'method' => 'post' ,
'action' => $rateurl -> out_omit_querystring ()
);
$formstart = html_writer :: start_tag ( 'form' , $formattrs );
$formstart .= html_writer :: start_tag ( 'div' , array ( 'class' => 'ratingform' ));
// add the hidden inputs
foreach ( $inputs as $name => $value ) {
$attributes = array ( 'type' => 'hidden' , 'class' => 'ratinginput' , 'name' => $name , 'value' => $value );
$formstart .= html_writer :: empty_tag ( 'input' , $attributes );
}
2010-05-21 03:43:45 +00:00
2010-04-23 09:44:19 +00:00
if ( empty ( $ratinghtml )) {
$ratinghtml .= $strrate . ': ' ;
}
$ratinghtml = $formstart . $ratinghtml ;
2010-04-22 05:15:23 +00:00
2011-05-11 13:32:36 +08:00
$scalearray = array ( RATING_UNSET_RATING => $strrate . '...' ) + $rating -> settings -> scale -> scaleitems ;
$scaleattrs = array ( 'class' => 'postratingmenu ratinginput' , 'id' => 'menurating' . $rating -> itemid );
2012-07-31 12:07:16 +08:00
$ratinghtml .= html_writer :: label ( $rating -> rating , 'menurating' . $rating -> itemid , false , array ( 'class' => 'accesshide' ));
2011-05-11 13:32:36 +08:00
$ratinghtml .= html_writer :: select ( $scalearray , 'rating' , $rating -> rating , false , $scaleattrs );
2010-03-16 05:57:51 +00:00
2010-04-23 09:44:19 +00:00
//output submit button
2010-05-17 07:31:22 +00:00
$ratinghtml .= html_writer :: start_tag ( 'span' , array ( 'class' => " ratingsubmit " ));
2011-05-11 13:32:36 +08:00
$attributes = array ( 'type' => 'submit' , 'class' => 'postratingmenusubmit' , 'id' => 'postratingsubmit' . $rating -> itemid , 'value' => s ( get_string ( 'rate' , 'rating' )));
2010-05-17 07:31:22 +00:00
$ratinghtml .= html_writer :: empty_tag ( 'input' , $attributes );
2010-03-16 05:57:51 +00:00
2011-05-11 13:32:36 +08:00
if ( ! $rating -> settings -> scale -> isnumeric ) {
2012-10-15 16:48:21 +08:00
// If a global scale, try to find current course ID from the context
if ( empty ( $rating -> settings -> scale -> courseid ) and $coursecontext = $rating -> context -> get_course_context ( false )) {
$courseid = $coursecontext -> instanceid ;
} else {
$courseid = $rating -> settings -> scale -> courseid ;
}
$ratinghtml .= $this -> help_icon_scale ( $courseid , $rating -> settings -> scale );
2010-03-16 05:57:51 +00:00
}
2010-05-17 07:31:22 +00:00
$ratinghtml .= html_writer :: end_tag ( 'span' );
$ratinghtml .= html_writer :: end_tag ( 'div' );
$ratinghtml .= html_writer :: end_tag ( 'form' );
2010-03-16 05:57:51 +00:00
}
2010-04-22 05:15:23 +00:00
return $ratinghtml ;
2010-03-16 05:57:51 +00:00
}
2012-01-05 13:28:08 +13:00
/**
2009-08-10 06:22:04 +00:00
* Centered heading with attached help button ( same title text )
2012-01-05 13:28:08 +13:00
* and optional icon attached .
*
2009-12-30 15:19:55 +00:00
* @ param string $text A heading text
2010-04-10 09:10:08 +00:00
* @ param string $helpidentifier The keyword that defines a help page
2009-12-30 15:19:55 +00:00
* @ param string $component component name
* @ param string | moodle_url $icon
* @ param string $iconalt icon alt text
2013-08-29 10:30:02 +08:00
* @ param int $level The level of importance of the heading . Defaulting to 2
* @ param string $classnames A space - separated list of CSS classes . Defaulting to null
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
2013-08-29 10:30:02 +08:00
public function heading_with_help ( $text , $helpidentifier , $component = 'moodle' , $icon = '' , $iconalt = '' , $level = 2 , $classnames = null ) {
2009-12-30 15:19:55 +00:00
$image = '' ;
if ( $icon ) {
2013-10-27 09:42:00 +08:00
$image = $this -> pix_icon ( $icon , $iconalt , $component , array ( 'class' => 'icon iconlarge' ));
2009-08-10 06:22:04 +00:00
}
2009-12-30 15:19:55 +00:00
2010-04-13 21:51:49 +00:00
$help = '' ;
if ( $helpidentifier ) {
$help = $this -> help_icon ( $helpidentifier , $component );
}
2009-12-30 15:19:55 +00:00
2013-08-29 10:30:02 +08:00
return $this -> heading ( $image . $text . $help , $level , $classnames );
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a help icon .
2009-08-10 06:22:04 +00:00
*
2010-08-16 15:29:46 +00:00
* @ deprecated since Moodle 2.0
2010-01-13 18:50:28 +00:00
*/
2010-04-13 20:34:27 +00:00
public function old_help_icon ( $helpidentifier , $title , $component = 'moodle' , $linktext = '' ) {
2013-02-05 09:25:29 +00:00
throw new coding_exception ( 'old_help_icon() can not be used any more, please see help_icon().' );
2009-08-10 06:22:04 +00:00
}
2010-04-13 21:51:49 +00:00
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a help icon .
2010-04-13 21:51:49 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_help_icon ()} instead .
*
2010-04-13 21:51:49 +00:00
* @ param string $identifier The keyword that defines a help page
* @ param string $component component name
* @ param string | bool $linktext true means use $title as link text , string means link text value
* @ return string HTML fragment
*/
public function help_icon ( $identifier , $component = 'moodle' , $linktext = '' ) {
2010-05-04 08:29:05 +00:00
$icon = new help_icon ( $identifier , $component );
2010-04-13 21:51:49 +00:00
$icon -> diag_strings ();
if ( $linktext === true ) {
$icon -> linktext = get_string ( $icon -> identifier , $icon -> component );
} else if ( ! empty ( $linktext )) {
$icon -> linktext = $linktext ;
}
return $this -> render ( $icon );
}
/**
* Implementation of user image rendering .
2012-01-05 13:28:08 +13:00
*
2012-01-12 12:55:50 +13:00
* @ param help_icon $helpicon A help icon instance
2010-04-13 21:51:49 +00:00
* @ return string HTML fragment
*/
protected function render_help_icon ( help_icon $helpicon ) {
2019-02-13 16:29:22 +08:00
$context = $helpicon -> export_for_template ( $this );
return $this -> render_from_template ( 'core/help_icon' , $context );
2010-04-13 21:51:49 +00:00
}
2009-08-10 06:22:04 +00:00
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a scale help icon .
2009-08-10 06:22:04 +00:00
*
2009-12-30 15:19:55 +00:00
* @ param int $courseid
2012-01-05 13:28:08 +13:00
* @ param stdClass $scale instance
* @ return string HTML fragment
2009-08-10 06:22:04 +00:00
*/
2009-12-30 15:19:55 +00:00
public function help_icon_scale ( $courseid , stdClass $scale ) {
global $CFG ;
2009-10-23 18:49:00 +00:00
2009-12-30 15:19:55 +00:00
$title = get_string ( 'helpprefix2' , '' , $scale -> name ) . ' (' . get_string ( 'newwindow' ) . ')' ;
2009-10-23 18:49:00 +00:00
2010-02-16 17:31:35 +00:00
$icon = $this -> pix_icon ( 'help' , get_string ( 'scales' ), 'moodle' , array ( 'class' => 'iconhelp' ));
2009-10-23 18:49:00 +00:00
2010-10-11 06:23:03 +00:00
$scaleid = abs ( $scale -> id );
$link = new moodle_url ( '/course/scales.php' , array ( 'id' => $courseid , 'list' => true , 'scaleid' => $scaleid ));
2010-03-24 02:35:16 +00:00
$action = new popup_action ( 'click' , $link , 'ratingscale' );
2009-10-23 18:49:00 +00:00
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'span' , $this -> action_link ( $link , $icon , $action ), array ( 'class' => 'helplink' ));
2009-08-10 06:22:04 +00:00
}
/**
* Creates and returns a spacer image with optional line break .
*
2012-01-12 12:55:50 +13:00
* @ param array $attributes Any HTML attributes to add to the spaced .
* @ param bool $br Include a BR after the spacer .... DON 'T USE THIS. Don' t be
* laxy do it with CSS which is a much better solution .
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
2010-02-16 17:31:35 +00:00
public function spacer ( array $attributes = null , $br = false ) {
$attributes = ( array ) $attributes ;
if ( empty ( $attributes [ 'width' ])) {
$attributes [ 'width' ] = 1 ;
2009-12-27 23:31:46 +00:00
}
2010-09-17 08:01:16 +00:00
if ( empty ( $attributes [ 'height' ])) {
2010-02-16 17:31:35 +00:00
$attributes [ 'height' ] = 1 ;
2009-08-10 06:22:04 +00:00
}
2010-02-16 17:31:35 +00:00
$attributes [ 'class' ] = 'spacer' ;
2009-08-10 06:22:04 +00:00
2010-02-16 17:31:35 +00:00
$output = $this -> pix_icon ( 'spacer' , '' , 'moodle' , $attributes );
2009-08-14 03:16:23 +00:00
2010-02-16 17:31:35 +00:00
if ( ! empty ( $br )) {
2009-12-27 23:31:46 +00:00
$output .= '<br />' ;
}
2009-08-10 06:22:04 +00:00
return $output ;
}
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display the specified user ' s avatar .
2009-08-10 06:22:04 +00:00
*
2010-01-13 17:13:52 +00:00
* User avatar may be obtained in two ways :
2009-08-10 06:22:04 +00:00
* < pre >
2009-12-27 19:47:21 +00:00
* // Option 1: (shortcut for simple cases, preferred way)
* // $user has come from the DB and has fields id, picture, imagealt, firstname and lastname
* $OUTPUT -> user_picture ( $user , array ( 'popup' => true ));
*
2010-01-13 17:13:52 +00:00
* // Option 2:
* $userpic = new user_picture ( $user );
2009-08-10 06:22:04 +00:00
* // Set properties of $userpic
2009-12-27 19:47:21 +00:00
* $userpic -> popup = true ;
2010-01-13 17:13:52 +00:00
* $OUTPUT -> render ( $userpic );
2009-08-10 06:22:04 +00:00
* </ pre >
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_user_picture ()} instead .
*
2012-01-05 13:28:08 +13:00
* @ param stdClass $user Object with at least fields id , picture , imagealt , firstname , lastname
2009-12-27 19:47:21 +00:00
* If any of these are missing , the database is queried . Avoid this
2009-08-10 06:22:04 +00:00
* if at all possible , particularly for reports . It is very bad for performance .
2009-12-27 19:47:21 +00:00
* @ param array $options associative array with user picture options , used only if not a user_picture object ,
* options are :
* - courseid = $this -> page -> course -> id ( course id of user profile in link )
* - size = 35 ( size of image )
* - link = true ( make image clickable - the link leads to user profile )
* - popup = false ( open in popup )
* - alttext = true ( add image alt attribute )
2010-01-13 17:13:52 +00:00
* - class = image class attribute ( default 'userpicture' )
2014-07-08 15:17:26 +08:00
* - visibletoscreenreaders = true ( whether to be visible to screen readers )
2017-07-18 17:11:59 +08:00
* - includefullname = false ( whether to include the user ' s full name together with the user picture )
2019-03-22 11:48:12 +01:00
* - includetoken = false ( whether to use a token for authentication . True for current user , int value for other user id )
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
2010-01-13 17:13:52 +00:00
public function user_picture ( stdClass $user , array $options = null ) {
$userpicture = new user_picture ( $user );
foreach (( array ) $options as $key => $value ) {
2019-10-20 18:14:48 +02:00
if ( property_exists ( $userpicture , $key )) {
2010-01-13 17:13:52 +00:00
$userpicture -> $key = $value ;
}
}
return $this -> render ( $userpicture );
}
/**
* Internal implementation of user image rendering .
2012-01-05 13:28:08 +13:00
*
2010-01-13 17:13:52 +00:00
* @ param user_picture $userpicture
* @ return string
*/
protected function render_user_picture ( user_picture $userpicture ) {
$user = $userpicture -> user ;
2018-11-22 11:39:10 +01:00
$canviewfullnames = has_capability ( 'moodle/site:viewfullnames' , $this -> page -> context );
2010-01-13 17:13:52 +00:00
2020-12-08 14:54:19 +07:00
$alt = '' ;
2010-01-13 17:13:52 +00:00
if ( $userpicture -> alttext ) {
if ( ! empty ( $user -> imagealt )) {
$alt = $user -> imagealt ;
}
}
if ( empty ( $userpicture -> size )) {
$size = 35 ;
} else if ( $userpicture -> size === true or $userpicture -> size == 1 ) {
$size = 100 ;
} else {
$size = $userpicture -> size ;
2009-08-10 06:22:04 +00:00
}
2010-01-13 17:13:52 +00:00
$class = $userpicture -> class ;
2009-08-10 06:22:04 +00:00
2012-04-30 23:36:47 +02:00
if ( $user -> picture == 0 ) {
2010-01-13 17:13:52 +00:00
$class .= ' defaultuserpic' ;
}
2009-08-10 06:22:04 +00:00
2011-09-06 11:29:05 +12:00
$src = $userpicture -> get_url ( $this -> page , $this );
2019-01-04 15:49:23 +08:00
$attributes = array ( 'src' => $src , 'class' => $class , 'width' => $size , 'height' => $size );
2014-07-08 15:17:26 +08:00
if ( ! $userpicture -> visibletoscreenreaders ) {
2019-01-04 15:49:23 +08:00
$alt = '' ;
}
2020-12-05 00:57:47 +11:00
$attributes [ 'alt' ] = $alt ;
2019-01-04 15:49:23 +08:00
if ( ! empty ( $alt )) {
$attributes [ 'title' ] = $alt ;
2014-07-08 15:17:26 +08:00
}
2021-08-11 14:33:21 +02:00
// get the image html output first
if ( $user -> picture == 0 && ! defined ( 'BEHAT_SITE_RUNNING' )) {
$output = html_writer :: tag ( 'span' , mb_substr ( $user -> firstname , 0 , 1 ) . mb_substr ( $user -> lastname , 0 , 1 ),
[ 'class' => 'userinitials size-' . $size ]);
} else {
$output = html_writer :: empty_tag ( 'img' , $attributes );
}
2010-01-13 17:13:52 +00:00
2017-07-18 17:11:59 +08:00
// Show fullname together with the picture when desired.
if ( $userpicture -> includefullname ) {
2018-06-18 15:01:46 +08:00
$output .= fullname ( $userpicture -> user , $canviewfullnames );
2017-07-18 17:11:59 +08:00
}
2010-01-13 17:13:52 +00:00
if ( empty ( $userpicture -> courseid )) {
$courseid = $this -> page -> course -> id ;
} else {
$courseid = $userpicture -> courseid ;
}
2010-05-04 13:04:35 +00:00
if ( $courseid == SITEID ) {
$url = new moodle_url ( '/user/profile.php' , array ( 'id' => $user -> id ));
} else {
$url = new moodle_url ( '/user/view.php' , array ( 'id' => $user -> id , 'course' => $courseid ));
}
2010-01-13 17:13:52 +00:00
2020-12-08 14:54:19 +07:00
// Then wrap it in link if needed. Also we don't wrap it in link if the link redirects to itself.
2021-03-15 20:20:33 +01:00
if ( ! $userpicture -> link ||
( $this -> page -> has_set_url () && $this -> page -> url == $url )) { // Protect against unset page->url.
2020-12-08 14:54:19 +07:00
return $output ;
}
2020-04-08 12:24:02 +02:00
$attributes = array ( 'href' => $url , 'class' => 'd-inline-block aabtn' );
2014-07-08 15:17:26 +08:00
if ( ! $userpicture -> visibletoscreenreaders ) {
$attributes [ 'tabindex' ] = '-1' ;
$attributes [ 'aria-hidden' ] = 'true' ;
}
2010-01-13 17:13:52 +00:00
if ( $userpicture -> popup ) {
$id = html_writer :: random_id ( 'userpicture' );
$attributes [ 'id' ] = $id ;
2010-02-11 15:44:06 +00:00
$this -> add_action_handler ( new popup_action ( 'click' , $url ), $id );
2010-01-13 17:13:52 +00:00
}
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'a' , $output , $attributes );
2009-08-10 06:22:04 +00:00
}
2010-05-26 08:32:31 +00:00
/**
* Internal implementation of file tree viewer items rendering .
2012-01-05 13:28:08 +13:00
*
2010-05-26 08:32:31 +00:00
* @ param array $dir
* @ return string
*/
public function htmllize_file_tree ( $dir ) {
if ( empty ( $dir [ 'subdirs' ]) and empty ( $dir [ 'files' ])) {
return '' ;
}
$result = '<ul>' ;
foreach ( $dir [ 'subdirs' ] as $subdir ) {
$result .= '<li>' . s ( $subdir [ 'dirname' ]) . ' ' . $this -> htmllize_file_tree ( $subdir ) . '</li>' ;
}
foreach ( $dir [ 'files' ] as $file ) {
$filename = $file -> get_filename ();
$result .= '<li><span>' . html_writer :: link ( $file -> fileurl , $filename ) . '</span></li>' ;
}
$result .= '</ul>' ;
return $result ;
}
2012-01-05 13:28:08 +13:00
2010-05-24 07:55:57 +00:00
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display the file picker
2010-05-24 07:55:57 +00:00
*
* < pre >
* $OUTPUT -> file_picker ( $options );
* </ pre >
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_file_picker ()} instead .
*
2010-05-24 07:55:57 +00:00
* @ param array $options associative array with file manager options
* options are :
* maxbytes =>- 1 ,
* itemid => 0 ,
* client_id => uniqid (),
* acepted_types => '*' ,
* return_types => FILE_INTERNAL ,
2020-03-24 13:32:22 +00:00
* context => current page context
2010-05-24 07:55:57 +00:00
* @ return string HTML fragment
*/
public function file_picker ( $options ) {
$fp = new file_picker ( $options );
return $this -> render ( $fp );
}
2012-01-05 13:28:08 +13:00
2010-05-26 08:32:31 +00:00
/**
* Internal implementation of file picker rendering .
2012-01-05 13:28:08 +13:00
*
2010-05-26 08:32:31 +00:00
* @ param file_picker $fp
* @ return string
*/
2010-05-24 07:55:57 +00:00
public function render_file_picker ( file_picker $fp ) {
$options = $fp -> options ;
$client_id = $options -> client_id ;
$strsaved = get_string ( 'filesaved' , 'repository' );
$straddfile = get_string ( 'openpicker' , 'repository' );
$strloading = get_string ( 'loading' , 'repository' );
2012-02-23 19:34:59 +00:00
$strdndenabled = get_string ( 'dndenabled_inbox' , 'moodle' );
2012-04-26 11:53:52 +08:00
$strdroptoupload = get_string ( 'droptoupload' , 'moodle' );
2020-03-24 14:20:45 +00:00
$iconprogress = $this -> pix_icon ( 'i/loading_small' , $strloading ) . '' ;
2010-05-24 07:55:57 +00:00
$currentfile = $options -> currentfile ;
if ( empty ( $currentfile )) {
2012-05-30 10:05:10 +08:00
$currentfile = '' ;
} else {
$currentfile .= ' - ' ;
2010-05-24 07:55:57 +00:00
}
2010-09-30 03:36:38 +00:00
if ( $options -> maxbytes ) {
$size = $options -> maxbytes ;
} else {
$size = get_max_upload_file_size ();
}
2010-10-04 03:09:58 +00:00
if ( $size == - 1 ) {
2010-10-04 10:03:52 +00:00
$maxsize = '' ;
2010-10-04 03:09:58 +00:00
} else {
2021-09-22 11:42:33 +01:00
$maxsize = get_string ( 'maxfilesize' , 'moodle' , display_size ( $size , 0 ));
2010-10-04 03:09:58 +00:00
}
2011-04-18 11:42:27 +02:00
if ( $options -> buttonname ) {
2011-04-12 14:30:13 +12:00
$buttonname = ' name="' . $options -> buttonname . '"' ;
} else {
$buttonname = '' ;
}
2010-05-24 07:55:57 +00:00
$html = <<< EOD
< div class = " filemanager-loading mdl-align " id = 'filepicker-loading-{$client_id}' >
2020-03-24 14:20:45 +00:00
$iconprogress
2010-05-24 07:55:57 +00:00
</ div >
2018-04-17 15:13:31 +02:00
< div id = " filepicker-wrapper- { $client_id } " class = " mdl-left w-100 " style = " display:none " >
2010-05-24 07:55:57 +00:00
< div >
2016-11-11 13:14:30 +00:00
< input type = " button " class = " btn btn-secondary fp-btn-choose " id = " filepicker-button- { $client_id } " value = " { $straddfile } " { $buttonname } />
2010-09-07 10:06:57 +00:00
< span > $maxsize </ span >
2010-05-24 07:55:57 +00:00
</ div >
EOD ;
if ( $options -> env != 'url' ) {
$html .= <<< EOD
2020-08-10 16:10:19 +02:00
< div id = " file_info_ { $client_id } " class = " mdl-left filepicker-filelist " style = " position: relative " >
2012-05-24 15:02:52 +08:00
< div class = " filepicker-filename " >
2012-11-27 15:58:30 +08:00
< div class = " filepicker-container " > $currentfile < div class = " dndupload-message " > $strdndenabled < br />< div class = " dndupload-arrow " ></ div ></ div ></ div >
2013-01-16 11:18:18 +08:00
< div class = " dndupload-progressbars " ></ div >
2012-05-24 15:02:52 +08:00
</ div >
2012-11-27 15:58:30 +08:00
< div >< div class = " dndupload-target " > { $strdroptoupload } < br />< div class = " dndupload-arrow " ></ div ></ div ></ div >
2011-11-08 20:05:19 +00:00
</ div >
2010-05-24 07:55:57 +00:00
EOD ;
}
$html .= '</div>' ;
return $html ;
}
2009-08-10 06:22:04 +00:00
/**
2016-07-27 14:13:25 +08:00
* @ deprecated since Moodle 3.2
2009-08-10 06:22:04 +00:00
*/
2018-07-19 14:15:19 +08:00
public function update_module_button () {
throw new coding_exception ( 'core_renderer::update_module_button() can not be used anymore. Activity ' .
'modules should not add the edit module button, the link is already available in the Administration block. ' .
'Themes can choose to display the link in the buttons row consistently for all module types.' );
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a " Turn editing on/off " button in a form .
*
2009-08-10 06:22:04 +00:00
* @ param moodle_url $url The URL + params to send through when clicking the button
* @ return string HTML the button
*/
public function edit_button ( moodle_url $url ) {
2010-08-03 10:56:35 +00:00
2021-07-06 10:01:05 +02:00
if ( $this -> page -> theme -> haseditswitch == true ) {
return ;
}
2010-08-03 10:56:35 +00:00
$url -> param ( 'sesskey' , sesskey ());
if ( $this -> page -> user_is_editing ()) {
$url -> param ( 'edit' , 'off' );
$editstring = get_string ( 'turneditingoff' );
2009-08-10 06:22:04 +00:00
} else {
2010-08-03 10:56:35 +00:00
$url -> param ( 'edit' , 'on' );
$editstring = get_string ( 'turneditingon' );
2009-08-10 06:22:04 +00:00
}
2010-08-03 10:56:35 +00:00
return $this -> single_button ( $url , $editstring );
2009-08-10 06:22:04 +00:00
}
2021-07-06 10:01:05 +02:00
/**
* Create a navbar switch for toggling editing mode .
*
* @ return string Html containing the edit switch
*/
public function edit_switch () {
if ( $this -> page -> user_allowed_editing ()) {
$temp = ( object ) [
'legacyseturl' => ( new moodle_url ( '/editmode.php' )) -> out ( false ),
'pagecontextid' => $this -> page -> context -> id ,
'pageurl' => $this -> page -> url ,
'sesskey' => sesskey (),
];
if ( $this -> page -> user_is_editing ()) {
$temp -> checked = true ;
}
return $this -> render_from_template ( 'core/editswitch' , $temp );
}
}
2009-08-10 06:22:04 +00:00
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a simple button to close a window
2009-08-10 06:22:04 +00:00
*
* @ param string $text The lang string for the button ' s label ( already output from get_string ())
2010-01-14 19:18:04 +00:00
* @ return string html fragment
2009-08-10 06:22:04 +00:00
*/
2009-08-13 01:15:58 +00:00
public function close_window_button ( $text = '' ) {
2009-08-10 06:22:04 +00:00
if ( empty ( $text )) {
$text = get_string ( 'closewindow' );
}
2010-01-16 15:39:56 +00:00
$button = new single_button ( new moodle_url ( '#' ), $text , 'get' );
$button -> add_action ( new component_action ( 'click' , 'close_window' ));
2010-01-14 19:18:04 +00:00
return $this -> container ( $this -> render ( $button ), 'closewindow' );
2009-08-10 06:22:04 +00:00
}
/**
* Output an error message . By default wraps the error message in < span class = " error " >.
* If the error message is blank , nothing is output .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $message the error message .
* @ return string the HTML to output .
*/
public function error_text ( $message ) {
if ( empty ( $message )) {
return '' ;
}
2013-01-31 11:02:49 +08:00
$message = $this -> pix_icon ( 'i/warning' , get_string ( 'error' ), '' , array ( 'class' => 'icon icon-pre' , 'title' => '' )) . $message ;
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'span' , $message , array ( 'class' => 'error' ));
2009-08-10 06:22:04 +00:00
}
/**
* Do not call this function directly .
*
2012-02-14 18:24:09 +13:00
* To terminate the current script with a fatal error , call the { @ link print_error }
2009-08-10 06:22:04 +00:00
* function , or throw an exception . Doing either of those things will then call this
* function to display the error , before terminating the execution .
*
* @ param string $message The message to output
* @ param string $moreinfourl URL where more info can be found about the error
* @ param string $link Link for the Continue button
* @ param array $backtrace The execution backtrace
* @ param string $debuginfo Debugging information
* @ return string the HTML to output .
*/
2016-09-13 14:23:21 +02:00
public function fatal_error ( $message , $moreinfourl , $link , $backtrace , $debuginfo = null , $errorcode = " " ) {
2010-11-05 08:44:51 +00:00
global $CFG ;
2009-08-10 06:22:04 +00:00
$output = '' ;
2009-10-31 14:12:16 +00:00
$obbuffer = '' ;
2009-08-13 03:38:30 +00:00
2009-08-10 06:22:04 +00:00
if ( $this -> has_started ()) {
2009-10-30 13:44:07 +00:00
// we can not always recover properly here, we have problems with output buffering,
// html tables, etc.
2009-08-10 06:22:04 +00:00
$output .= $this -> opencontainers -> pop_all_but_last ();
2009-10-30 13:44:07 +00:00
2009-08-10 06:22:04 +00:00
} else {
2009-10-30 13:44:07 +00:00
// It is really bad if library code throws exception when output buffering is on,
// because the buffered text would be printed before our start of page.
// NOTE: this hack might be behave unexpectedly in case output buffering is enabled in PHP.ini
2010-11-05 08:44:51 +00:00
error_reporting ( 0 ); // disable notices from gzip compression, etc.
2009-10-30 13:44:07 +00:00
while ( ob_get_level () > 0 ) {
2010-11-05 08:32:36 +00:00
$buff = ob_get_clean ();
if ( $buff === false ) {
break ;
}
$obbuffer .= $buff ;
2009-10-30 13:44:07 +00:00
}
2010-11-05 08:44:51 +00:00
error_reporting ( $CFG -> debug );
2009-10-31 14:12:16 +00:00
2013-03-25 16:06:02 +01:00
// Output not yet started.
$protocol = ( isset ( $_SERVER [ 'SERVER_PROTOCOL' ]) ? $_SERVER [ 'SERVER_PROTOCOL' ] : 'HTTP/1.0' );
if ( empty ( $_SERVER [ 'HTTP_RANGE' ])) {
@ header ( $protocol . ' 404 Not Found' );
2016-11-07 09:55:11 +08:00
} else if ( core_useragent :: check_safari_ios_version ( 602 ) && ! empty ( $_SERVER [ 'HTTP_X_PLAYBACK_SESSION_ID' ])) {
// Coax iOS 10 into sending the session cookie.
@ header ( $protocol . ' 403 Forbidden' );
2013-03-25 16:06:02 +01:00
} else {
// Must stop byteserving attempts somehow,
// this is weird but Chrome PDF viewer can be stopped only with 407!
@ header ( $protocol . ' 407 Proxy Authentication Required' );
2009-10-30 12:46:16 +00:00
}
2013-03-25 16:06:02 +01:00
2010-08-16 19:11:21 +00:00
$this -> page -> set_context ( null ); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
2010-01-18 09:40:14 +00:00
$this -> page -> set_url ( '/' ); // no url
2009-12-27 12:02:04 +00:00
//$this->page->set_pagelayout('base'); //TODO: MDL-20676 blocks on error pages are weird, unfortunately it somehow detect the pagelayout from URL :-(
2010-06-23 09:13:23 +00:00
$this -> page -> set_title ( get_string ( 'error' ));
2010-06-23 09:16:53 +00:00
$this -> page -> set_heading ( $this -> page -> course -> fullname );
2009-08-10 06:22:04 +00:00
$output .= $this -> header ();
}
2019-10-29 16:29:46 +08:00
$message = '<p class="errormessage">' . s ( $message ) . '</p>' .
'<p class="errorcode"><a href="' . s ( $moreinfourl ) . '">' .
2009-08-10 06:22:04 +00:00
get_string ( 'moreinformation' ) . '</a></p>' ;
2011-09-11 00:07:28 +02:00
if ( empty ( $CFG -> rolesactive )) {
$message .= '<p class="errormessage">' . get_string ( 'installproblem' , 'error' ) . '</p>' ;
//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.
}
2018-02-26 13:02:14 +01:00
$output .= $this -> box ( $message , 'errorbox alert alert-danger' , null , array ( 'data-rel' => 'fatalerror' ));
2009-08-10 06:22:04 +00:00
2013-08-10 22:46:49 +02:00
if ( $CFG -> debugdeveloper ) {
2019-06-05 18:44:46 +10:00
$labelsep = get_string ( 'labelsep' , 'langconfig' );
2009-10-31 14:12:16 +00:00
if ( ! empty ( $debuginfo )) {
2010-03-20 13:46:15 +00:00
$debuginfo = s ( $debuginfo ); // removes all nasty JS
$debuginfo = str_replace ( " \n " , '<br />' , $debuginfo ); // keep newlines
2019-06-05 18:44:46 +10:00
$label = get_string ( 'debuginfo' , 'debug' ) . $labelsep ;
$output .= $this -> notification ( " <strong> $label </strong> " . $debuginfo , 'notifytiny' );
2009-10-31 14:12:16 +00:00
}
if ( ! empty ( $backtrace )) {
2019-06-05 18:44:46 +10:00
$label = get_string ( 'stacktrace' , 'debug' ) . $labelsep ;
$output .= $this -> notification ( " <strong> $label </strong> " . format_backtrace ( $backtrace ), 'notifytiny' );
2009-10-31 14:12:16 +00:00
}
if ( $obbuffer !== '' ) {
2019-06-05 18:44:46 +10:00
$label = get_string ( 'outputbuffer' , 'debug' ) . $labelsep ;
$output .= $this -> notification ( " <strong> $label </strong> " . s ( $obbuffer ), 'notifytiny' );
2009-10-31 14:12:16 +00:00
}
2009-08-10 06:22:04 +00:00
}
2011-09-09 10:19:27 +02:00
if ( empty ( $CFG -> rolesactive )) {
// continue does not make much sense if moodle is not installed yet because error is most probably not recoverable
} else if ( ! empty ( $link )) {
2009-08-10 06:22:04 +00:00
$output .= $this -> continue_button ( $link );
}
$output .= $this -> footer ();
// Padding to encourage IE to display our error page, rather than its own.
$output .= str_repeat ( ' ' , 512 );
return $output ;
}
/**
2016-02-16 08:45:29 +08:00
* Output a notification ( that is , a status message about something that has just happened ) .
2009-08-10 06:22:04 +00:00
*
2016-02-16 08:48:39 +08:00
* Note : \core\notification :: add () may be more suitable for your usage .
*
2016-02-16 08:45:29 +08:00
* @ param string $message The message to print out .
2020-11-27 16:20:45 +00:00
* @ param ? string $type The type of notification . See constants on \core\output\notification .
* @ param bool $closebutton Whether to show a close icon to remove the notification ( default true ) .
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
2020-11-27 16:20:45 +00:00
public function notification ( $message , $type = null , $closebutton = true ) {
2016-02-16 08:45:29 +08:00
$typemappings = [
// Valid types.
'success' => \core\output\notification :: NOTIFY_SUCCESS ,
'info' => \core\output\notification :: NOTIFY_INFO ,
'warning' => \core\output\notification :: NOTIFY_WARNING ,
'error' => \core\output\notification :: NOTIFY_ERROR ,
// Legacy types mapped to current types.
'notifyproblem' => \core\output\notification :: NOTIFY_ERROR ,
'notifytiny' => \core\output\notification :: NOTIFY_ERROR ,
'notifyerror' => \core\output\notification :: NOTIFY_ERROR ,
'notifysuccess' => \core\output\notification :: NOTIFY_SUCCESS ,
'notifymessage' => \core\output\notification :: NOTIFY_INFO ,
'notifyredirect' => \core\output\notification :: NOTIFY_INFO ,
'redirectmessage' => \core\output\notification :: NOTIFY_INFO ,
];
$extraclasses = [];
if ( $type ) {
if ( strpos ( $type , ' ' ) === false ) {
// No spaces in the list of classes, therefore no need to loop over and determine the class.
if ( isset ( $typemappings [ $type ])) {
$type = $typemappings [ $type ];
} else {
// The value provided did not match a known type. It must be an extra class.
$extraclasses = [ $type ];
}
} else {
// Identify what type of notification this is.
$classarray = explode ( ' ' , self :: prepare_classes ( $type ));
// Separate out the type of notification from the extra classes.
foreach ( $classarray as $class ) {
if ( isset ( $typemappings [ $class ])) {
$type = $typemappings [ $class ];
} else {
$extraclasses [] = $class ;
}
2015-03-23 14:27:16 +08:00
}
}
}
2020-11-27 16:20:45 +00:00
$notification = new \core\output\notification ( $message , $type , $closebutton );
2016-02-16 08:45:29 +08:00
if ( count ( $extraclasses )) {
$notification -> set_extra_classes ( $extraclasses );
}
2015-03-23 14:27:16 +08:00
2016-02-16 08:45:29 +08:00
// Return the rendered template.
return $this -> render_from_template ( $notification -> get_template_name (), $notification -> export_for_template ( $this ));
2015-03-23 14:27:16 +08:00
}
2018-01-17 10:52:16 +08:00
/**
* @ deprecated since Moodle 3.1 MDL - 30811 - please do not use this function any more .
*/
2018-01-17 02:18:16 +01:00
public function notify_problem () {
throw new coding_exception ( 'core_renderer::notify_problem() can not be used any more, ' .
'please use \core\notification::add(), or \core\output\notification as required.' );
2015-03-23 14:27:16 +08:00
}
2018-01-17 10:52:16 +08:00
/**
* @ deprecated since Moodle 3.1 MDL - 30811 - please do not use this function any more .
*/
2018-01-17 02:18:16 +01:00
public function notify_success () {
throw new coding_exception ( 'core_renderer::notify_success() can not be used any more, ' .
'please use \core\notification::add(), or \core\output\notification as required.' );
2015-03-23 14:27:16 +08:00
}
2018-01-17 10:52:16 +08:00
/**
* @ deprecated since Moodle 3.1 MDL - 30811 - please do not use this function any more .
*/
2018-01-17 02:18:16 +01:00
public function notify_message () {
throw new coding_exception ( 'core_renderer::notify_message() can not be used any more, ' .
'please use \core\notification::add(), or \core\output\notification as required.' );
2015-03-23 14:27:16 +08:00
}
2018-01-17 10:52:16 +08:00
/**
* @ deprecated since Moodle 3.1 MDL - 30811 - please do not use this function any more .
*/
2018-01-17 02:18:16 +01:00
public function notify_redirect () {
throw new coding_exception ( 'core_renderer::notify_redirect() can not be used any more, ' .
'please use \core\notification::add(), or \core\output\notification as required.' );
2015-03-23 14:27:16 +08:00
}
/**
* Render a notification ( that is , a status message about something that has
* just happened ) .
*
* @ param \core\output\notification $notification the notification to print out
* @ return string the HTML to output .
*/
protected function render_notification ( \core\output\notification $notification ) {
2016-02-16 08:45:29 +08:00
return $this -> render_from_template ( $notification -> get_template_name (), $notification -> export_for_template ( $this ));
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a continue button that goes to a particular URL .
2009-08-10 06:22:04 +00:00
*
2010-01-14 19:18:04 +00:00
* @ param string | moodle_url $url The url the button goes to .
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
2010-01-14 19:18:04 +00:00
public function continue_button ( $url ) {
if ( ! ( $url instanceof moodle_url )) {
$url = new moodle_url ( $url );
2009-08-10 06:22:04 +00:00
}
2017-02-04 21:18:19 +05:30
$button = new single_button ( $url , get_string ( 'continue' ), 'get' , true );
2010-01-14 19:18:04 +00:00
$button -> class = 'continuebutton' ;
2009-08-10 06:22:04 +00:00
2010-01-14 19:18:04 +00:00
return $this -> render ( $button );
2009-08-10 06:22:04 +00:00
}
/**
2012-01-05 13:28:08 +13:00
* Returns HTML to display a single paging bar to provide access to other pages ( usually in a search )
2009-08-10 06:22:04 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_paging_bar ()} instead .
*
2010-05-22 20:16:12 +00:00
* @ param int $totalcount The total number of entries available to be paged through
2010-02-17 16:59:41 +00:00
* @ param int $page The page you are currently viewing
* @ param int $perpage The number of entries that should be shown per page
* @ param string | moodle_url $baseurl url of the current page , the $pagevar parameter is added
* @ param string $pagevar name of page parameter that holds the page number
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
2010-02-17 16:59:41 +00:00
public function paging_bar ( $totalcount , $page , $perpage , $baseurl , $pagevar = 'page' ) {
$pb = new paging_bar ( $totalcount , $page , $perpage , $baseurl , $pagevar );
return $this -> render ( $pb );
}
/**
2018-08-09 15:20:55 +02:00
* Returns HTML to display the paging bar .
2012-01-05 13:28:08 +13:00
*
2018-09-04 14:08:50 +02:00
* @ param paging_bar $pagingbar
2018-08-09 15:20:55 +02:00
* @ return string the HTML to output .
2010-02-17 16:59:41 +00:00
*/
protected function render_paging_bar ( paging_bar $pagingbar ) {
2018-08-09 15:20:55 +02:00
// Any more than 10 is not usable and causes weird wrapping of the pagination.
$pagingbar -> maxdisplay = 10 ;
return $this -> render_from_template ( 'core/paging_bar' , $pagingbar -> export_for_template ( $this ));
2009-08-10 06:22:04 +00:00
}
2016-12-12 12:17:59 +11:00
/**
* Returns HTML to display initials bar to provide access to other pages ( usually in a search )
*
* @ param string $current the currently selected letter .
* @ param string $class class name to add to this initial bar .
* @ param string $title the name to put in front of this initial bar .
* @ param string $urlvar URL parameter name for this initial .
* @ param string $url URL object .
* @ param array $alpha of letters in the alphabet .
* @ return string the HTML to output .
*/
public function initials_bar ( $current , $class , $title , $urlvar , $url , $alpha = null ) {
$ib = new initials_bar ( $current , $class , $title , $urlvar , $url , $alpha );
return $this -> render ( $ib );
}
/**
* Internal implementation of initials bar rendering .
*
* @ param initials_bar $initialsbar
* @ return string
*/
protected function render_initials_bar ( initials_bar $initialsbar ) {
return $this -> render_from_template ( 'core/initials_bar' , $initialsbar -> export_for_template ( $this ));
}
2009-08-10 06:22:04 +00:00
/**
* Output the place a skip link goes to .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $id The target name from the corresponding $PAGE -> requires -> skip_link_to ( $target ) call .
* @ return string the HTML to output .
*/
2010-02-16 08:26:21 +00:00
public function skip_link_target ( $id = null ) {
2015-11-05 16:49:24 +08:00
return html_writer :: span ( '' , '' , array ( 'id' => $id ));
2009-08-10 06:22:04 +00:00
}
/**
* Outputs a heading
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $text The text of the heading
* @ param int $level The level of importance of the heading . Defaulting to 2
2013-08-29 10:30:02 +08:00
* @ param string $classes A space - separated list of CSS classes . Defaulting to null
2009-08-10 06:22:04 +00:00
* @ param string $id An optional ID
* @ return string the HTML to output .
*/
2013-08-29 10:30:02 +08:00
public function heading ( $text , $level = 2 , $classes = null , $id = null ) {
2009-08-10 06:22:04 +00:00
$level = ( integer ) $level ;
if ( $level < 1 or $level > 6 ) {
throw new coding_exception ( 'Heading level must be an integer between 1 and 6.' );
}
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'h' . $level , $text , array ( 'id' => $id , 'class' => renderer_base :: prepare_classes ( $classes )));
2009-08-10 06:22:04 +00:00
}
/**
* Outputs a box .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $contents The contents of the box
* @ param string $classes A space - separated list of CSS classes
* @ param string $id An optional ID
2013-07-24 11:18:19 +12:00
* @ param array $attributes An array of other attributes to give the box .
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
2013-07-24 11:18:19 +12:00
public function box ( $contents , $classes = 'generalbox' , $id = null , $attributes = array ()) {
return $this -> box_start ( $classes , $id , $attributes ) . $contents . $this -> box_end ();
2009-08-10 06:22:04 +00:00
}
/**
2012-01-12 12:55:50 +13:00
* Outputs the opening section of a box .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $classes A space - separated list of CSS classes
* @ param string $id An optional ID
2013-07-24 11:18:19 +12:00
* @ param array $attributes An array of other attributes to give the box .
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
2013-07-24 11:18:19 +12:00
public function box_start ( $classes = 'generalbox' , $id = null , $attributes = array ()) {
2010-01-13 17:13:52 +00:00
$this -> opencontainers -> push ( 'box' , html_writer :: end_tag ( 'div' ));
2013-07-24 11:18:19 +12:00
$attributes [ 'id' ] = $id ;
2019-02-13 16:29:22 +08:00
$attributes [ 'class' ] = 'box py-3 ' . renderer_base :: prepare_classes ( $classes );
2013-07-24 11:18:19 +12:00
return html_writer :: start_tag ( 'div' , $attributes );
2009-08-10 06:22:04 +00:00
}
/**
* Outputs the closing section of a box .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
public function box_end () {
return $this -> opencontainers -> pop ( 'box' );
}
/**
* Outputs a container .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $contents The contents of the box
* @ param string $classes A space - separated list of CSS classes
* @ param string $id An optional ID
* @ return string the HTML to output .
*/
2010-02-16 08:26:21 +00:00
public function container ( $contents , $classes = null , $id = null ) {
2009-08-10 06:22:04 +00:00
return $this -> container_start ( $classes , $id ) . $contents . $this -> container_end ();
}
/**
* Outputs the opening section of a container .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $classes A space - separated list of CSS classes
* @ param string $id An optional ID
* @ return string the HTML to output .
*/
2010-02-16 08:26:21 +00:00
public function container_start ( $classes = null , $id = null ) {
2010-01-13 17:13:52 +00:00
$this -> opencontainers -> push ( 'container' , html_writer :: end_tag ( 'div' ));
return html_writer :: start_tag ( 'div' , array ( 'id' => $id ,
2009-12-16 18:00:58 +00:00
'class' => renderer_base :: prepare_classes ( $classes )));
2009-08-10 06:22:04 +00:00
}
/**
* Outputs the closing section of a container .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string the HTML to output .
*/
public function container_end () {
return $this -> opencontainers -> pop ( 'container' );
}
2009-08-28 08:47:31 +00:00
2010-04-19 06:30:30 +00:00
/**
2009-08-28 08:47:31 +00:00
* Make nested HTML lists out of the items
*
* The resulting list will look something like this :
*
* < pre >
* << ul >>
* << li >>< div class = 'tree_item parent' > ( item contents ) </ div >
* << ul >
* << li >>< div class = 'tree_item' > ( item contents ) </ div ><</ li >>
* <</ ul >>
* <</ li >>
* <</ ul >>
* </ pre >
*
2012-01-05 13:28:08 +13:00
* @ param array $items
* @ param array $attrs html attributes passed to the top ofs the list
2009-08-28 08:47:31 +00:00
* @ return string HTML
*/
2012-01-05 13:28:08 +13:00
public function tree_block_contents ( $items , $attrs = array ()) {
2009-08-28 08:47:31 +00:00
// exit if empty, we don't want an empty ul element
if ( empty ( $items )) {
return '' ;
}
// array of nested li elements
$lis = array ();
foreach ( $items as $item ) {
// this applies to the li item which contains all child lists too
$content = $item -> content ( $this );
$liclasses = array ( $item -> get_css_type ());
2010-04-19 06:30:30 +00:00
if ( ! $item -> forceopen || ( ! $item -> forceopen && $item -> collapse ) || ( $item -> children -> count () == 0 && $item -> nodetype == navigation_node :: NODETYPE_BRANCH )) {
2009-08-28 08:47:31 +00:00
$liclasses [] = 'collapsed' ;
}
if ( $item -> isactive === true ) {
$liclasses [] = 'current_branch' ;
}
$liattr = array ( 'class' => join ( ' ' , $liclasses ));
// class attribute on the div item which only contains the item content
$divclasses = array ( 'tree_item' );
2010-04-19 06:30:30 +00:00
if ( $item -> children -> count () > 0 || $item -> nodetype == navigation_node :: NODETYPE_BRANCH ) {
2009-08-28 08:47:31 +00:00
$divclasses [] = 'branch' ;
} else {
$divclasses [] = 'leaf' ;
}
if ( ! empty ( $item -> classes ) && count ( $item -> classes ) > 0 ) {
$divclasses [] = join ( ' ' , $item -> classes );
}
$divattr = array ( 'class' => join ( ' ' , $divclasses ));
if ( ! empty ( $item -> id )) {
$divattr [ 'id' ] = $item -> id ;
}
2010-02-18 18:15:56 +00:00
$content = html_writer :: tag ( 'p' , $content , $divattr ) . $this -> tree_block_contents ( $item -> children );
2009-08-28 08:47:31 +00:00
if ( ! empty ( $item -> preceedwithhr ) && $item -> preceedwithhr === true ) {
2010-02-18 18:15:56 +00:00
$content = html_writer :: empty_tag ( 'hr' ) . $content ;
2009-08-28 08:47:31 +00:00
}
2010-02-18 18:15:56 +00:00
$content = html_writer :: tag ( 'li' , $content , $liattr );
2009-08-28 08:47:31 +00:00
$lis [] = $content ;
}
2010-02-18 18:15:56 +00:00
return html_writer :: tag ( 'ul' , implode ( " \n " , $lis ), $attrs );
2009-08-28 08:47:31 +00:00
}
2016-02-19 14:46:37 +08:00
/**
* Returns a search box .
*
* @ param string $id The search box wrapper div id , defaults to an autogenerated one .
* @ return string HTML with the search form hidden by default .
*/
public function search_box ( $id = false ) {
global $CFG ;
// Accessing $CFG directly as using \core_search::is_global_search_enabled would
// result in an extra included file for each site, even the ones where global search
// is disabled.
2016-02-23 10:44:20 +08:00
if ( empty ( $CFG -> enableglobalsearch ) || ! has_capability ( 'moodle/search:query' , context_system :: instance ())) {
2016-02-19 14:46:37 +08:00
return '' ;
}
2020-08-31 08:11:30 +00:00
$data = [
'action' => new moodle_url ( '/search/index.php' ),
'hiddenfields' => ( object ) [ 'name' => 'context' , 'value' => $this -> page -> context -> id ],
'inputname' => 'q' ,
'searchstring' => get_string ( 'search' ),
];
return $this -> render_from_template ( 'core/search_input_navbar' , $data );
2016-02-19 14:46:37 +08:00
}
2016-06-16 07:47:53 +00:00
/**
2016-09-30 07:41:32 +00:00
* Allow plugins to provide some content to be rendered in the navbar .
* The plugin must define a PLUGIN_render_navbar_output function that returns
* the HTML they wish to add to the navbar .
2016-06-16 07:47:53 +00:00
*
2016-09-30 07:41:32 +00:00
* @ return string HTML for the navbar
2016-06-16 07:47:53 +00:00
*/
2016-09-30 07:41:32 +00:00
public function navbar_plugin_output () {
$output = '' ;
2016-07-07 05:12:41 +00:00
2018-10-16 11:27:50 +08:00
// Give subsystems an opportunity to inject extra html content. The callback
// must always return a string containing valid html.
foreach ( \core_component :: get_core_subsystems () as $name => $path ) {
if ( $path ) {
$output .= component_callback ( $name , 'render_navbar_output' , [ $this ], '' );
}
}
2016-09-30 07:41:32 +00:00
if ( $pluginsfunction = get_plugins_with_function ( 'render_navbar_output' )) {
foreach ( $pluginsfunction as $plugintype => $plugins ) {
foreach ( $plugins as $pluginfunction ) {
$output .= $pluginfunction ( $this );
}
}
2016-06-16 07:47:53 +00:00
}
2016-07-08 07:45:04 +00:00
2016-09-30 07:41:32 +00:00
return $output ;
2016-07-08 07:45:04 +00:00
}
2014-10-03 15:12:37 +08:00
/**
* Construct a user menu , returning HTML that can be echoed out by a
* layout file .
*
* @ param stdClass $user A user object , usually $USER .
* @ param bool $withlinks true if a dropdown should be built .
* @ return string HTML fragment .
*/
2014-10-03 17:02:02 +08:00
public function user_menu ( $user = null , $withlinks = null ) {
2014-10-03 15:12:37 +08:00
global $USER , $CFG ;
2014-10-03 17:02:02 +08:00
require_once ( $CFG -> dirroot . '/user/lib.php' );
2014-10-03 15:12:37 +08:00
if ( is_null ( $user )) {
$user = $USER ;
}
// Note: this behaviour is intended to match that of core_renderer::login_info,
// but should not be considered to be good practice; layout options are
// intended to be theme-specific. Please don't copy this snippet anywhere else.
if ( is_null ( $withlinks )) {
$withlinks = empty ( $this -> page -> layout_options [ 'nologinlinks' ]);
}
2014-10-03 17:02:02 +08:00
// Add a class for when $withlinks is false.
$usermenuclasses = 'usermenu' ;
if ( ! $withlinks ) {
$usermenuclasses .= ' withoutlinks' ;
}
2014-10-03 15:12:37 +08:00
$returnstr = " " ;
// If during initial install, return the empty return string.
if ( during_initial_install ()) {
return $returnstr ;
}
2014-12-18 08:40:57 +08:00
$loginpage = $this -> is_login_page ();
2014-10-03 15:12:37 +08:00
$loginurl = get_login_url ();
2014-10-03 17:02:02 +08:00
2021-03-31 12:41:23 +08:00
// Get some navigation opts.
$opts = user_get_user_navigation_info ( $user , $this -> page );
2014-10-03 15:12:37 +08:00
2021-03-31 12:41:23 +08:00
if ( ! empty ( $opts -> unauthenticateduser )) {
$returnstr = get_string ( $opts -> unauthenticateduser [ 'content' ], 'moodle' );
// If not logged in, show the typical not-logged-in string.
if ( ! $loginpage && ( ! $opts -> unauthenticateduser [ 'guest' ] || $withlinks )) {
$returnstr .= " (<a href= \" $loginurl\ " > " . get_string('login') . '</a>)';
2014-10-03 15:12:37 +08:00
}
2014-10-03 17:02:02 +08:00
return html_writer :: div (
html_writer :: span (
$returnstr ,
2021-05-31 13:03:19 +02:00
'login nav-link'
2014-10-03 17:02:02 +08:00
),
$usermenuclasses
2014-10-03 15:12:37 +08:00
);
}
$avatarclasses = " avatars " ;
$avatarcontents = html_writer :: span ( $opts -> metadata [ 'useravatar' ], 'avatar current' );
$usertextcontents = $opts -> metadata [ 'userfullname' ];
// Other user.
if ( ! empty ( $opts -> metadata [ 'asotheruser' ])) {
$avatarcontents .= html_writer :: span (
$opts -> metadata [ 'realuseravatar' ],
'avatar realuser'
);
$usertextcontents = $opts -> metadata [ 'realuserfullname' ];
$usertextcontents .= html_writer :: tag (
'span' ,
get_string (
'loggedinas' ,
'moodle' ,
html_writer :: span (
$opts -> metadata [ 'userfullname' ],
'value'
)
),
array ( 'class' => 'meta viewingas' )
);
}
// Role.
if ( ! empty ( $opts -> metadata [ 'asotherrole' ])) {
$role = core_text :: strtolower ( preg_replace ( '#[ ]+#' , '-' , trim ( $opts -> metadata [ 'rolename' ])));
$usertextcontents .= html_writer :: span (
$opts -> metadata [ 'rolename' ],
'meta role role-' . $role
);
}
// User login failures.
if ( ! empty ( $opts -> metadata [ 'userloginfail' ])) {
$usertextcontents .= html_writer :: span (
$opts -> metadata [ 'userloginfail' ],
'meta loginfailures'
);
}
// MNet.
if ( ! empty ( $opts -> metadata [ 'asmnetuser' ])) {
$mnet = strtolower ( preg_replace ( '#[ ]+#' , '-' , trim ( $opts -> metadata [ 'mnetidprovidername' ])));
$usertextcontents .= html_writer :: span (
$opts -> metadata [ 'mnetidprovidername' ],
'meta mnet mnet-' . $mnet
);
}
$returnstr .= html_writer :: span (
2018-04-17 15:13:31 +02:00
html_writer :: span ( $usertextcontents , 'usertext mr-1' ) .
2014-10-03 17:02:02 +08:00
html_writer :: span ( $avatarcontents , $avatarclasses ),
2014-10-03 15:12:37 +08:00
'userbutton'
);
// Create a divider (well, a filler).
$divider = new action_menu_filler ();
$divider -> primary = false ;
$am = new action_menu ();
2014-10-10 17:17:54 +08:00
$am -> set_menu_trigger (
2021-05-31 13:03:19 +02:00
$returnstr ,
'nav-link'
2014-10-10 17:17:54 +08:00
);
2018-09-03 15:08:38 +08:00
$am -> set_action_label ( get_string ( 'usermenu' ));
2014-10-03 15:12:37 +08:00
$am -> set_alignment ( action_menu :: TR , action_menu :: BR );
2014-11-03 13:44:58 +13:00
$am -> set_nowrap_on_items ();
2014-10-03 15:12:37 +08:00
if ( $withlinks ) {
$navitemcount = count ( $opts -> navitems );
$idx = 0 ;
foreach ( $opts -> navitems as $key => $value ) {
2014-12-15 09:05:35 +08:00
switch ( $value -> itemtype ) {
case 'divider' :
// If the nav item is a divider, add one and skip link processing.
$am -> add ( $divider );
break ;
case 'invalid' :
// Silently skip invalid entries (should we post a notification?).
break ;
case 'link' :
// Process this as a link item.
$pix = null ;
if ( isset ( $value -> pix ) && ! empty ( $value -> pix )) {
2019-01-10 10:22:48 +08:00
$pix = new pix_icon ( $value -> pix , '' , null , array ( 'class' => 'iconsmall' ));
2014-12-15 09:05:35 +08:00
} else if ( isset ( $value -> imgsrc ) && ! empty ( $value -> imgsrc )) {
$value -> title = html_writer :: img (
$value -> imgsrc ,
$value -> title ,
array ( 'class' => 'iconsmall' )
) . $value -> title ;
}
2016-03-22 08:26:59 +08:00
2014-12-15 09:05:35 +08:00
$al = new action_menu_link_secondary (
$value -> url ,
$pix ,
$value -> title ,
array ( 'class' => 'icon' )
);
2016-03-22 08:26:59 +08:00
if ( ! empty ( $value -> titleidentifier )) {
$al -> attributes [ 'data-title' ] = $value -> titleidentifier ;
}
2014-12-15 09:05:35 +08:00
$am -> add ( $al );
break ;
2014-10-03 15:12:37 +08:00
}
$idx ++ ;
2015-01-28 12:00:11 +08:00
// Add dividers after the first item and before the last item.
if ( $idx == 1 || $idx == $navitemcount - 1 ) {
$am -> add ( $divider );
}
2014-10-03 15:12:37 +08:00
}
}
return html_writer :: div (
$this -> render ( $am ),
$usermenuclasses
);
}
2020-03-02 16:13:33 +11:00
/**
* Secure layout login info .
*
* @ return string
*/
public function secure_layout_login_info () {
if ( get_config ( 'core' , 'logininfoinsecurelayout' )) {
return $this -> login_info ( false );
} else {
return '' ;
}
}
/**
* Returns the language menu in the secure layout .
*
* No custom menu items are passed though , such that it will render only the language selection .
*
* @ return string
*/
public function secure_layout_language_menu () {
if ( get_config ( 'core' , 'langmenuinsecurelayout' )) {
$custommenu = new custom_menu ( '' , current_language ());
return $this -> render_custom_menu ( $custommenu );
} else {
return '' ;
}
}
2009-08-28 08:47:31 +00:00
/**
2019-02-13 16:29:22 +08:00
* This renders the navbar .
* Uses bootstrap compatible html .
2009-08-28 08:47:31 +00:00
*/
public function navbar () {
2019-02-13 16:29:22 +08:00
return $this -> render_from_template ( 'core/navbar' , $this -> page -> navbar );
2010-04-19 06:30:30 +00:00
}
2015-10-12 17:11:28 +11:00
/**
* Renders a breadcrumb navigation node object .
*
* @ param breadcrumb_navigation_node $item The navigation node to render .
* @ return string HTML fragment
*/
protected function render_breadcrumb_navigation_node ( breadcrumb_navigation_node $item ) {
if ( $item -> action instanceof moodle_url ) {
$content = $item -> get_content ();
$title = $item -> get_title ();
$attributes = array ();
$attributes [ 'itemprop' ] = 'url' ;
if ( $title !== '' ) {
$attributes [ 'title' ] = $title ;
}
if ( $item -> hidden ) {
$attributes [ 'class' ] = 'dimmed_text' ;
}
2018-08-10 16:54:36 +08:00
if ( $item -> is_last ()) {
$attributes [ 'aria-current' ] = 'page' ;
}
2015-10-12 17:11:28 +11:00
$content = html_writer :: tag ( 'span' , $content , array ( 'itemprop' => 'title' ));
$content = html_writer :: link ( $item -> action , $content , $attributes );
$attributes = array ();
$attributes [ 'itemscope' ] = '' ;
$attributes [ 'itemtype' ] = 'http://data-vocabulary.org/Breadcrumb' ;
$content = html_writer :: tag ( 'span' , $content , $attributes );
} else {
$content = $this -> render_navigation_node ( $item );
}
return $content ;
}
2012-01-05 13:28:08 +13:00
/**
* Renders a navigation node object .
*
* @ param navigation_node $item The navigation node to render .
* @ return string HTML fragment
*/
2010-04-19 06:30:30 +00:00
protected function render_navigation_node ( navigation_node $item ) {
$content = $item -> get_content ();
$title = $item -> get_title ();
2010-04-23 04:21:33 +00:00
if ( $item -> icon instanceof renderable && ! $item -> hideicon ) {
2010-04-19 06:30:30 +00:00
$icon = $this -> render ( $item -> icon );
2010-06-03 08:27:03 +00:00
$content = $icon . $content ; // use CSS for spacing of icons
2010-04-19 06:30:30 +00:00
}
if ( $item -> helpbutton !== null ) {
2011-05-10 16:44:18 +08:00
$content = trim ( $item -> helpbutton ) . html_writer :: tag ( 'span' , $content , array ( 'class' => 'clearhelpbutton' , 'tabindex' => '0' ));
2010-04-19 06:30:30 +00:00
}
if ( $content === '' ) {
2010-05-11 15:47:12 +00:00
return '' ;
2010-04-19 06:30:30 +00:00
}
if ( $item -> action instanceof action_link ) {
$link = $item -> action ;
if ( $item -> hidden ) {
$link -> add_class ( 'dimmed' );
}
2012-04-11 10:56:49 +12:00
if ( ! empty ( $content )) {
// Providing there is content we will use that for the link content.
$link -> text = $content ;
}
2011-02-11 16:26:20 +08:00
$content = $this -> render ( $link );
2010-04-19 06:30:30 +00:00
} else if ( $item -> action instanceof moodle_url ) {
$attributes = array ();
if ( $title !== '' ) {
$attributes [ 'title' ] = $title ;
}
if ( $item -> hidden ) {
$attributes [ 'class' ] = 'dimmed_text' ;
}
$content = html_writer :: link ( $item -> action , $content , $attributes );
} else if ( is_string ( $item -> action ) || empty ( $item -> action )) {
2011-05-10 16:44:18 +08:00
$attributes = array ( 'tabindex' => '0' ); //add tab support to span but still maintain character stream sequence.
2010-04-19 06:30:30 +00:00
if ( $title !== '' ) {
$attributes [ 'title' ] = $title ;
}
if ( $item -> hidden ) {
$attributes [ 'class' ] = 'dimmed_text' ;
}
$content = html_writer :: tag ( 'span' , $content , $attributes );
}
return $content ;
2009-08-28 08:47:31 +00:00
}
2009-12-23 17:44:17 +00:00
/**
* Accessibility : Right arrow - like character is
* used in the breadcrumb trail , course navigation menu
* ( previous / next activity ), calendar , and search forum block .
* If the theme does not set characters , appropriate defaults
* are set automatically . Please DO NOT
* use & lt ; & gt ; & raquo ; - these are confusing for blind users .
2012-01-05 13:28:08 +13:00
*
2009-12-23 17:44:17 +00:00
* @ return string
*/
public function rarrow () {
return $this -> page -> theme -> rarrow ;
}
/**
2012-09-12 16:13:43 +02:00
* Accessibility : Left arrow - like character is
2009-12-23 17:44:17 +00:00
* used in the breadcrumb trail , course navigation menu
* ( previous / next activity ), calendar , and search forum block .
* If the theme does not set characters , appropriate defaults
* are set automatically . Please DO NOT
* use & lt ; & gt ; & raquo ; - these are confusing for blind users .
2012-01-05 13:28:08 +13:00
*
2009-12-23 17:44:17 +00:00
* @ return string
*/
public function larrow () {
return $this -> page -> theme -> larrow ;
}
2009-12-23 17:51:41 +00:00
2014-10-28 09:29:28 +00:00
/**
* Accessibility : Up arrow - like character is used in
* the book heirarchical navigation .
* If the theme does not set characters , appropriate defaults
* are set automatically . Please DO NOT
* use ^ - this is confusing for blind users .
*
* @ return string
*/
public function uarrow () {
return $this -> page -> theme -> uarrow ;
}
2016-07-19 15:06:00 +08:00
/**
* Accessibility : Down arrow - like character .
* If the theme does not set characters , appropriate defaults
* are set automatically .
*
* @ return string
*/
public function darrow () {
return $this -> page -> theme -> darrow ;
}
2010-05-10 05:24:00 +00:00
/**
* Returns the custom menu if one has been set
*
2010-05-22 20:16:12 +00:00
* A custom menu can be configured by browsing to
2010-05-10 05:24:00 +00:00
* Settings : Administration > Appearance > Themes > Theme settings
* and then configuring the custommenu config setting as described .
2010-05-19 08:05:36 +00:00
*
2013-04-15 11:05:46 +10:00
* Theme developers : DO NOT OVERRIDE ! Please override function
* { @ link core_renderer :: render_custom_menu ()} instead .
*
2012-01-06 12:48:01 +13:00
* @ param string $custommenuitems - custom menuitems set by theme instead of global theme settings
2010-05-10 05:24:00 +00:00
* @ return string
*/
2012-01-06 12:48:01 +13:00
public function custom_menu ( $custommenuitems = '' ) {
2010-05-11 07:30:04 +00:00
global $CFG ;
2019-02-13 16:29:22 +08:00
2012-01-06 12:48:01 +13:00
if ( empty ( $custommenuitems ) && ! empty ( $CFG -> custommenuitems )) {
$custommenuitems = $CFG -> custommenuitems ;
}
2019-02-13 16:29:22 +08:00
$custommenu = new custom_menu ( $custommenuitems , current_language ());
return $this -> render_custom_menu ( $custommenu );
}
/**
* We want to show the custom menus as a list of links in the footer on small screens .
* Just return the menu object exported so we can render it differently .
*/
public function custom_menu_flat () {
global $CFG ;
$custommenuitems = '' ;
if ( empty ( $custommenuitems ) && ! empty ( $CFG -> custommenuitems )) {
$custommenuitems = $CFG -> custommenuitems ;
2010-05-11 07:30:04 +00:00
}
2012-01-06 12:48:01 +13:00
$custommenu = new custom_menu ( $custommenuitems , current_language ());
2019-02-13 16:29:22 +08:00
$langs = get_string_manager () -> get_list_of_translations ();
$haslangmenu = $this -> lang_menu () != '' ;
if ( $haslangmenu ) {
$strlang = get_string ( 'language' );
$currentlang = current_language ();
if ( isset ( $langs [ $currentlang ])) {
$currentlang = $langs [ $currentlang ];
} else {
$currentlang = $strlang ;
}
$this -> language = $custommenu -> add ( $currentlang , new moodle_url ( '#' ), $strlang , 10000 );
foreach ( $langs as $langtype => $langname ) {
$this -> language -> add ( $langname , new moodle_url ( $this -> page -> url , array ( 'lang' => $langtype )), $langname );
}
}
return $custommenu -> export_for_template ( $this );
2010-05-10 05:24:00 +00:00
}
/**
* Renders a custom menu object ( located in outputcomponents . php )
*
* The custom menu this method produces makes use of the YUI3 menunav widget
* and requires very specific html elements and classes .
*
* @ staticvar int $menucount
* @ param custom_menu $menu
* @ return string
*/
protected function render_custom_menu ( custom_menu $menu ) {
2019-02-13 16:29:22 +08:00
global $CFG ;
$langs = get_string_manager () -> get_list_of_translations ();
$haslangmenu = $this -> lang_menu () != '' ;
if ( ! $menu -> has_children () && ! $haslangmenu ) {
2010-05-10 05:24:00 +00:00
return '' ;
}
2019-02-13 16:29:22 +08:00
if ( $haslangmenu ) {
$strlang = get_string ( 'language' );
$currentlang = current_language ();
if ( isset ( $langs [ $currentlang ])) {
$currentlang = $langs [ $currentlang ];
} else {
$currentlang = $strlang ;
}
$this -> language = $menu -> add ( $currentlang , new moodle_url ( '#' ), $strlang , 10000 );
foreach ( $langs as $langtype => $langname ) {
$this -> language -> add ( $langname , new moodle_url ( $this -> page -> url , array ( 'lang' => $langtype )), $langname );
}
}
$content = '' ;
2010-05-10 05:24:00 +00:00
foreach ( $menu -> get_children () as $item ) {
2019-02-13 16:29:22 +08:00
$context = $item -> export_for_template ( $this );
$content .= $this -> render_from_template ( 'core/custom_menu_item' , $context );
2010-05-10 05:24:00 +00:00
}
2019-02-13 16:29:22 +08:00
2010-05-10 05:24:00 +00:00
return $content ;
}
/**
* Renders a custom menu node as part of a submenu
*
* The custom menu this method produces makes use of the YUI3 menunav widget
* and requires very specific html elements and classes .
*
2012-01-05 13:28:08 +13:00
* @ see core : renderer :: render_custom_menu ()
2010-05-10 05:24:00 +00:00
*
* @ staticvar int $submenucount
* @ param custom_menu_item $menunode
* @ return string
*/
protected function render_custom_menu_item ( custom_menu_item $menunode ) {
// Required to ensure we get unique trackable id's
static $submenucount = 0 ;
if ( $menunode -> has_children ()) {
// If the child has menus render it as a sub menu
$submenucount ++ ;
$content = html_writer :: start_tag ( 'li' );
if ( $menunode -> get_url () !== null ) {
$url = $menunode -> get_url ();
} else {
$url = '#cm_submenu_' . $submenucount ;
}
$content .= html_writer :: link ( $url , $menunode -> get_text (), array ( 'class' => 'yui3-menu-label' , 'title' => $menunode -> get_title ()));
$content .= html_writer :: start_tag ( 'div' , array ( 'id' => 'cm_submenu_' . $submenucount , 'class' => 'yui3-menu custom_menu_submenu' ));
$content .= html_writer :: start_tag ( 'div' , array ( 'class' => 'yui3-menu-content' ));
$content .= html_writer :: start_tag ( 'ul' );
foreach ( $menunode -> get_children () as $menunode ) {
$content .= $this -> render_custom_menu_item ( $menunode );
}
$content .= html_writer :: end_tag ( 'ul' );
$content .= html_writer :: end_tag ( 'div' );
$content .= html_writer :: end_tag ( 'div' );
$content .= html_writer :: end_tag ( 'li' );
} else {
2014-06-18 15:45:40 +08:00
// The node doesn't have children so produce a final menuitem.
// Also, if the node's text matches '####', add a class so we can treat it as a divider.
$content = '' ;
if ( preg_match ( " /^#+ $ / " , $menunode -> get_text ())) {
// This is a divider.
$content = html_writer :: start_tag ( 'li' , array ( 'class' => 'yui3-menuitem divider' ));
2010-05-10 05:24:00 +00:00
} else {
2014-06-18 15:45:40 +08:00
$content = html_writer :: start_tag (
'li' ,
array (
'class' => 'yui3-menuitem'
)
);
if ( $menunode -> get_url () !== null ) {
$url = $menunode -> get_url ();
} else {
$url = '#' ;
}
$content .= html_writer :: link (
$url ,
$menunode -> get_text (),
array ( 'class' => 'yui3-menuitem-content' , 'title' => $menunode -> get_title ())
);
2010-05-10 05:24:00 +00:00
}
$content .= html_writer :: end_tag ( 'li' );
}
// Return the sub menu
return $content ;
}
2011-05-31 14:25:52 +08:00
2011-05-31 14:28:03 +08:00
/**
2011-05-31 14:25:52 +08:00
* Renders theme links for switching between default and other themes .
2011-05-31 14:28:03 +08:00
*
* @ return string
2011-05-31 14:25:52 +08:00
*/
protected function theme_switch_links () {
2013-07-30 17:00:54 +12:00
$actualdevice = core_useragent :: get_device_type ();
2011-05-31 14:28:03 +08:00
$currentdevice = $this -> page -> devicetypeinuse ;
$switched = ( $actualdevice != $currentdevice );
2011-05-31 14:25:52 +08:00
2011-05-31 14:28:03 +08:00
if ( ! $switched && $currentdevice == 'default' && $actualdevice == 'default' ) {
// The user is using the a default device and hasn't switched so don't shown the switch
// device links.
2011-05-31 14:25:52 +08:00
return '' ;
}
if ( $switched ) {
$linktext = get_string ( 'switchdevicerecommended' );
2011-05-31 14:28:03 +08:00
$devicetype = $actualdevice ;
2011-05-31 14:25:52 +08:00
} else {
$linktext = get_string ( 'switchdevicedefault' );
2011-05-31 14:28:03 +08:00
$devicetype = 'default' ;
2011-05-31 14:25:52 +08:00
}
2011-05-31 14:28:03 +08:00
$linkurl = new moodle_url ( '/theme/switchdevice.php' , array ( 'url' => $this -> page -> url , 'device' => $devicetype , 'sesskey' => sesskey ()));
2011-05-31 14:25:52 +08:00
2011-05-31 14:28:03 +08:00
$content = html_writer :: start_tag ( 'div' , array ( 'id' => 'theme_switch_link' ));
2015-01-23 16:02:49 +00:00
$content .= html_writer :: link ( $linkurl , $linktext , array ( 'rel' => 'nofollow' ));
2011-05-31 14:25:52 +08:00
$content .= html_writer :: end_tag ( 'div' );
return $content ;
}
2013-04-11 19:06:52 +10:00
/**
* Renders tabs
*
* This function replaces print_tabs () used before Moodle 2.5 but with slightly different arguments
*
2013-04-15 11:05:46 +10:00
* Theme developers : In order to change how tabs are displayed please override functions
* { @ link core_renderer :: render_tabtree ()} and / or { @ link core_renderer :: render_tabobject ()}
*
2013-04-11 19:06:52 +10:00
* @ param array $tabs array of tabs , each of them may have it ' s own -> subtree
* @ param string | null $selected which tab to mark as selected , all parent tabs will
* automatically be marked as activated
* @ param array | string | null $inactive list of ids of inactive tabs , regardless of
* their level . Note that you can as weel specify tabobject :: $inactive for separate instances
* @ return string
*/
2013-04-15 11:05:46 +10:00
public final function tabtree ( $tabs , $selected = null , $inactive = null ) {
2013-04-11 19:06:52 +10:00
return $this -> render ( new tabtree ( $tabs , $selected , $inactive ));
}
/**
* Renders tabtree
*
* @ param tabtree $tabtree
* @ return string
*/
protected function render_tabtree ( tabtree $tabtree ) {
if ( empty ( $tabtree -> subtree )) {
return '' ;
}
2019-02-13 16:29:22 +08:00
$data = $tabtree -> export_for_template ( $this );
return $this -> render_from_template ( 'core/tabtree' , $data );
2013-04-11 19:06:52 +10:00
}
/**
* Renders tabobject ( part of tabtree )
*
* This function is called from { @ link core_renderer :: render_tabtree ()}
* and also it calls itself when printing the $tabobject subtree recursively .
*
* Property $tabobject -> level indicates the number of row of tabs .
*
* @ param tabobject $tabobject
* @ return string HTML fragment
*/
protected function render_tabobject ( tabobject $tabobject ) {
$str = '' ;
// Print name of the current tab.
if ( $tabobject instanceof tabtree ) {
// No name for tabtree root.
} else if ( $tabobject -> inactive || $tabobject -> activated || ( $tabobject -> selected && ! $tabobject -> linkedwhenselected )) {
// Tab name without a link. The <a> tag is used for styling.
2013-11-06 10:11:31 +08:00
$str .= html_writer :: tag ( 'a' , html_writer :: span ( $tabobject -> text ), array ( 'class' => 'nolink moodle-has-zindex' ));
2013-04-11 19:06:52 +10:00
} else {
// Tab name with a link.
if ( ! ( $tabobject -> link instanceof moodle_url )) {
// backward compartibility when link was passed as quoted string
$str .= " <a href= \" $tabobject->link\ " title = \ " $tabobject->title\ " >< span > $tabobject -> text </ span ></ a > " ;
} else {
$str .= html_writer :: link ( $tabobject -> link , html_writer :: span ( $tabobject -> text ), array ( 'title' => $tabobject -> title ));
}
}
if ( empty ( $tabobject -> subtree )) {
if ( $tabobject -> selected ) {
$str .= html_writer :: tag ( 'div' , ' ' , array ( 'class' => 'tabrow' . ( $tabobject -> level + 1 ) . ' empty' ));
}
return $str ;
}
2015-11-09 23:22:53 +00:00
// Print subtree.
if ( $tabobject -> level == 0 || $tabobject -> selected || $tabobject -> activated ) {
$str .= html_writer :: start_tag ( 'ul' , array ( 'class' => 'tabrow' . $tabobject -> level ));
$cnt = 0 ;
foreach ( $tabobject -> subtree as $tab ) {
$liclass = '' ;
if ( ! $cnt ) {
$liclass .= ' first' ;
}
if ( $cnt == count ( $tabobject -> subtree ) - 1 ) {
$liclass .= ' last' ;
}
if (( empty ( $tab -> subtree )) && ( ! empty ( $tab -> selected ))) {
$liclass .= ' onerow' ;
}
2013-04-11 19:06:52 +10:00
2015-11-09 23:22:53 +00:00
if ( $tab -> selected ) {
$liclass .= ' here selected' ;
} else if ( $tab -> activated ) {
$liclass .= ' here active' ;
}
2013-04-11 19:06:52 +10:00
2015-11-09 23:22:53 +00:00
// This will recursively call function render_tabobject() for each item in subtree.
$str .= html_writer :: tag ( 'li' , $this -> render ( $tab ), array ( 'class' => trim ( $liclass )));
$cnt ++ ;
}
$str .= html_writer :: end_tag ( 'ul' );
2013-04-11 19:06:52 +10:00
}
return $str ;
}
2013-06-07 10:32:52 +12:00
/**
* Get the HTML for blocks in the given region .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:32:52 +12:00
* @ param string $region The region to get HTML for .
2020-12-09 12:02:20 +11:00
* @ param array $classes Wrapping tag classes .
* @ param string $tag Wrapping tag .
* @ param boolean $fakeblocksonly Include fake blocks only .
2013-06-07 10:32:52 +12:00
* @ return string HTML .
*/
2020-12-09 12:02:20 +11:00
public function blocks ( $region , $classes = array (), $tag = 'aside' , $fakeblocksonly = false ) {
2013-06-10 09:10:06 +12:00
$displayregion = $this -> page -> apply_theme_region_manipulations ( $region );
2013-06-07 10:32:52 +12:00
$classes = ( array ) $classes ;
$classes [] = 'block-region' ;
$attributes = array (
2013-06-10 09:10:06 +12:00
'id' => 'block-region-' . preg_replace ( '#[^a-zA-Z0-9_\-]+#' , '-' , $displayregion ),
2013-06-07 10:32:52 +12:00
'class' => join ( ' ' , $classes ),
2013-06-10 09:10:06 +12:00
'data-blockregion' => $displayregion ,
2013-06-07 10:32:52 +12:00
'data-droptarget' => '1'
);
2013-11-17 13:33:30 +00:00
if ( $this -> page -> blocks -> region_has_content ( $displayregion , $this )) {
2020-12-09 12:02:20 +11:00
$content = $this -> blocks_for_region ( $displayregion , $fakeblocksonly );
2013-11-02 00:43:48 +00:00
} else {
$content = '' ;
}
return html_writer :: tag ( $tag , $content , $attributes );
2013-06-07 10:32:52 +12:00
}
2013-06-07 10:34:02 +12:00
2014-02-24 10:38:17 +13:00
/**
* Renders a custom block region .
*
* Use this method if you want to add an additional block region to the content of the page .
* Please note this should only be used in special situations .
* We want to leave the theme is control where ever possible !
*
* This method must use the same method that the theme uses within its layout file .
* As such it asks the theme what method it is using .
* It can be one of two values , blocks or blocks_for_region ( deprecated ) .
*
* @ param string $regionname The name of the custom region to add .
* @ return string HTML for the block region .
*/
public function custom_block_region ( $regionname ) {
if ( $this -> page -> theme -> get_block_render_method () === 'blocks' ) {
return $this -> blocks ( $regionname );
} else {
return $this -> blocks_for_region ( $regionname );
}
}
2013-06-07 10:34:02 +12:00
/**
* Returns the CSS classes to apply to the body tag .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:34:02 +12:00
* @ param array $additionalclasses Any additional classes to apply .
* @ return string
*/
public function body_css_classes ( array $additionalclasses = array ()) {
2019-02-13 16:29:22 +08:00
return $this -> page -> bodyclasses . ' ' . implode ( ' ' , $additionalclasses );
2013-06-07 10:34:02 +12:00
}
/**
* The ID attribute to apply to the body tag .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:34:02 +12:00
* @ return string
*/
public function body_id () {
return $this -> page -> bodyid ;
}
/**
* Returns HTML attributes to use within the body tag . This includes an ID and classes .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:34:02 +12:00
* @ param string | array $additionalclasses Any additional classes to give the body tag ,
* @ return string
*/
public function body_attributes ( $additionalclasses = array ()) {
if ( ! is_array ( $additionalclasses )) {
$additionalclasses = explode ( ' ' , $additionalclasses );
}
return ' id="' . $this -> body_id () . '" class="' . $this -> body_css_classes ( $additionalclasses ) . '"' ;
}
2013-06-07 10:35:38 +12:00
/**
* Gets HTML for the page heading .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:35:38 +12:00
* @ param string $tag The tag to encase the heading in . h1 by default .
* @ return string HTML .
*/
public function page_heading ( $tag = 'h1' ) {
return html_writer :: tag ( $tag , $this -> page -> heading );
}
/**
* Gets the HTML for the page heading button .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:35:38 +12:00
* @ return string HTML .
*/
public function page_heading_button () {
return $this -> page -> button ;
}
/**
* Returns the Moodle docs link to use for this page .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:35:38 +12:00
* @ param string $text
* @ return string
*/
public function page_doc_link ( $text = null ) {
if ( $text === null ) {
$text = get_string ( 'moodledocslink' );
}
$path = page_get_doc_link_path ( $this -> page );
if ( ! $path ) {
return '' ;
}
return $this -> doc_link ( $path , $text );
}
/**
* Returns the page heading menu .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:35:38 +12:00
* @ return string HTML .
*/
public function page_heading_menu () {
return $this -> page -> headingmenu ;
}
/**
* Returns the title to use on the page .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2013-06-07 10:35:38 +12:00
* @ return string
*/
public function page_title () {
return $this -> page -> title ;
}
/**
2019-07-30 16:01:38 +01:00
* Returns the moodle_url for the favicon .
2013-06-07 10:35:38 +12:00
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.5 . 1 2.6
2019-07-30 16:01:38 +01:00
* @ return moodle_url The moodle_url for the favicon
2013-06-07 10:35:38 +12:00
*/
public function favicon () {
2017-01-19 16:20:27 +08:00
return $this -> image_url ( 'favicon' , 'theme' );
2013-06-07 10:35:38 +12:00
}
2015-01-30 18:24:18 +08:00
/**
* Renders preferences groups .
*
* @ param preferences_groups $renderable The renderable
* @ return string The output .
*/
public function render_preferences_groups ( preferences_groups $renderable ) {
2019-02-13 16:29:22 +08:00
return $this -> render_from_template ( 'core/preferences_groups' , $renderable );
2015-01-30 18:24:18 +08:00
}
/**
* Renders preferences group .
*
* @ param preferences_group $renderable The renderable
* @ return string The output .
*/
public function render_preferences_group ( preferences_group $renderable ) {
$html = '' ;
2019-03-07 09:40:47 +08:00
$html .= html_writer :: start_tag ( 'div' , array ( 'class' => 'col-sm-4 preferences-group' ));
2015-01-30 18:24:18 +08:00
$html .= $this -> heading ( $renderable -> title , 3 );
$html .= html_writer :: start_tag ( 'ul' );
foreach ( $renderable -> nodes as $node ) {
if ( $node -> has_children ()) {
debugging ( 'Preferences nodes do not support children' , DEBUG_DEVELOPER );
}
$html .= html_writer :: tag ( 'li' , $this -> render ( $node ));
}
$html .= html_writer :: end_tag ( 'ul' );
$html .= html_writer :: end_tag ( 'div' );
return $html ;
}
2015-02-17 13:35:02 +08:00
2015-04-08 12:45:46 +08:00
public function context_header ( $headerinfo = null , $headinglevel = 1 ) {
2019-02-13 16:29:22 +08:00
global $DB , $USER , $CFG , $SITE ;
2017-03-09 15:50:24 +08:00
require_once ( $CFG -> dirroot . '/user/lib.php' );
2015-02-17 13:35:02 +08:00
$context = $this -> page -> context ;
2017-01-09 12:09:21 +08:00
$heading = null ;
$imagedata = null ;
$subheader = null ;
$userbuttons = null ;
2019-02-13 16:29:22 +08:00
2015-04-08 12:45:46 +08:00
// Make sure to use the heading if it has been set.
if ( isset ( $headerinfo [ 'heading' ])) {
$heading = $headerinfo [ 'heading' ];
2021-01-21 22:34:40 +11:00
} else {
$heading = $this -> page -> heading ;
2015-04-08 12:45:46 +08:00
}
2018-10-23 09:41:26 +02:00
2015-04-08 12:45:46 +08:00
// The user context currently has images and buttons. Other contexts may follow.
if ( isset ( $headerinfo [ 'user' ]) || $context -> contextlevel == CONTEXT_USER ) {
if ( isset ( $headerinfo [ 'user' ])) {
$user = $headerinfo [ 'user' ];
} else {
// Look up the user information if it is not supplied.
$user = $DB -> get_record ( 'user' , array ( 'id' => $context -> instanceid ));
}
2017-01-09 12:09:21 +08:00
2015-04-08 12:45:46 +08:00
// If the user context is set, then use that for capability checks.
if ( isset ( $headerinfo [ 'usercontext' ])) {
$context = $headerinfo [ 'usercontext' ];
}
2017-01-09 12:09:21 +08:00
// Only provide user information if the user is the current user, or a user which the current user can view.
2017-08-15 15:37:33 +08:00
// When checking user_can_view_profile(), either:
// If the page context is course, check the course context (from the page object) or;
// If page context is NOT course, then check across all courses.
$course = ( $this -> page -> context -> contextlevel == CONTEXT_COURSE ) ? $this -> page -> course : null ;
2015-04-08 12:45:46 +08:00
2017-08-15 15:37:33 +08:00
if ( user_can_view_profile ( $user , $course )) {
2017-01-09 12:09:21 +08:00
// Use the user's full name if the heading isn't set.
2021-01-21 22:34:40 +11:00
if ( empty ( $heading )) {
2017-01-09 12:09:21 +08:00
$heading = fullname ( $user );
}
$imagedata = $this -> user_picture ( $user , array ( 'size' => 100 ));
// Check to see if we should be displaying a message button.
2019-02-22 15:12:41 +01:00
if ( ! empty ( $CFG -> messaging ) && has_capability ( 'moodle/site:sendmessage' , $context )) {
2017-01-09 12:09:21 +08:00
$userbuttons = array (
'messages' => array (
'buttontype' => 'message' ,
'title' => get_string ( 'message' , 'message' ),
'url' => new moodle_url ( '/message/index.php' , array ( 'id' => $user -> id )),
'image' => 'message' ,
2019-02-13 19:02:16 +08:00
'linkattributes' => \core_message\helper :: messageuser_link_params ( $user -> id ),
2017-01-09 12:09:21 +08:00
'page' => $this -> page
2019-02-22 15:12:41 +01:00
)
2017-01-09 12:09:21 +08:00
);
2016-06-21 03:32:06 +00:00
2019-02-22 15:12:41 +01:00
if ( $USER -> id != $user -> id ) {
$iscontact = \core_message\api :: is_contact ( $USER -> id , $user -> id );
$contacttitle = $iscontact ? 'removefromyourcontacts' : 'addtoyourcontacts' ;
$contacturlaction = $iscontact ? 'removecontact' : 'addcontact' ;
$contactimage = $iscontact ? 'removecontact' : 'addcontact' ;
$userbuttons [ 'togglecontact' ] = array (
'buttontype' => 'togglecontact' ,
'title' => get_string ( $contacttitle , 'message' ),
'url' => new moodle_url ( '/message/index.php' , array (
'user1' => $USER -> id ,
'user2' => $user -> id ,
$contacturlaction => $user -> id ,
'sesskey' => sesskey ())
),
'image' => $contactimage ,
'linkattributes' => \core_message\helper :: togglecontact_link_params ( $user , $iscontact ),
'page' => $this -> page
);
}
2017-01-09 12:09:21 +08:00
}
} else {
$heading = null ;
2015-04-08 12:45:46 +08:00
}
}
2020-12-10 23:23:49 +11:00
if ( $this -> should_display_main_logo ( $headinglevel )) {
$sitename = format_string ( $SITE -> fullname , true , [ 'context' => context_course :: instance ( SITEID )]);
// Logo.
$html = html_writer :: div (
html_writer :: empty_tag ( 'img' , [
'src' => $this -> get_logo_url ( null , 150 ),
'alt' => get_string ( 'logoof' , '' , $sitename ),
'class' => 'img-fluid'
]),
'logo'
);
// Heading.
if ( ! isset ( $heading )) {
$html .= $this -> heading ( $this -> page -> heading , $headinglevel , 'sr-only' );
} else {
$html .= $this -> heading ( $heading , $headinglevel , 'sr-only' );
}
return $html ;
}
2015-04-08 12:45:46 +08:00
$contextheader = new context_header ( $heading , $headinglevel , $imagedata , $userbuttons );
2015-02-17 13:35:02 +08:00
return $this -> render_context_header ( $contextheader );
2015-04-08 12:45:46 +08:00
}
2015-02-17 13:35:02 +08:00
2016-07-15 17:02:22 +08:00
/**
* Renders the skip links for the page .
*
* @ param array $links List of skip links .
* @ return string HTML for the skip links .
*/
public function render_skip_links ( $links ) {
$context = [ 'links' => []];
foreach ( $links as $url => $text ) {
$context [ 'links' ][] = [ 'url' => $url , 'text' => $text ];
}
return $this -> render_from_template ( 'core/skip_links' , $context );
}
2015-02-17 13:35:02 +08:00
/**
* Renders the header bar .
*
* @ param context_header $contextheader Header bar object .
* @ return string HTML for the header bar .
*/
protected function render_context_header ( context_header $contextheader ) {
2020-12-07 02:41:06 +11:00
// Generate the heading first and before everything else as we might have to do an early return.
if ( ! isset ( $contextheader -> heading )) {
$heading = $this -> heading ( $this -> page -> heading , $contextheader -> headinglevel );
} else {
$heading = $this -> heading ( $contextheader -> heading , $contextheader -> headinglevel );
}
2018-10-09 13:00:42 +08:00
$showheader = empty ( $this -> page -> layout_options [ 'nocontextheader' ]);
if ( ! $showheader ) {
2020-12-07 02:41:06 +11:00
// Return the heading wrapped in an sr-only element so it is only visible to screen-readers.
return html_writer :: div ( $heading , 'sr-only' );
2018-10-09 13:00:42 +08:00
}
2015-02-17 13:35:02 +08:00
// All the html stuff goes here.
2015-04-08 12:45:46 +08:00
$html = html_writer :: start_div ( 'page-context-header' );
2015-02-17 13:35:02 +08:00
// Image data.
if ( isset ( $contextheader -> imagedata )) {
2015-04-08 12:45:46 +08:00
// Header specific image.
2021-06-24 13:49:08 +08:00
$html .= html_writer :: div ( $contextheader -> imagedata , 'page-header-image icon-size-7' );
2015-02-17 13:35:02 +08:00
}
// Headings.
2021-06-24 13:49:08 +08:00
if ( isset ( $contextheader -> prefix )) {
$prefix = html_writer :: div ( $contextheader -> prefix , 'text-muted' );
$heading = $prefix . $heading ;
}
2020-12-07 02:41:06 +11:00
$html .= html_writer :: tag ( 'div' , $heading , array ( 'class' => 'page-header-headings' ));
2015-02-17 13:35:02 +08:00
// Buttons.
if ( isset ( $contextheader -> additionalbuttons )) {
$html .= html_writer :: start_div ( 'btn-group header-button-group' );
2015-04-08 12:45:46 +08:00
foreach ( $contextheader -> additionalbuttons as $button ) {
if ( ! isset ( $button -> page )) {
2015-03-17 13:50:38 +08:00
// Include js for messaging.
2016-09-02 17:36:42 +08:00
if ( $button [ 'buttontype' ] === 'togglecontact' ) {
2016-09-22 12:30:28 +08:00
\core_message\helper :: togglecontact_requirejs ();
2015-03-17 13:50:38 +08:00
}
2019-02-13 19:02:16 +08:00
if ( $button [ 'buttontype' ] === 'message' ) {
\core_message\helper :: messageuser_requirejs ();
}
2015-04-08 12:45:46 +08:00
$image = $this -> pix_icon ( $button [ 'formattedimage' ], $button [ 'title' ], 'moodle' , array (
2015-02-17 13:35:02 +08:00
'class' => 'iconsmall' ,
'role' => 'presentation'
));
2015-04-08 12:45:46 +08:00
$image .= html_writer :: span ( $button [ 'title' ], 'header-button-title' );
2015-02-17 13:35:02 +08:00
} else {
$image = html_writer :: empty_tag ( 'img' , array (
2015-04-08 12:45:46 +08:00
'src' => $button [ 'formattedimage' ],
2015-02-17 13:35:02 +08:00
'role' => 'presentation'
));
}
2015-04-08 12:45:46 +08:00
$html .= html_writer :: link ( $button [ 'url' ], html_writer :: tag ( 'span' , $image ), $button [ 'linkattributes' ]);
2015-02-17 13:35:02 +08:00
}
$html .= html_writer :: end_div ();
}
$html .= html_writer :: end_div ();
return $html ;
}
2015-04-08 12:45:46 +08:00
/**
* Wrapper for header elements .
2015-03-27 16:17:30 +08:00
*
* @ return string HTML to display the main header .
2015-04-08 12:45:46 +08:00
*/
2015-05-04 17:02:33 +08:00
public function full_header () {
2021-09-05 23:08:30 +10:00
$pagetype = $this -> page -> pagetype ;
$homepage = get_home_page ();
$homepagetype = null ;
if ( $homepage == HOMEPAGE_MY ) {
$homepagetype = 'my-index' ;
} else if ( $homepage == HOMEPAGE_SITE ) {
$homepagetype = 'site-index' ;
}
2020-03-24 13:32:22 +00:00
if ( $this -> page -> include_region_main_settings_in_header_actions () &&
! $this -> page -> blocks -> is_block_present ( 'settings' )) {
2019-10-04 10:31:06 +08:00
// Only include the region main settings if the page has requested it and it doesn't already have
// the settings block on it. The region main settings are included in the settings block and
// duplicating the content causes behat failures.
2020-03-24 13:32:22 +00:00
$this -> page -> add_header_action ( html_writer :: div (
2019-09-10 14:03:19 +08:00
$this -> region_main_settings_menu (),
'd-print-none' ,
[ 'id' => 'region-main-settings-menu' ]
));
}
2019-02-13 16:29:22 +08:00
$header = new stdClass ();
$header -> settingsmenu = $this -> context_header_settings_menu ();
$header -> contextheader = $this -> context_header ();
2020-03-24 13:32:22 +00:00
$header -> hasnavbar = empty ( $this -> page -> layout_options [ 'nonavbar' ]);
2019-02-13 16:29:22 +08:00
$header -> navbar = $this -> navbar ();
$header -> pageheadingbutton = $this -> page_heading_button ();
$header -> courseheader = $this -> course_header ();
2020-03-24 13:32:22 +00:00
$header -> headeractions = $this -> page -> get_header_actions ();
2021-09-05 23:08:30 +10:00
if ( ! empty ( $pagetype ) && ! empty ( $homepagetype ) && $pagetype == $homepagetype ) {
$header -> welcomemessage = \core_user :: welcome_message ();
}
2019-02-13 16:29:22 +08:00
return $this -> render_from_template ( 'core/full_header' , $header );
}
/**
* This is an optional menu that can be added to a layout by a theme . It contains the
* menu for the course administration , only on the course main page .
*
* @ return string
*/
public function context_header_settings_menu () {
$context = $this -> page -> context ;
$menu = new action_menu ();
$items = $this -> page -> navbar -> get_items ();
$currentnode = end ( $items );
$showcoursemenu = false ;
$showfrontpagemenu = false ;
$showusermenu = false ;
// We are on the course home page.
if (( $context -> contextlevel == CONTEXT_COURSE ) &&
! empty ( $currentnode ) &&
( $currentnode -> type == navigation_node :: TYPE_COURSE || $currentnode -> type == navigation_node :: TYPE_SECTION )) {
$showcoursemenu = true ;
}
$courseformat = course_get_format ( $this -> page -> course );
// This is a single activity course format, always show the course menu on the activity main page.
if ( $context -> contextlevel == CONTEXT_MODULE &&
! $courseformat -> has_view_page ()) {
$this -> page -> navigation -> initialise ();
$activenode = $this -> page -> navigation -> find_active_node ();
// If the settings menu has been forced then show the menu.
if ( $this -> page -> is_settings_menu_forced ()) {
$showcoursemenu = true ;
} else if ( ! empty ( $activenode ) && ( $activenode -> type == navigation_node :: TYPE_ACTIVITY ||
$activenode -> type == navigation_node :: TYPE_RESOURCE )) {
// We only want to show the menu on the first page of the activity. This means
// the breadcrumb has no additional nodes.
if ( $currentnode && ( $currentnode -> key == $activenode -> key && $currentnode -> type == $activenode -> type )) {
$showcoursemenu = true ;
}
}
}
// This is the site front page.
if ( $context -> contextlevel == CONTEXT_COURSE &&
! empty ( $currentnode ) &&
$currentnode -> key === 'home' ) {
$showfrontpagemenu = true ;
}
// This is the user profile page.
if ( $context -> contextlevel == CONTEXT_USER &&
! empty ( $currentnode ) &&
( $currentnode -> key === 'myprofile' )) {
$showusermenu = true ;
}
if ( $showfrontpagemenu ) {
$settingsnode = $this -> page -> settingsnav -> find ( 'frontpage' , navigation_node :: TYPE_SETTING );
if ( $settingsnode ) {
// Build an action menu based on the visible nodes from this navigation tree.
$skipped = $this -> build_action_menu_from_navigation ( $menu , $settingsnode , false , true );
// We only add a list to the full settings menu if we didn't include every node in the short menu.
if ( $skipped ) {
$text = get_string ( 'morenavigationlinks' );
$url = new moodle_url ( '/course/admin.php' , array ( 'courseid' => $this -> page -> course -> id ));
$link = new action_link ( $url , $text , null , null , new pix_icon ( 't/edit' , $text ));
$menu -> add_secondary_action ( $link );
}
}
} else if ( $showcoursemenu ) {
$settingsnode = $this -> page -> settingsnav -> find ( 'courseadmin' , navigation_node :: TYPE_COURSE );
if ( $settingsnode ) {
// Build an action menu based on the visible nodes from this navigation tree.
$skipped = $this -> build_action_menu_from_navigation ( $menu , $settingsnode , false , true );
// We only add a list to the full settings menu if we didn't include every node in the short menu.
if ( $skipped ) {
$text = get_string ( 'morenavigationlinks' );
$url = new moodle_url ( '/course/admin.php' , array ( 'courseid' => $this -> page -> course -> id ));
$link = new action_link ( $url , $text , null , null , new pix_icon ( 't/edit' , $text ));
$menu -> add_secondary_action ( $link );
}
}
} else if ( $showusermenu ) {
// Get the course admin node from the settings navigation.
$settingsnode = $this -> page -> settingsnav -> find ( 'useraccount' , navigation_node :: TYPE_CONTAINER );
if ( $settingsnode ) {
// Build an action menu based on the visible nodes from this navigation tree.
$this -> build_action_menu_from_navigation ( $menu , $settingsnode );
}
}
return $this -> render ( $menu );
}
/**
* Take a node in the nav tree and make an action menu out of it .
* The links are injected in the action menu .
*
* @ param action_menu $menu
* @ param navigation_node $node
* @ param boolean $indent
* @ param boolean $onlytopleafnodes
* @ return boolean nodesskipped - True if nodes were skipped in building the menu
*/
protected function build_action_menu_from_navigation ( action_menu $menu ,
navigation_node $node ,
$indent = false ,
$onlytopleafnodes = false ) {
$skipped = false ;
// Build an action menu based on the visible nodes from this navigation tree.
foreach ( $node -> children as $menuitem ) {
if ( $menuitem -> display ) {
if ( $onlytopleafnodes && $menuitem -> children -> count ()) {
$skipped = true ;
continue ;
}
if ( $menuitem -> action ) {
if ( $menuitem -> action instanceof action_link ) {
$link = $menuitem -> action ;
// Give preference to setting icon over action icon.
if ( ! empty ( $menuitem -> icon )) {
$link -> icon = $menuitem -> icon ;
}
} else {
$link = new action_link ( $menuitem -> action , $menuitem -> text , null , null , $menuitem -> icon );
}
} else {
if ( $onlytopleafnodes ) {
$skipped = true ;
continue ;
}
$link = new action_link ( new moodle_url ( '#' ), $menuitem -> text , null , [ 'disabled' => true ], $menuitem -> icon );
}
if ( $indent ) {
$link -> add_class ( 'ml-4' );
}
if ( ! empty ( $menuitem -> classes )) {
$link -> add_class ( implode ( " " , $menuitem -> classes ));
}
$menu -> add_secondary_action ( $link );
$skipped = $skipped || $this -> build_action_menu_from_navigation ( $menu , $menuitem , true );
}
}
return $skipped ;
}
/**
* This is an optional menu that can be added to a layout by a theme . It contains the
* menu for the most specific thing from the settings block . E . g . Module administration .
*
* @ return string
*/
public function region_main_settings_menu () {
$context = $this -> page -> context ;
$menu = new action_menu ();
if ( $context -> contextlevel == CONTEXT_MODULE ) {
$this -> page -> navigation -> initialise ();
$node = $this -> page -> navigation -> find_active_node ();
$buildmenu = false ;
// If the settings menu has been forced then show the menu.
if ( $this -> page -> is_settings_menu_forced ()) {
$buildmenu = true ;
} else if ( ! empty ( $node ) && ( $node -> type == navigation_node :: TYPE_ACTIVITY ||
$node -> type == navigation_node :: TYPE_RESOURCE )) {
$items = $this -> page -> navbar -> get_items ();
$navbarnode = end ( $items );
// We only want to show the menu on the first page of the activity. This means
// the breadcrumb has no additional nodes.
if ( $navbarnode && ( $navbarnode -> key === $node -> key && $navbarnode -> type == $node -> type )) {
$buildmenu = true ;
}
}
if ( $buildmenu ) {
// Get the course admin node from the settings navigation.
$node = $this -> page -> settingsnav -> find ( 'modulesettings' , navigation_node :: TYPE_SETTING );
if ( $node ) {
// Build an action menu based on the visible nodes from this navigation tree.
$this -> build_action_menu_from_navigation ( $menu , $node );
}
}
} else if ( $context -> contextlevel == CONTEXT_COURSECAT ) {
// For course category context, show category settings menu, if we're on the course category page.
if ( $this -> page -> pagetype === 'course-index-category' ) {
$node = $this -> page -> settingsnav -> find ( 'categorysettings' , navigation_node :: TYPE_CONTAINER );
if ( $node ) {
// Build an action menu based on the visible nodes from this navigation tree.
$this -> build_action_menu_from_navigation ( $menu , $node );
}
}
} else {
$items = $this -> page -> navbar -> get_items ();
$navbarnode = end ( $items );
if ( $navbarnode && ( $navbarnode -> key === 'participants' )) {
$node = $this -> page -> settingsnav -> find ( 'users' , navigation_node :: TYPE_CONTAINER );
if ( $node ) {
// Build an action menu based on the visible nodes from this navigation tree.
$this -> build_action_menu_from_navigation ( $menu , $node );
}
}
}
return $this -> render ( $menu );
2015-02-17 13:35:02 +08:00
}
2015-10-02 23:13:44 +08:00
/**
* Displays the list of tags associated with an entry
*
* @ param array $tags list of instances of core_tag or stdClass
* @ param string $label label to display in front , by default 'Tags' ( get_string ( 'tags' )), set to null
* to use default , set to '' ( empty string ) to omit the label completely
* @ param string $classes additional classes for the enclosing div element
* @ param int $limit limit the number of tags to display , if size of $tags is more than this limit the " more " link
* will be appended to the end , JS will toggle the rest of the tags
* @ param context $pagecontext specify if needed to overwrite the current page context for the view tag link
2019-09-30 13:09:32 +01:00
* @ param bool $accesshidelabel if true , the label should have class = " accesshide " added .
2015-10-02 23:13:44 +08:00
* @ return string
*/
2019-09-30 13:09:32 +01:00
public function tag_list ( $tags , $label = null , $classes = '' , $limit = 10 ,
$pagecontext = null , $accesshidelabel = false ) {
$list = new \core_tag\output\taglist ( $tags , $label , $classes , $limit , $pagecontext , $accesshidelabel );
2015-10-02 23:13:44 +08:00
return $this -> render_from_template ( 'core_tag/taglist' , $list -> export_for_template ( $this ));
2016-01-18 15:18:14 +08:00
}
/**
* Renders element for inline editing of any value
*
* @ param \core\output\inplace_editable $element
* @ return string
*/
public function render_inplace_editable ( \core\output\inplace_editable $element ) {
return $this -> render_from_template ( 'core/inplace_editable' , $element -> export_for_template ( $this ));
2015-10-02 23:13:44 +08:00
}
2016-06-20 19:12:43 +08:00
/**
* Renders a bar chart .
*
* @ param \core\chart_bar $chart The chart .
* @ return string .
*/
public function render_chart_bar ( \core\chart_bar $chart ) {
return $this -> render_chart ( $chart );
}
/**
* Renders a line chart .
*
* @ param \core\chart_line $chart The chart .
* @ return string .
*/
public function render_chart_line ( \core\chart_line $chart ) {
return $this -> render_chart ( $chart );
}
/**
* Renders a pie chart .
*
* @ param \core\chart_pie $chart The chart .
* @ return string .
*/
public function render_chart_pie ( \core\chart_pie $chart ) {
return $this -> render_chart ( $chart );
}
/**
* Renders a chart .
*
* @ param \core\chart_base $chart The chart .
2016-07-04 18:38:05 +08:00
* @ param bool $withtable Whether to include a data table with the chart .
2016-06-20 19:12:43 +08:00
* @ return string .
*/
2016-07-04 18:38:05 +08:00
public function render_chart ( \core\chart_base $chart , $withtable = true ) {
$chartdata = json_encode ( $chart );
return $this -> render_from_template ( 'core/chart' , ( object ) [
'chartdata' => $chartdata ,
'withtable' => $withtable
]);
2016-06-20 19:12:43 +08:00
}
2016-08-11 19:42:28 +08:00
/**
* Renders the login form .
*
2016-09-21 13:20:03 +08:00
* @ param \core_auth\output\login $form The renderable .
2016-08-11 19:42:28 +08:00
* @ return string
*/
2016-09-21 13:20:03 +08:00
public function render_login ( \core_auth\output\login $form ) {
2019-02-13 16:29:22 +08:00
global $CFG , $SITE ;
2018-09-24 21:40:25 +02:00
2016-08-11 19:42:28 +08:00
$context = $form -> export_for_template ( $this );
// Override because rendering is not supported in template yet.
2018-09-24 21:40:25 +02:00
if ( $CFG -> rememberusername == 0 ) {
$context -> cookieshelpiconformatted = $this -> help_icon ( 'cookiesenabledonlysession' );
} else {
$context -> cookieshelpiconformatted = $this -> help_icon ( 'cookiesenabled' );
}
2016-08-11 19:42:28 +08:00
$context -> errorformatted = $this -> error_text ( $context -> error );
2019-02-13 16:29:22 +08:00
$url = $this -> get_logo_url ();
if ( $url ) {
$url = $url -> out ( false );
}
$context -> logourl = $url ;
$context -> sitename = format_string ( $SITE -> fullname , true ,
[ 'context' => context_course :: instance ( SITEID ), " escape " => false ]);
2016-08-11 19:42:28 +08:00
2017-05-17 21:01:09 +02:00
return $this -> render_from_template ( 'core/loginform' , $context );
2016-08-11 19:42:28 +08:00
}
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
/**
* Renders an mform element from a template .
*
* @ param HTML_QuickForm_element $element element
* @ param bool $required if input is required field
2016-08-22 12:22:21 +08:00
* @ param bool $advanced if input is an advanced field
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
* @ param string $error error message to display
* @ param bool $ingroup True if this element is rendered as part of a group
* @ return mixed string | bool
*/
2016-08-22 12:22:21 +08:00
public function mform_element ( $element , $required , $advanced , $error , $ingroup ) {
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
$templatename = 'core_form/element-' . $element -> getType ();
if ( $ingroup ) {
$templatename .= " -inline " ;
}
try {
// We call this to generate a file not found exception if there is no template.
// We don't want to call export_for_template if there is no template.
core\output\mustache_template_finder :: get_template_filepath ( $templatename );
if ( $element instanceof templatable ) {
$elementcontext = $element -> export_for_template ( $this );
$helpbutton = '' ;
if ( method_exists ( $element , 'getHelpButton' )) {
$helpbutton = $element -> getHelpButton ();
}
$label = $element -> getLabel ();
2016-11-24 17:23:27 +08:00
$text = '' ;
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
if ( method_exists ( $element , 'getText' )) {
2016-11-24 17:23:27 +08:00
// There currently exists code that adds a form element with an empty label.
// If this is the case then set the label to the description.
if ( empty ( $label )) {
$label = $element -> getText ();
} else {
$text = $element -> getText ();
}
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
}
2018-08-09 10:54:08 +08:00
// Generate the form element wrapper ids and names to pass to the template.
// This differs between group and non-group elements.
if ( $element -> getType () === 'group' ) {
// Group element.
// The id will be something like 'fgroup_id_NAME'. E.g. fgroup_id_mygroup.
$elementcontext [ 'wrapperid' ] = $elementcontext [ 'id' ];
2019-04-09 14:00:14 +02:00
// Ensure group elements pass through the group name as the element name.
2018-08-09 10:54:08 +08:00
$elementcontext [ 'name' ] = $elementcontext [ 'groupname' ];
} else {
// Non grouped element.
// Creates an id like 'fitem_id_NAME'. E.g. fitem_id_mytextelement.
$elementcontext [ 'wrapperid' ] = 'fitem_' . $elementcontext [ 'id' ];
}
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
$context = array (
'element' => $elementcontext ,
'label' => $label ,
2016-11-24 17:23:27 +08:00
'text' => $text ,
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
'required' => $required ,
2016-08-22 12:22:21 +08:00
'advanced' => $advanced ,
MDL-55417 forms: Render form elements with a template
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.
Initial support is for all core form elements including:
text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.
Part of MDL-55071
2016-08-04 22:29:32 +08:00
'helpbutton' => $helpbutton ,
'error' => $error
);
return $this -> render_from_template ( $templatename , $context );
}
} catch ( Exception $e ) {
// No template for this element.
return false ;
}
}
2016-09-23 12:40:48 +02:00
2016-10-06 14:10:14 +08:00
/**
* Render the login signup form into a nice template for the theme .
*
* @ param mform $form
* @ return string
*/
public function render_login_signup_form ( $form ) {
2019-02-13 16:29:22 +08:00
global $SITE ;
2016-10-06 14:10:14 +08:00
$context = $form -> export_for_template ( $this );
2019-02-13 16:29:22 +08:00
$url = $this -> get_logo_url ();
if ( $url ) {
$url = $url -> out ( false );
}
$context [ 'logourl' ] = $url ;
$context [ 'sitename' ] = format_string ( $SITE -> fullname , true ,
[ 'context' => context_course :: instance ( SITEID ), " escape " => false ]);
2016-10-06 14:10:14 +08:00
return $this -> render_from_template ( 'core/signup_form_layout' , $context );
}
2018-03-08 15:01:25 +08:00
/**
* Render the verify age and location page into a nice template for the theme .
*
* @ param \core_auth\output\verify_age_location_page $page The renderable
* @ return string
*/
protected function render_verify_age_location_page ( $page ) {
$context = $page -> export_for_template ( $this );
return $this -> render_from_template ( 'core/auth_verify_age_location_page' , $context );
}
/**
* Render the digital minor contact information page into a nice template for the theme .
*
* @ param \core_auth\output\digital_minor_page $page The renderable
* @ return string
*/
protected function render_digital_minor_page ( $page ) {
$context = $page -> export_for_template ( $this );
return $this -> render_from_template ( 'core/auth_digital_minor_page' , $context );
}
2016-09-23 12:40:48 +02:00
/**
* Renders a progress bar .
*
* Do not use $OUTPUT -> render ( $bar ), instead use progress_bar :: create () .
*
* @ param progress_bar $bar The bar .
* @ return string HTML fragment
*/
public function render_progress_bar ( progress_bar $bar ) {
$data = $bar -> export_for_template ( $this );
return $this -> render_from_template ( 'core/progress_bar' , $data );
}
2019-03-01 14:43:25 +08:00
2021-01-09 14:32:19 +11:00
/**
* Renders an update to a progress bar .
*
* Note : This does not cleanly map to a renderable class and should
* never be used directly .
*
* @ param string $id
* @ param float $percent
* @ param string $msg Message
* @ param string $estimate time remaining message
* @ return string ascii fragment
*/
public function render_progress_bar_update ( string $id , float $percent , string $msg , string $estimate ) : string {
return html_writer :: script ( js_writer :: function_call ( 'updateProgressBar' , [ $id , $percent , $msg , $estimate ]));
}
2019-03-01 14:43:25 +08:00
/**
* Renders element for a toggle - all checkbox .
*
* @ param \core\output\checkbox_toggleall $element
* @ return string
*/
public function render_checkbox_toggleall ( \core\output\checkbox_toggleall $element ) {
2019-03-25 10:58:00 +08:00
return $this -> render_from_template ( $element -> get_template (), $element -> export_for_template ( $this ));
2019-03-01 14:43:25 +08:00
}
2021-07-01 15:57:29 +02:00
/**
* Renders release information in the footer popup
* @ return string Moodle release info .
*/
public function moodle_release () {
global $CFG ;
if ( is_siteadmin ()) {
return $CFG -> release ;
}
}
2009-12-16 18:00:58 +00:00
}
2009-08-10 06:22:04 +00:00
/**
* A renderer that generates output for command - line scripts .
*
* The implementation of this renderer is probably incomplete .
*
* @ copyright 2009 Tim Hunt
2012-01-05 13:28:08 +13:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
* @ since Moodle 2.0
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2009-08-10 06:22:04 +00:00
*/
2009-12-17 13:45:54 +00:00
class core_renderer_cli extends core_renderer {
2012-01-05 13:28:08 +13:00
2021-01-09 14:32:19 +11:00
/**
* @ var array $progressmaximums stores the largest percentage for a progress bar .
* @ return string ascii fragment
*/
private $progressmaximums = [];
2009-08-10 06:22:04 +00:00
/**
* Returns the page header .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ return string HTML fragment
*/
public function header () {
return $this -> page -> heading . " \n " ;
}
2020-03-14 23:34:49 +11:00
/**
* Renders a Check API result
*
* To aid in CLI consistency this status is NOT translated and the visual
* width is always exactly 10 chars .
*
* @ param result $result
* @ return string HTML fragment
*/
protected function render_check_result ( core\check\result $result ) {
$status = $result -> get_status ();
$labels = [
core\check\result :: NA => ' ' . cli_ansi_format ( '<colour:gray>' ) . ' NA ' ,
core\check\result :: OK => ' ' . cli_ansi_format ( '<colour:green>' ) . ' OK ' ,
core\check\result :: INFO => ' ' . cli_ansi_format ( '<colour:blue>' ) . ' INFO ' ,
core\check\result :: UNKNOWN => ' ' . cli_ansi_format ( '<colour:grey>' ) . ' UNKNOWN ' ,
core\check\result :: WARNING => ' ' . cli_ansi_format ( '<colour:black><bgcolour:yellow>' ) . ' WARNING ' ,
core\check\result :: ERROR => ' ' . cli_ansi_format ( '<bgcolour:red>' ) . ' ERROR ' ,
core\check\result :: CRITICAL => '' . cli_ansi_format ( '<bgcolour:red>' ) . ' CRITICAL ' ,
];
$string = $labels [ $status ] . cli_ansi_format ( '<colour:normal>' );
return $string ;
}
/**
* Renders a Check API result
*
* @ param result $result
* @ return string fragment
*/
public function check_result ( core\check\result $result ) {
return $this -> render_check_result ( $result );
}
2021-01-09 14:32:19 +11:00
/**
* Renders a progress bar .
*
* Do not use $OUTPUT -> render ( $bar ), instead use progress_bar :: create () .
*
* @ param progress_bar $bar The bar .
* @ return string ascii fragment
*/
public function render_progress_bar ( progress_bar $bar ) {
global $CFG ;
$size = 55 ; // The width of the progress bar in chars.
$ascii = " \n " ;
if ( stream_isatty ( STDOUT )) {
require_once ( $CFG -> libdir . '/clilib.php' );
$ascii .= " [ " . str_repeat ( ' ' , $size ) . " ] 0% \n " ;
return cli_ansi_format ( $ascii );
}
$this -> progressmaximums [ $bar -> get_id ()] = 0 ;
$ascii .= '[' ;
return $ascii ;
}
/**
* Renders an update to a progress bar .
*
* Note : This does not cleanly map to a renderable class and should
* never be used directly .
*
* @ param string $id
* @ param float $percent
* @ param string $msg Message
* @ param string $estimate time remaining message
* @ return string ascii fragment
*/
public function render_progress_bar_update ( string $id , float $percent , string $msg , string $estimate ) : string {
$size = 55 ; // The width of the progress bar in chars.
$ascii = '' ;
// If we are rendering to a terminal then we can safely use ansii codes
// to move the cursor and redraw the complete progress bar each time
// it is updated.
if ( stream_isatty ( STDOUT )) {
$colour = $percent == 100 ? 'green' : 'blue' ;
$done = $percent * $size * 0.01 ;
$whole = floor ( $done );
$bar = " <colour: $colour > " ;
$bar .= str_repeat ( '█' , $whole );
if ( $whole < $size ) {
// By using unicode chars for partial blocks we can have higher
// precision progress bar.
$fraction = floor (( $done - $whole ) * 8 );
$bar .= core_text :: substr ( ' ▏▎▍▌▋▊▉' , $fraction , 1 );
// Fill the rest of the empty bar.
$bar .= str_repeat ( ' ' , $size - $whole - 1 );
}
$bar .= '<colour:normal>' ;
if ( $estimate ) {
$estimate = " - $estimate " ;
}
$ascii .= '<cursor:up>' ;
$ascii .= '<cursor:up>' ;
$ascii .= sprintf ( " [ $bar ] %3.1f%% %-22s \n " , $percent , $estimate );
$ascii .= sprintf ( " %-80s \n " , $msg );
return cli_ansi_format ( $ascii );
}
// If we are not rendering to a tty, ie when piped to another command
// or on windows we need to progressively render the progress bar
// which can only ever go forwards.
$done = round ( $percent * $size * 0.01 );
$delta = max ( 0 , $done - $this -> progressmaximums [ $id ]);
$ascii .= str_repeat ( '#' , $delta );
2021-02-06 15:50:02 +11:00
if ( $percent >= 100 && $delta > 0 ) {
2021-01-09 14:32:19 +11:00
$ascii .= sprintf ( " ] %3.1f%% \n $msg\n " , $percent );
}
2021-02-06 15:50:02 +11:00
$this -> progressmaximums [ $id ] += $delta ;
2021-01-09 14:32:19 +11:00
return $ascii ;
}
2009-08-10 06:22:04 +00:00
/**
* Returns a template fragment representing a Heading .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $text The text of the heading
* @ param int $level The level of importance of the heading
* @ param string $classes A space - separated list of CSS classes
* @ param string $id An optional ID
* @ return string A template fragment for a heading
*/
2010-04-10 22:23:46 +00:00
public function heading ( $text , $level = 2 , $classes = 'main' , $id = null ) {
2009-08-10 06:22:04 +00:00
$text .= " \n " ;
switch ( $level ) {
case 1 :
return '=>' . $text ;
case 2 :
return '-->' . $text ;
default :
return $text ;
}
}
/**
* Returns a template fragment representing a fatal error .
2012-01-05 13:28:08 +13:00
*
2009-08-10 06:22:04 +00:00
* @ param string $message The message to output
* @ param string $moreinfourl URL where more info can be found about the error
* @ param string $link Link for the Continue button
* @ param array $backtrace The execution backtrace
* @ param string $debuginfo Debugging information
* @ return string A template fragment for a fatal error
*/
2016-09-13 14:23:21 +02:00
public function fatal_error ( $message , $moreinfourl , $link , $backtrace , $debuginfo = null , $errorcode = " " ) {
2013-09-24 22:43:46 +02:00
global $CFG ;
2009-08-10 06:22:04 +00:00
$output = " !!! $message !!! \n " ;
2013-08-10 22:46:49 +02:00
if ( $CFG -> debugdeveloper ) {
2009-08-10 06:22:04 +00:00
if ( ! empty ( $debuginfo )) {
2010-10-27 07:48:19 +00:00
$output .= $this -> notification ( $debuginfo , 'notifytiny' );
2009-08-10 06:22:04 +00:00
}
if ( ! empty ( $backtrace )) {
2010-10-27 07:48:19 +00:00
$output .= $this -> notification ( 'Stack trace: ' . format_backtrace ( $backtrace , true ), 'notifytiny' );
2009-08-10 06:22:04 +00:00
}
}
2010-10-27 07:48:19 +00:00
return $output ;
2009-08-10 06:22:04 +00:00
}
/**
* Returns a template fragment representing a notification .
2012-01-05 13:28:08 +13:00
*
2016-02-16 08:45:29 +08:00
* @ param string $message The message to print out .
* @ param string $type The type of notification . See constants on \core\output\notification .
2020-11-27 16:20:45 +00:00
* @ param bool $closebutton Whether to show a close icon to remove the notification ( default true ) .
2009-08-10 06:22:04 +00:00
* @ return string A template fragment for a notification
*/
2020-11-27 16:20:45 +00:00
public function notification ( $message , $type = null , $closebutton = true ) {
2009-08-10 06:22:04 +00:00
$message = clean_text ( $message );
2016-02-16 08:45:29 +08:00
if ( $type === 'notifysuccess' || $type === 'success' ) {
2009-08-10 06:22:04 +00:00
return " ++ $message ++ \n " ;
}
return " !! $message !! \n " ;
}
2014-10-22 14:32:15 +11:00
/**
* There is no footer for a cli request , however we must override the
* footer method to prevent the default footer .
*/
public function footer () {}
2016-05-18 01:48:37 +00:00
/**
* Render a notification ( that is , a status message about something that has
* just happened ) .
*
* @ param \core\output\notification $notification the notification to print out
* @ return string plain text output
*/
public function render_notification ( \core\output\notification $notification ) {
return $this -> notification ( $notification -> get_message (), $notification -> get_message_type ());
}
2009-08-10 06:22:04 +00:00
}
2010-03-28 09:05:47 +00:00
/**
* A renderer that generates output for ajax scripts .
*
* This renderer prevents accidental sends back only json
* encoded error messages , all other output is ignored .
*
* @ copyright 2010 Petr Skoda
2012-01-05 13:28:08 +13:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
* @ since Moodle 2.0
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2010-03-28 09:05:47 +00:00
*/
class core_renderer_ajax extends core_renderer {
2012-01-05 13:28:08 +13:00
2010-03-28 09:05:47 +00:00
/**
* Returns a template fragment representing a fatal error .
2012-01-05 13:28:08 +13:00
*
2010-03-28 09:05:47 +00:00
* @ param string $message The message to output
* @ param string $moreinfourl URL where more info can be found about the error
* @ param string $link Link for the Continue button
* @ param array $backtrace The execution backtrace
* @ param string $debuginfo Debugging information
* @ return string A template fragment for a fatal error
*/
2016-09-13 14:23:21 +02:00
public function fatal_error ( $message , $moreinfourl , $link , $backtrace , $debuginfo = null , $errorcode = " " ) {
2010-09-17 08:02:32 +00:00
global $CFG ;
2010-08-16 19:11:21 +00:00
$this -> page -> set_context ( null ); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
2010-03-28 09:05:47 +00:00
$e = new stdClass ();
$e -> error = $message ;
2016-09-13 14:23:21 +02:00
$e -> errorcode = $errorcode ;
2010-03-28 09:05:47 +00:00
$e -> stacktrace = NULL ;
$e -> debuginfo = NULL ;
2010-07-16 08:30:06 +00:00
$e -> reproductionlink = NULL ;
2010-03-28 09:05:47 +00:00
if ( ! empty ( $CFG -> debug ) and $CFG -> debug >= DEBUG_DEVELOPER ) {
2014-04-16 15:11:44 +08:00
$link = ( string ) $link ;
if ( $link ) {
$e -> reproductionlink = $link ;
}
2010-03-28 09:05:47 +00:00
if ( ! empty ( $debuginfo )) {
$e -> debuginfo = $debuginfo ;
}
if ( ! empty ( $backtrace )) {
$e -> stacktrace = format_backtrace ( $backtrace , true );
}
}
2010-08-10 08:50:08 +00:00
$this -> header ();
2010-03-28 09:05:47 +00:00
return json_encode ( $e );
}
2012-01-05 13:28:08 +13:00
/**
* Used to display a notification .
* For the AJAX notifications are discarded .
*
2016-02-16 08:45:29 +08:00
* @ param string $message The message to print out .
* @ param string $type The type of notification . See constants on \core\output\notification .
2020-11-27 16:20:45 +00:00
* @ param bool $closebutton Whether to show a close icon to remove the notification ( default true ) .
2012-01-05 13:28:08 +13:00
*/
2020-11-27 16:20:45 +00:00
public function notification ( $message , $type = null , $closebutton = true ) {
}
2010-08-10 08:50:08 +00:00
2012-01-05 13:28:08 +13:00
/**
* Used to display a redirection message .
2012-01-12 12:55:50 +13:00
* AJAX redirections should not occur and as such redirection messages
2012-01-05 13:28:08 +13:00
* are discarded .
2012-02-14 18:24:09 +13:00
*
* @ param moodle_url | string $encodedurl
* @ param string $message
* @ param int $delay
* @ param bool $debugdisableredirect
2016-02-16 08:49:42 +08:00
* @ param string $messagetype The type of notification to show the message in .
* See constants on \core\output\notification .
2012-01-05 13:28:08 +13:00
*/
2016-02-16 08:49:42 +08:00
public function redirect_message ( $encodedurl , $message , $delay , $debugdisableredirect ,
$messagetype = \core\output\notification :: NOTIFY_INFO ) {}
2010-08-10 08:50:08 +00:00
2012-01-05 13:28:08 +13:00
/**
* Prepares the start of an AJAX output .
*/
2010-03-28 09:05:47 +00:00
public function header () {
2010-08-10 08:50:08 +00:00
// unfortunately YUI iframe upload does not support application/json
if ( ! empty ( $_FILES )) {
2011-01-14 09:32:17 +01:00
@ header ( 'Content-type: text/plain; charset=utf-8' );
2013-06-11 20:15:42 +02:00
if ( ! core_useragent :: supports_json_contenttype ()) {
@ header ( 'X-Content-Type-Options: nosniff' );
}
} else if ( ! core_useragent :: supports_json_contenttype ()) {
@ header ( 'Content-type: text/plain; charset=utf-8' );
@ header ( 'X-Content-Type-Options: nosniff' );
2010-08-10 08:50:08 +00:00
} else {
2011-01-14 09:32:17 +01:00
@ header ( 'Content-type: application/json; charset=utf-8' );
2010-08-10 08:50:08 +00:00
}
2012-02-14 18:24:09 +13:00
// Headers to make it not cacheable and json
2010-08-10 08:50:08 +00:00
@ header ( 'Cache-Control: no-store, no-cache, must-revalidate' );
@ header ( 'Cache-Control: post-check=0, pre-check=0' , false );
@ header ( 'Pragma: no-cache' );
@ header ( 'Expires: Mon, 20 Aug 1969 09:23:00 GMT' );
@ header ( 'Last-Modified: ' . gmdate ( 'D, d M Y H:i:s' ) . ' GMT' );
@ header ( 'Accept-Ranges: none' );
2010-03-28 09:05:47 +00:00
}
2010-08-10 08:50:08 +00:00
2012-01-05 13:28:08 +13:00
/**
* There is no footer for an AJAX request , however we must override the
* footer method to prevent the default footer .
*/
public function footer () {}
2010-03-28 09:05:47 +00:00
2012-01-05 13:28:08 +13:00
/**
* No need for headers in an AJAX request ... this should never happen .
* @ param string $text
* @ param int $level
* @ param string $classes
* @ param string $id
*/
public function heading ( $text , $level = 2 , $classes = 'main' , $id = null ) {}
2012-04-11 10:56:49 +12:00
}
MDL-29624 Media embedding system, part 1: new API and filter changes
Includes new API in medialib.php and core_media_renderer in outputrenderers.php.
Enable/disable settings moved from filter to systemwide appearance page.
Filter changed to use new API.
AMOS BEGIN
MOV [flashanimation,filter_mediaplugin],[flashanimation,core_media]
MOV [flashanimation_help,filter_mediaplugin],[flashanimation_desc,core_media]
MOV [flashvideo,filter_mediaplugin],[flashvideo,core_media]
MOV [flashvideo_help,filter_mediaplugin],[flashvideo_desc,core_media]
MOV [html5audio,filter_mediaplugin],[html5audio,core_media]
MOV [html5audio_help,filter_mediaplugin],[html5audio_desc,core_media]
MOV [html5video,filter_mediaplugin],[html5video,core_media]
MOV [html5video_help,filter_mediaplugin],[html5video_desc,core_media]
MOV [mp3audio,filter_mediaplugin],[mp3audio,core_media]
MOV [mp3audio_help,filter_mediaplugin],[mp3audio_desc,core_media]
MOV [legacyquicktime,filter_mediaplugin],[legacyquicktime,core_media]
MOV [legacyquicktime_help,filter_mediaplugin],[legacyquicktime_desc,core_media]
MOV [legacyreal,filter_mediaplugin],[legacyreal,core_media]
MOV [legacyreal_help,filter_mediaplugin],[legacyreal_desc,core_media]
MOV [legacywmp,filter_mediaplugin],[legacywmp,core_media]
MOV [legacywmp_help,filter_mediaplugin],[legacywmp_desc,core_media]
MOV [legacyheading,filter_mediaplugin],[legacyheading,core_media]
MOV [legacyheading_help,filter_mediaplugin],[legacyheading_desc,core_media]
MOV [sitevimeo,filter_mediaplugin],[sitevimeo,core_media]
MOV [sitevimeo_help,filter_mediaplugin],[sitevimeo_desc,core_media]
MOV [siteyoutube,filter_mediaplugin],[siteyoutube,core_media]
MOV [siteyoutube_help,filter_mediaplugin],[siteyoutube_desc,core_media]
AMOS END
2011-12-13 17:08:34 +00:00
2013-09-12 09:51:58 +12:00
/**
* The maintenance renderer .
*
* The purpose of this renderer is to block out the core renderer methods that are not usable when the site
* is running a maintenance related task .
* It must always extend the core_renderer as we switch from the core_renderer to this renderer in a couple of places .
*
2014-05-19 17:03:04 +01:00
* @ since Moodle 2.6
2013-09-12 09:51:58 +12:00
* @ package core
* @ category output
* @ copyright 2013 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
*/
class core_renderer_maintenance extends core_renderer {
/**
* Initialises the renderer instance .
2019-02-13 14:16:26 +08:00
*
2013-09-12 09:51:58 +12:00
* @ param moodle_page $page
* @ param string $target
* @ throws coding_exception
*/
public function __construct ( moodle_page $page , $target ) {
if ( $target !== RENDERER_TARGET_MAINTENANCE || $page -> pagelayout !== 'maintenance' ) {
throw new coding_exception ( 'Invalid request for the maintenance renderer.' );
}
parent :: __construct ( $page , $target );
}
/**
* Does nothing . The maintenance renderer cannot produce blocks .
*
* @ param block_contents $bc
* @ param string $region
* @ return string
*/
public function block ( block_contents $bc , $region ) {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce blocks .
*
* @ param string $region
* @ param array $classes
* @ param string $tag
2020-12-09 12:02:20 +11:00
* @ param boolean $fakeblocksonly
2013-09-12 09:51:58 +12:00
* @ return string
*/
2020-12-09 12:02:20 +11:00
public function blocks ( $region , $classes = array (), $tag = 'aside' , $fakeblocksonly = false ) {
2013-09-12 09:51:58 +12:00
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce blocks .
*
* @ param string $region
2020-12-09 12:02:20 +11:00
* @ param boolean $fakeblocksonly Output fake block only .
2013-09-12 09:51:58 +12:00
* @ return string
*/
2020-12-09 12:02:20 +11:00
public function blocks_for_region ( $region , $fakeblocksonly = false ) {
2013-09-12 09:51:58 +12:00
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a course content header .
*
* @ param bool $onlyifnotcalledbefore
* @ return string
*/
public function course_content_header ( $onlyifnotcalledbefore = false ) {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a course content footer .
*
* @ param bool $onlyifnotcalledbefore
* @ return string
*/
public function course_content_footer ( $onlyifnotcalledbefore = false ) {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a course header .
*
* @ return string
*/
public function course_header () {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a course footer .
*
* @ return string
*/
public function course_footer () {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a custom menu .
*
* @ param string $custommenuitems
* @ return string
*/
public function custom_menu ( $custommenuitems = '' ) {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce a file picker .
*
* @ param array $options
* @ return string
*/
public function file_picker ( $options ) {
return '' ;
}
/**
* Does nothing . The maintenance renderer cannot produce and HTML file tree .
*
* @ param array $dir
* @ return string
*/
public function htmllize_file_tree ( $dir ) {
return '' ;
}
2016-11-16 16:00:39 +08:00
/**
* Overridden confirm message for upgrades .
*
* @ param string $message The question to ask the user
* @ param single_button | moodle_url | string $continue The single_button component representing the Continue answer .
* @ param single_button | moodle_url | string $cancel The single_button component representing the Cancel answer .
* @ return string HTML fragment
*/
public function confirm ( $message , $continue , $cancel ) {
// We need plain styling of confirm boxes on upgrade because we don't know which stylesheet we have (it could be
// from any previous version of Moodle).
if ( $continue instanceof single_button ) {
$continue -> primary = true ;
} else if ( is_string ( $continue )) {
$continue = new single_button ( new moodle_url ( $continue ), get_string ( 'continue' ), 'post' , true );
} else if ( $continue instanceof moodle_url ) {
$continue = new single_button ( $continue , get_string ( 'continue' ), 'post' , true );
} else {
throw new coding_exception ( 'The continue param to $OUTPUT->confirm() must be either a URL' .
' (string/moodle_url) or a single_button instance.' );
}
if ( $cancel instanceof single_button ) {
$output = '' ;
} else if ( is_string ( $cancel )) {
$cancel = new single_button ( new moodle_url ( $cancel ), get_string ( 'cancel' ), 'get' );
} else if ( $cancel instanceof moodle_url ) {
$cancel = new single_button ( $cancel , get_string ( 'cancel' ), 'get' );
} else {
throw new coding_exception ( 'The cancel param to $OUTPUT->confirm() must be either a URL' .
' (string/moodle_url) or a single_button instance.' );
}
$output = $this -> box_start ( 'generalbox' , 'notice' );
$output .= html_writer :: tag ( 'h4' , get_string ( 'confirm' ));
$output .= html_writer :: tag ( 'p' , $message );
$output .= html_writer :: tag ( 'div' , $this -> render ( $continue ) . $this -> render ( $cancel ), array ( 'class' => 'buttons' ));
$output .= $this -> box_end ();
return $output ;
}
2013-09-12 09:51:58 +12:00
/**
* Does nothing . The maintenance renderer does not support JS .
*
* @ param block_contents $bc
*/
public function init_block_hider_js ( block_contents $bc ) {
2019-02-13 14:16:26 +08:00
// Does nothing.
2013-09-12 09:51:58 +12:00
}
/**
* Does nothing . The maintenance renderer cannot produce language menus .
*
* @ return string
*/
public function lang_menu () {
return '' ;
}
/**
* Does nothing . The maintenance renderer has no need for login information .
*
* @ param null $withlinks
* @ return string
*/
public function login_info ( $withlinks = null ) {
return '' ;
}
2019-02-13 16:29:22 +08:00
/**
* Secure login info .
*
* @ return string
*/
public function secure_login_info () {
return $this -> login_info ( false );
}
2013-09-12 09:51:58 +12:00
/**
* Does nothing . The maintenance renderer cannot produce user pictures .
*
* @ param stdClass $user
* @ param array $options
* @ return string
*/
public function user_picture ( stdClass $user , array $options = null ) {
return '' ;
}
}