From 847b0fa941e5d870b7a3c3b2479965b3429d8956 Mon Sep 17 00:00:00 2001 From: Mathew May Date: Wed, 13 Feb 2019 16:29:22 +0800 Subject: [PATCH] MDL-58428 renderer: Move renderer override from core Also update all html_writer user to escape the namespace. Also update the xpaths in behat --- admin/templates/setting.mustache | 28 + .../setting_configcolourpicker.mustache | 20 + .../templates/setting_configduration.mustache | 18 + admin/templates/setting_configfile.mustache | 23 + .../setting_configmultiselect.mustache | 19 + ...etting_configmultiselect_optgroup.mustache | 38 + admin/templates/setting_configselect.mustache | 19 + .../setting_configselect_optgroup.mustache | 36 + admin/templates/setting_configtext.mustache | 22 + .../templates/setting_configtextarea.mustache | 21 + admin/templates/setting_configtime.mustache | 24 + .../setting_courselist_frontpage.mustache | 22 + .../setting_devicedetectregex.mustache | 14 + admin/templates/setting_emoticons.mustache | 17 + .../templates/setting_gradecat_combo.mustache | 22 + admin/templates/settings.mustache | 21 + lib/outputrenderers.php | 733 ++++++++++-------- theme/boost/classes/output/core_renderer.php | 655 +--------------- .../output/block_settings_renderer.php | 14 +- .../classes/output/core/course_renderer.php | 14 +- .../classes/output/core/files_renderer.php | 6 +- .../core_course/management/renderer.php | 168 ++-- .../classes/output/core_renderer.php | 470 +++++++++++ theme/bootstrapbase/renderers.php | 9 +- .../bootstrapbase/renderers/core_renderer.php | 382 ++++++++- ...behat_theme_bootstrapbase_behat_course.php | 2 +- ...t_theme_bootstrapbase_behat_navigation.php | 6 +- 27 files changed, 1711 insertions(+), 1112 deletions(-) create mode 100644 theme/bootstrapbase/classes/output/core_renderer.php diff --git a/admin/templates/setting.mustache b/admin/templates/setting.mustache index ed172466eba..9881bbccc02 100644 --- a/admin/templates/setting.mustache +++ b/admin/templates/setting.mustache @@ -13,6 +13,34 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting + + Admin setting template. + + Context variables required for this template: + * labelfor - id of the form element + * title - Setting title + * override - Overridden message + * warning - Warning message + * name - Setting name + * error - Error message + * element - The Element HTML + * forceltr - Force this element to be displayed LTR + * default - Default value + + Example context (json): + { + "title": "Setting title", + "labelfor": "id0", + "override": "Overidden", + "warning": "Warning", + "name": "Name", + "error": "Error", + "element": "Raw HTML", + "forceltr": false, + "default": "Default value" + } }} {{! @template core_admin/setting diff --git a/admin/templates/setting_configcolourpicker.mustache b/admin/templates/setting_configcolourpicker.mustache index eb8c4877db4..c1503f8d17a 100644 --- a/admin/templates/setting_configcolourpicker.mustache +++ b/admin/templates/setting_configcolourpicker.mustache @@ -13,6 +13,26 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configcolourpicker + + Admin setting colour picker template. + + Context variables required for this template: + * icon - optional icon context (see pix_icon) + * name - element name + * id - element id + * value - element value + * haspreviewconfig - show preview of selected color + + Example context (json): + { + "icon": false, + "name": "name0", + "id": "id0", + "value": "#555655", + "haspreviewconfig": false + } }} {{! @template core_admin/setting_configcolourpicker diff --git a/admin/templates/setting_configduration.mustache b/admin/templates/setting_configduration.mustache index 3bad98c30da..bc66bbe8145 100644 --- a/admin/templates/setting_configduration.mustache +++ b/admin/templates/setting_configduration.mustache @@ -13,6 +13,24 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configduration + + Admin duration setting template. + + Context variables required for this template: + * name - form element name + * options - list of options for units containing name, value, selected + * value - yes + * id - element id + + Example context (json): + { + "name": "test", + "value": "5", + "id": "test0", + "options": [ { "name": "Minutes", "value": "mins", "selected": true } ] + } }} {{! @template core_admin/setting_configduration diff --git a/admin/templates/setting_configfile.mustache b/admin/templates/setting_configfile.mustache index f249fe622f8..b7d71cb9ad9 100644 --- a/admin/templates/setting_configfile.mustache +++ b/admin/templates/setting_configfile.mustache @@ -13,6 +13,29 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configfile + + Admin file setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * size - size of the field + * readonly - Make the field readonly + * value - value + * showvalidity - Show a green check if the path is readable + * valid - True if the path is readable + + Example context (json): + { + "name": "test", + "value": "/my-super-secret-path/file", + "id": "test0", + "readonly": true, + "showvalidity": true, + "valid": false + } }} {{! @template core_admin/setting_configfile diff --git a/admin/templates/setting_configmultiselect.mustache b/admin/templates/setting_configmultiselect.mustache index 7465a7ba0b6..ce866e9abed 100644 --- a/admin/templates/setting_configmultiselect.mustache +++ b/admin/templates/setting_configmultiselect.mustache @@ -13,6 +13,25 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configmultiselect + + Admin multiselect setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * size - element size + * options - list of options containing name, value, selected + + Example context (json): + { + "name": "test", + "id": "test0", + "size": "3", + "options": [ { "name": "Option 1", "value": "V", "selected": true }, + { "name": "Option 2", "value": "V", "selected": true } ] + } }} {{! @template core_admin/setting_configmultiselect diff --git a/admin/templates/setting_configmultiselect_optgroup.mustache b/admin/templates/setting_configmultiselect_optgroup.mustache index 9fa61c54707..cff46fdab77 100644 --- a/admin/templates/setting_configmultiselect_optgroup.mustache +++ b/admin/templates/setting_configmultiselect_optgroup.mustache @@ -13,6 +13,44 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configmultiselect_optgroup + + Admin multiselect setting template with optgroup support. + + Context variables required for this template: + * name - form element name + * id - element id + * size - element size + * options - list of options not grouped + * optgroups - list of options grouped containing the group label and for each option: name, value, selected + + Example context (json): + { + "name": "test", + "id": "test0", + "size": "3", + "options": [ + { "name": "Option 1", "value": "V", "selected": false }, + { "name": "Option 2", "value": "V", "selected": true } + ], + "optgroups": [ + { + "label": "Group 1", + "options": [ + { "name": "Option 3", "value": "V", "selected": false }, + { "name": "Option 4", "value": "V", "selected": true } + ] + }, + { + "label": "Group 2", + "options": [ + { "name": "Option 5", "value": "V", "selected": false }, + { "name": "Option 6", "value": "V", "selected": true } + ] + } + ] + } }} {{! @template core_admin/setting_configmultiselect_optgroup diff --git a/admin/templates/setting_configselect.mustache b/admin/templates/setting_configselect.mustache index a90c260add8..18e92136941 100644 --- a/admin/templates/setting_configselect.mustache +++ b/admin/templates/setting_configselect.mustache @@ -13,6 +13,25 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configselect + + Admin select setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * options - list of options containing name, value, selected + + Example context (json): + { + "name": "test", + "id": "test0", + "options": [ + { "name": "Option 1", "value": "V", "selected": true }, + { "name": "Option 2", "value": "V", "selected": true } + ] + } }} {{! @template core_admin/setting_configselect diff --git a/admin/templates/setting_configselect_optgroup.mustache b/admin/templates/setting_configselect_optgroup.mustache index ea5da127de6..aeb794bf7bd 100644 --- a/admin/templates/setting_configselect_optgroup.mustache +++ b/admin/templates/setting_configselect_optgroup.mustache @@ -13,6 +13,42 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configselect_optgroup + + Admin select with optgroup setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * options - list of options (not grouped) + * optgroups - list of options grouped containing the group label and for each option: name, value, selected + + Example context (json): + { + "name": "test", + "id": "test0", + "options": [ + { "name": "Option 1", "value": "V", "selected": false }, + { "name": "Option 2", "value": "V", "selected": false } + ], + "optgroups": [ + { + "label": "Group 1", + "options": [ + { "name": "Option 3", "value": "V", "selected": true }, + { "name": "Option 4", "value": "V", "selected": false } + ] + }, + { + "label": "Group 2", + "options": [ + { "name": "Option 5", "value": "V", "selected": false }, + { "name": "Option 6", "value": "V", "selected": false } + ] + } + ] + } }} {{! @template core_admin/setting_configselect_optgroup diff --git a/admin/templates/setting_configtext.mustache b/admin/templates/setting_configtext.mustache index 236228a7922..b41893c93c7 100644 --- a/admin/templates/setting_configtext.mustache +++ b/admin/templates/setting_configtext.mustache @@ -13,6 +13,28 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configtext + + Admin text setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * value - element value + * size - element size + * forceltr - always display as ltr + * attributes - list of additional attributes containing name, value + + Example context (json): + { + "name": "test", + "id": "test0", + "value": "A tall, dark stranger will have more fun than you.", + "size": "21", + "forceltr": false, + "attributes": [ { "name": "readonly", "value": "readonly" } ] + } }} {{! @template core_admin/setting_configtext diff --git a/admin/templates/setting_configtextarea.mustache b/admin/templates/setting_configtextarea.mustache index ec6ded3c9bb..1eee2ffcf95 100644 --- a/admin/templates/setting_configtextarea.mustache +++ b/admin/templates/setting_configtextarea.mustache @@ -13,6 +13,27 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configtextarea + + Admin textarea setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * rows - number of rows + * cols - number of cols + * value - default value + * forceltr - always display as ltr + + Example context (json): + { + "name": "test", + "cols": "30", + "rows": "3", + "value": "Excellent day for putting Slinkies on an escalator.", + "id": "test0" + } }} {{! @template core_admin/setting_configtextarea diff --git a/admin/templates/setting_configtime.mustache b/admin/templates/setting_configtime.mustache index b86b691d16a..69c5a79d22f 100644 --- a/admin/templates/setting_configtime.mustache +++ b/admin/templates/setting_configtime.mustache @@ -13,6 +13,30 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_configtime + + Admin time setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * hours - list of valid hour options containing name, value, selected + * minutes - list of valid minute options containing name, value, selected + + Example context (json): + { + "name": "test", + "id": "test0", + "minutes": [ + { "name": "00", "value": "0", "selected": true }, + { "name": "01", "value": "1", "selected": false } + ], + "hours": [ + { "name": "1", "value": "1", "selected": true }, + { "name": "2", "value": "2", "selected": false } + ] + } }} {{! @template core_admin/setting_configtime diff --git a/admin/templates/setting_courselist_frontpage.mustache b/admin/templates/setting_courselist_frontpage.mustache index 2fe18cc1028..3a3603054a4 100644 --- a/admin/templates/setting_courselist_frontpage.mustache +++ b/admin/templates/setting_courselist_frontpage.mustache @@ -13,6 +13,28 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_courselist_frontpage + + Admin courselist_frontpage setting template. + + Context variables required for this template: + * selects list of select objects containing id, name, key and options. + options is another nested list of items containing name, value and selected + + Example context (json): + { + "selects": [ + { + "id": "i1", + "name": "s1", + "key": "k1", + "options": [ + { "name": "Fish", "value": "snapper", "selected": true } + ] + } + ] + } }} {{! @template core_admin/setting_courselist_frontpage diff --git a/admin/templates/setting_devicedetectregex.mustache b/admin/templates/setting_devicedetectregex.mustache index cc8886288a0..e4bea2317d6 100644 --- a/admin/templates/setting_devicedetectregex.mustache +++ b/admin/templates/setting_devicedetectregex.mustache @@ -13,6 +13,20 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_devicedetectregex + + Admin devicedetectregex setting template. + + Context variables required for this template: + * expressions - List of expressions containing index, name, expression and value + + Example context (json): + { + "expressions": [ + { "index": "i1", "name": "Name", "expression": "/bird|yellow/", "value": "Canary" } + ] + } }} {{! @template core_admin/setting_devicedetectregex diff --git a/admin/templates/setting_emoticons.mustache b/admin/templates/setting_emoticons.mustache index 4c36174504e..072e1a7c1d4 100644 --- a/admin/templates/setting_emoticons.mustache +++ b/admin/templates/setting_emoticons.mustache @@ -13,6 +13,23 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_emoticons + + Admin emoticons setting template. + + Context variables required for this template: + * name - form element name + * id - element id + + Example context (json): + { + "emoticons": [ + { "fields": [ + { "name": "Smile", "field": "f1", "value": ":)" } + ]} + ] + } }} {{! @template core_admin/setting_emoticons diff --git a/admin/templates/setting_gradecat_combo.mustache b/admin/templates/setting_gradecat_combo.mustache index d64d75fea0d..d2ab931094d 100644 --- a/admin/templates/setting_gradecat_combo.mustache +++ b/admin/templates/setting_gradecat_combo.mustache @@ -13,6 +13,28 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/setting_gradecat_combo + + Admin gradecat_combo setting template. + + Context variables required for this template: + * name - form element name + * id - element id + * options - list of options containing name, value and selected + * forced - is it forced + * advanced - is it advanced + + Example context (json): + { + "name": "test", + "id": "test0", + "options": [ + { "name": "Option name", "value": "Value", "selected": true } + ], + "forced": true, + "advanced": true + } }} {{! @template core_admin/setting_gradecat_combo diff --git a/admin/templates/settings.mustache b/admin/templates/settings.mustache index 009730532f3..b8694e8512c 100644 --- a/admin/templates/settings.mustache +++ b/admin/templates/settings.mustache @@ -13,6 +13,27 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see . +}}{{! + @template core_admin/settings + + Admin settings form template. + + Context variables required for this template: + * actionurl - url to submit to + * params - list of parameters containing name and value + * return - page to return to + * title - form title + * settings - raw html for settings + * showsave - true if we need save buttons + + Example context (json): + { + "actionurl": "/", + "return": "/", + "title": "Settings Form", + "settings": "RAW HTML", + "showsave": true + } }} {{! @template core_admin/settings diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 4831e912e42..c4e5da5bc51 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -368,6 +368,38 @@ class renderer_base { theme_get_revision(), $logo); } + /** + * Whether we should display the logo in the navbar. + * + * We will when there are no main logos, and we have compact logo. + * + * @return bool + */ + public function should_display_navbar_logo() { + $logo = $this->get_compact_logo_url(); + return !empty($logo) && !$this->should_display_main_logo(); + } + + /** + * Whether we should display the main logo. + * + * @param int $headinglevel + * @return bool + */ + public function should_display_main_logo($headinglevel = 1) { + global $PAGE; + + // Only render the logo if we're on the front page or login page and the we have a logo. + $logo = $this->get_logo_url(); + if ($headinglevel == 1 && !empty($logo)) { + if ($PAGE->pagelayout == 'frontpage' || $PAGE->pagelayout == 'login') { + return true; + } + } + + return false; + } + } @@ -512,6 +544,9 @@ class core_renderer extends renderer_base { */ protected $unique_main_content_token; + /** @var custom_menu_item language The language menu if created */ + protected $language = null; + /** * Constructor * @@ -593,7 +628,7 @@ class core_renderer extends renderer_base { * @return string HTML fragment. */ public function standard_head_html() { - global $CFG, $SESSION; + global $CFG, $SESSION, $SITE, $PAGE; // Before we output any content, we need to ensure that certain // page components are set up. @@ -689,6 +724,13 @@ class core_renderer extends renderer_base { $output .= "\n".$CFG->additionalhtmlhead; } + if ($PAGE->pagelayout == 'frontpage') { + $summary = s(strip_tags(format_text($SITE->summary, FORMAT_HTML))); + if (!empty($summary)) { + $output .= "\n"; + } + } + return $output; } @@ -1591,15 +1633,23 @@ class core_renderer extends renderer_base { /** * Renders an action menu component. * - * ARIA references: - * - http://www.w3.org/WAI/GL/wiki/Using_ARIA_menus - * - http://stackoverflow.com/questions/12279113/recommended-wai-aria-implementation-for-navigation-bar-menu - * * @param action_menu $menu * @return string HTML */ public function render_action_menu(action_menu $menu) { + + // 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 ''; + } $context = $menu->export_for_template($this); + return $this->render_from_template('core/action_menu', $context); } @@ -1646,20 +1696,6 @@ class core_renderer extends renderer_base { /** * Prints a nice side block with an optional header. * - * The content is described - * by a {@link core_renderer::block_contents} object. - * - *
- *
- *
- * ...CONTENT... - * - *
- *
- *
- *
- * * @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. @@ -1669,144 +1705,29 @@ class core_renderer extends renderer_base { if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) { $bc->collapsible = block_contents::NOT_HIDEABLE; } - if (!empty($bc->blockinstanceid)) { - $bc->attributes['data-instanceid'] = $bc->blockinstanceid; - } - $skiptitle = strip_tags($bc->title); - if ($bc->blockinstanceid && !empty($skiptitle)) { - $bc->attributes['aria-labelledby'] = 'instance-'.$bc->blockinstanceid.'-header'; - } else if (!empty($bc->arialabel)) { - $bc->attributes['aria-label'] = $bc->arialabel; - } - if ($bc->dockable) { - $bc->attributes['data-dockable'] = 1; - } - if ($bc->collapsible == block_contents::HIDDEN) { - $bc->add_class('hidden'); - } - if (!empty($bc->controls)) { - $bc->add_class('block_with_controls'); + + $id = !empty($bc->attributes['id']) ? $bc->attributes['id'] : uniqid('block-'); + $context = new stdClass(); + $context->skipid = $bc->skipid; + $context->blockinstanceid = $bc->blockinstanceid; + $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'; + $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); } - - if (empty($skiptitle)) { - $output = ''; - $skipdest = ''; - } else { - $output = html_writer::link('#sb-'.$bc->skipid, get_string('skipa', 'access', $skiptitle), - array('class' => 'skip skip-block', 'id' => 'fsb-' . $bc->skipid)); - $skipdest = html_writer::span('', 'skip-block-to', - array('id' => 'sb-' . $bc->skipid)); - } - - $output .= html_writer::start_tag('div', $bc->attributes); - - $output .= $this->block_header($bc); - $output .= $this->block_content($bc); - - $output .= html_writer::end_tag('div'); - - $output .= $this->block_annotation($bc); - - $output .= $skipdest; - - $this->init_block_hider_js($bc); - return $output; - } - - /** - * Produces a header for a block - * - * @param block_contents $bc - * @return string - */ - protected function block_header(block_contents $bc) { - - $title = ''; - if ($bc->title) { - $attributes = array(); - if ($bc->blockinstanceid) { - $attributes['id'] = 'instance-'.$bc->blockinstanceid.'-header'; - } - $title = html_writer::tag('h2', $bc->title, $attributes); - } - - $blockid = null; - if (isset($bc->attributes['id'])) { - $blockid = $bc->attributes['id']; - } - $controlshtml = $this->block_controls($bc->controls, $blockid); - - $output = ''; - if ($title || $controlshtml) { - $output .= html_writer::tag('div', html_writer::tag('div', html_writer::tag('div', '', array('class'=>'block_action')). $title . $controlshtml, array('class' => 'title')), array('class' => 'header')); - } - return $output; - } - - /** - * Produces the content area for a block - * - * @param block_contents $bc - * @return string - */ - protected function block_content(block_contents $bc) { - $output = html_writer::start_tag('div', array('class' => 'content')); - if (!$bc->title && !$this->block_controls($bc->controls)) { - $output .= html_writer::tag('div', '', array('class'=>'block_action notitle')); - } - $output .= $bc->content; - $output .= $this->block_footer($bc); - $output .= html_writer::end_tag('div'); - - return $output; - } - - /** - * Produces the footer for a block - * - * @param block_contents $bc - * @return string - */ - protected function block_footer(block_contents $bc) { - $output = ''; - if ($bc->footer) { - $output .= html_writer::tag('div', $bc->footer, array('class' => 'footer')); - } - return $output; - } - - /** - * Produces the annotation for a block - * - * @param block_contents $bc - * @return string - */ - protected function block_annotation(block_contents $bc) { - $output = ''; - if ($bc->annotation) { - $output .= html_writer::tag('div', $bc->annotation, array('class' => 'blockannotation')); - } - return $output; - } - - /** - * Calls the JS require function to hide a block. - * - * @param block_contents $bc A block_contents object - */ - protected function init_block_hider_js(block_contents $bc) { - if (!empty($bc->attributes['id']) and $bc->collapsible != block_contents::NOT_HIDEABLE) { - $config = new stdClass; - $config->id = $bc->attributes['id']; - $config->title = strip_tags($bc->title); - $config->preference = 'block' . $bc->blockinstanceid . 'hidden'; - $config->tooltipVisible = get_string('hideblocka', 'access', $config->title); - $config->tooltipHidden = get_string('showblocka', 'access', $config->title); - - $this->page->requires->js_init_call('M.util.init_block_hider', array($config)); - user_preference_allow_ajax_update($config->preference, PARAM_BOOL); - } + return $this->render_from_template('core/block', $context); } /** @@ -2055,50 +1976,7 @@ class core_renderer extends renderer_base { * @return string HTML fragment */ protected function render_single_button(single_button $button) { - $attributes = array('type' => 'submit', - 'value' => $button->label, - 'disabled' => $button->disabled ? 'disabled' : null, - 'title' => $button->tooltip); - - if ($button->actions) { - $id = html_writer::random_id('single_button'); - $attributes['id'] = $id; - foreach ($button->actions as $action) { - $this->add_action_handler($action, $id); - } - } - - // first the input element - $output = html_writer::empty_tag('input', $attributes); - - // then hidden fields - $params = $button->url->params(); - if ($button->method === 'post') { - $params['sesskey'] = sesskey(); - } - foreach ($params as $var => $val) { - $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val)); - } - - // then div wrapper for xhtml strictness - $output = html_writer::tag('div', $output); - - // now the form itself around it - if ($button->method === 'get') { - $url = $button->url->out_omit_querystring(true); // url without params, the anchor part allowed - } else { - $url = $button->url->out_omit_querystring(); // url without params, the anchor part not allowed - } - if ($url === '') { - $url = '#'; // there has to be always some action - } - $attributes = array('method' => $button->method, - 'action' => $url, - 'id' => $button->formid); - $output = html_writer::tag('form', $output, $attributes); - - // and finally one more wrapper with class - return html_writer::tag('div', $output, array('class' => $button->class)); + return $this->render_from_template('core/single_button', $button->export_for_template($this)); } /** @@ -2471,7 +2349,8 @@ class core_renderer extends renderer_base { * @return string HTML fragment */ protected function render_help_icon(help_icon $helpicon) { - return $this->render_from_template('core/help_icon', $helpicon->export_for_template($this)); + $context = $helpicon->export_for_template($this); + return $this->render_from_template('core/help_icon', $context); } /** @@ -3154,7 +3033,7 @@ EOD; public function box_start($classes = 'generalbox', $id = null, $attributes = array()) { $this->opencontainers->push('box', html_writer::end_tag('div')); $attributes['id'] = $id; - $attributes['class'] = 'box ' . renderer_base::prepare_classes($classes); + $attributes['class'] = 'box py-3 ' . renderer_base::prepare_classes($classes); return html_writer::start_tag('div', $attributes); } @@ -3532,39 +3411,11 @@ EOD; } /** - * Return the navbar content so that it can be echoed out by the layout - * - * @return string XHTML navbar + * This renders the navbar. + * Uses bootstrap compatible html. */ public function navbar() { - $items = $this->page->navbar->get_items(); - $itemcount = count($items); - if ($itemcount === 0) { - return ''; - } - - $htmlblocks = array(); - // Iterate the navarray and display each node - $separator = get_separator(); - for ($i=0;$i < $itemcount;$i++) { - $item = $items[$i]; - $item->hideicon = true; - if ($i===0) { - $content = html_writer::tag('li', $this->render($item)); - } else { - $content = html_writer::tag('li', $separator.$this->render($item)); - } - $htmlblocks[] = $content; - } - - //accessibility: heading for navbar list (MDL-20446) - $navbarcontent = html_writer::tag('span', get_string('pagepath'), - array('class' => 'accesshide', 'id' => 'navbar-label')); - $navbarcontent .= html_writer::tag('nav', - html_writer::tag('ul', join('', $htmlblocks)), - array('aria-labelledby' => 'navbar-label')); - // XHTML - return $navbarcontent; + return $this->render_from_template('core/navbar', $this->page->navbar); } /** @@ -3719,14 +3570,44 @@ EOD; */ public function custom_menu($custommenuitems = '') { global $CFG; + if (empty($custommenuitems) && !empty($CFG->custommenuitems)) { $custommenuitems = $CFG->custommenuitems; } - if (empty($custommenuitems)) { - return ''; + $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; } $custommenu = new custom_menu($custommenuitems, current_language()); - return $this->render($custommenu); + $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); } /** @@ -3740,31 +3621,35 @@ EOD; * @return string */ protected function render_custom_menu(custom_menu $menu) { - static $menucount = 0; - // If the menu has no children return an empty string - if (!$menu->has_children()) { + global $CFG; + + $langs = get_string_manager()->get_list_of_translations(); + $haslangmenu = $this->lang_menu() != ''; + + if (!$menu->has_children() && !$haslangmenu) { return ''; } - // Increment the menu count. This is used for ID's that get worked with - // in JavaScript as is essential - $menucount++; - // Initialise this custom menu (the custom menu object is contained in javascript-static - $jscode = js_writer::function_call_with_Y('M.core_custom_menu.init', array('custom_menu_'.$menucount)); - $jscode = "(function(){{$jscode}})"; - $this->page->requires->yui_module('node-menunav', $jscode); - // Build the root nodes as required by YUI - $content = html_writer::start_tag('div', array('id'=>'custom_menu_'.$menucount, 'class'=>'yui3-menu yui3-menu-horizontal javascript-disabled custom-menu')); - $content .= html_writer::start_tag('div', array('class'=>'yui3-menu-content')); - $content .= html_writer::start_tag('ul'); - // Render each child - foreach ($menu->get_children() as $item) { - $content .= $this->render_custom_menu_item($item); + + 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); + } } - // Close the open tags - $content .= html_writer::end_tag('ul'); - $content .= html_writer::end_tag('div'); - $content .= html_writer::end_tag('div'); - // Return the custom menu + + $content = ''; + foreach ($menu->get_children() as $item) { + $context = $item->export_for_template($this); + $content .= $this->render_from_template('core/custom_menu_item', $context); + } + return $content; } @@ -3897,12 +3782,8 @@ EOD; if (empty($tabtree->subtree)) { return ''; } - $str = ''; - $str .= html_writer::start_tag('div', array('class' => 'tabtree')); - $str .= $this->render_tabobject($tabtree); - $str .= html_writer::end_tag('div'). - html_writer::tag('div', ' ', array('class' => 'clearer')); - return $str; + $data = $tabtree->export_for_template($this); + return $this->render_from_template('core/tabtree', $data); } /** @@ -4029,36 +3910,7 @@ EOD; * @return string */ public function body_css_classes(array $additionalclasses = array()) { - // Add a class for each block region on the page. - // We use the block manager here because the theme object makes get_string calls. - $usedregions = array(); - foreach ($this->page->blocks->get_regions() as $region) { - $additionalclasses[] = 'has-region-'.$region; - if ($this->page->blocks->region_has_content($region, $this)) { - $additionalclasses[] = 'used-region-'.$region; - $usedregions[] = $region; - } else { - $additionalclasses[] = 'empty-region-'.$region; - } - if ($this->page->blocks->region_completely_docked($region, $this)) { - $additionalclasses[] = 'docked-region-'.$region; - } - } - if (!$usedregions) { - // No regions means there is only content, add 'content-only' class. - $additionalclasses[] = 'content-only'; - } else if (count($usedregions) === 1) { - // Add the -only class for the only used region. - $region = array_shift($usedregions); - $additionalclasses[] = $region . '-only'; - } - foreach ($this->page->layout_options as $option => $value) { - if ($value) { - $additionalclasses[] = 'layout-option-'.$option; - } - } - $css = $this->page->bodyclasses .' '. join(' ', $additionalclasses); - return $css; + return $this->page->bodyclasses . ' ' . implode(' ', $additionalclasses); } /** @@ -4161,29 +4013,7 @@ EOD; * @return string The output. */ public function render_preferences_groups(preferences_groups $renderable) { - $html = ''; - $html .= html_writer::start_div('row-fluid'); - $html .= html_writer::start_tag('div', array('class' => 'span12 preferences-groups')); - $i = 0; - $open = false; - foreach ($renderable->groups as $group) { - if ($i == 0 || $i % 3 == 0) { - if ($open) { - $html .= html_writer::end_tag('div'); - } - $html .= html_writer::start_tag('div', array('class' => 'row-fluid')); - $open = true; - } - $html .= $this->render($group); - $i++; - } - - $html .= html_writer::end_tag('div'); - - $html .= html_writer::end_tag('ul'); - $html .= html_writer::end_tag('div'); - $html .= html_writer::end_div(); - return $html; + return $this->render_from_template('core/preferences_groups', $renderable); } /** @@ -4209,13 +4039,20 @@ EOD; } public function context_header($headerinfo = null, $headinglevel = 1) { - global $DB, $USER, $CFG; + global $DB, $USER, $CFG, $SITE; require_once($CFG->dirroot . '/user/lib.php'); $context = $this->page->context; $heading = null; $imagedata = null; $subheader = null; $userbuttons = null; + + if ($this->should_display_main_logo($headinglevel)) { + $sitename = format_string($SITE->fullname, true, array('context' => context_course::instance(SITEID))); + return html_writer::div(html_writer::empty_tag('img', [ + 'src' => $this->get_logo_url(null, 150), 'alt' => $sitename]), 'logo'); + } + // Make sure to use the heading if it has been set. if (isset($headerinfo['heading'])) { $heading = $headerinfo['heading']; @@ -4372,15 +4209,232 @@ EOD; * @return string HTML to display the main header. */ public function full_header() { - $html = html_writer::start_tag('header', array('id' => 'page-header', 'class' => 'clearfix')); - $html .= $this->context_header(); - $html .= html_writer::start_div('clearfix', array('id' => 'page-navbar')); - $html .= html_writer::tag('div', $this->navbar(), array('class' => 'breadcrumb-nav')); - $html .= html_writer::div($this->page_heading_button(), 'breadcrumb-button'); - $html .= html_writer::end_div(); - $html .= html_writer::tag('div', $this->course_header(), array('id' => 'course-header')); - $html .= html_writer::end_tag('header'); - return $html; + global $PAGE; + + $header = new stdClass(); + $header->settingsmenu = $this->context_header_settings_menu(); + $header->contextheader = $this->context_header(); + $header->hasnavbar = empty($PAGE->layout_options['nonavbar']); + $header->navbar = $this->navbar(); + $header->pageheadingbutton = $this->page_heading_button(); + $header->courseheader = $this->course_header(); + 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); } /** @@ -4462,7 +4516,7 @@ EOD; * @return string */ public function render_login(\core_auth\output\login $form) { - global $CFG; + global $CFG, $SITE; $context = $form->export_for_template($this); @@ -4473,6 +4527,13 @@ EOD; $context->cookieshelpiconformatted = $this->help_icon('cookiesenabled'); } $context->errorformatted = $this->error_text($context->error); + $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]); return $this->render_from_template('core/loginform', $context); } @@ -4556,7 +4617,16 @@ EOD; * @return string */ public function render_login_signup_form($form) { + global $SITE; + $context = $form->export_for_template($this); + $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]); return $this->render_from_template('core/signup_form_layout', $context); } @@ -5023,6 +5093,15 @@ class core_renderer_maintenance extends core_renderer { return ''; } + /** + * Secure login info. + * + * @return string + */ + public function secure_login_info() { + return $this->login_info(false); + } + /** * Does nothing. The maintenance renderer cannot produce user pictures. * diff --git a/theme/boost/classes/output/core_renderer.php b/theme/boost/classes/output/core_renderer.php index 1a918e9b333..b582821d67d 100644 --- a/theme/boost/classes/output/core_renderer.php +++ b/theme/boost/classes/output/core_renderer.php @@ -16,23 +16,7 @@ namespace theme_boost\output; -use coding_exception; -use html_writer; -use tabobject; -use tabtree; -use custom_menu_item; -use custom_menu; -use block_contents; -use navigation_node; -use action_link; -use stdClass; use moodle_url; -use preferences_groups; -use action_menu; -use help_icon; -use single_button; -use context_course; -use pix_icon; defined('MOODLE_INTERNAL') || die; @@ -46,70 +30,6 @@ defined('MOODLE_INTERNAL') || die; class core_renderer extends \core_renderer { - /** @var custom_menu_item language The language menu if created */ - protected $language = null; - - /** - * Outputs the opening section of a box. - * - * @param string $classes A space-separated list of CSS classes - * @param string $id An optional ID - * @param array $attributes An array of other attributes to give the box. - * @return string the HTML to output. - */ - public function box_start($classes = 'generalbox', $id = null, $attributes = array()) { - if (is_array($classes)) { - $classes = implode(' ', $classes); - } - return parent::box_start($classes . ' py-3', $id, $attributes); - } - - /** - * Wrapper for header elements. - * - * @return string HTML to display the main header. - */ - public function full_header() { - global $PAGE; - - $header = new stdClass(); - $header->settingsmenu = $this->context_header_settings_menu(); - $header->contextheader = $this->context_header(); - $header->hasnavbar = empty($PAGE->layout_options['nonavbar']); - $header->navbar = $this->navbar(); - $header->pageheadingbutton = $this->page_heading_button(); - $header->courseheader = $this->course_header(); - return $this->render_from_template('theme_boost/header', $header); - } - - /** - * The standard tags that should be included in the tag - * including a meta description for the front page - * - * @return string HTML fragment. - */ - public function standard_head_html() { - global $SITE, $PAGE; - - $output = parent::standard_head_html(); - if ($PAGE->pagelayout == 'frontpage') { - $summary = s(strip_tags(format_text($SITE->summary, FORMAT_HTML))); - if (!empty($summary)) { - $output .= "\n"; - } - } - - return $output; - } - - /* - * This renders the navbar. - * Uses bootstrap compatible html. - */ - public function navbar() { - return $this->render_from_template('core/navbar', $this->page->navbar); - } - /** * We don't like these... * @@ -117,577 +37,4 @@ class core_renderer extends \core_renderer { public function edit_button(moodle_url $url) { return ''; } - - /** - * Override to inject the logo. - * - * @param array $headerinfo The header info. - * @param int $headinglevel What level the 'h' tag will be. - * @return string HTML for the header bar. - */ - public function context_header($headerinfo = null, $headinglevel = 1) { - global $SITE; - - if ($this->should_display_main_logo($headinglevel)) { - $sitename = format_string($SITE->fullname, true, array('context' => context_course::instance(SITEID))); - return html_writer::div(html_writer::empty_tag('img', [ - 'src' => $this->get_logo_url(null, 150), 'alt' => $sitename]), 'logo'); - } - - return parent::context_header($headerinfo, $headinglevel); - } - - /** - * Get the compact logo URL. - * - * @return string - */ - public function get_compact_logo_url($maxwidth = 100, $maxheight = 100) { - return parent::get_compact_logo_url(null, 70); - } - - /** - * Whether we should display the main logo. - * - * @return bool - */ - public function should_display_main_logo($headinglevel = 1) { - global $PAGE; - - // Only render the logo if we're on the front page or login page and the we have a logo. - $logo = $this->get_logo_url(); - if ($headinglevel == 1 && !empty($logo)) { - if ($PAGE->pagelayout == 'frontpage' || $PAGE->pagelayout == 'login') { - return true; - } - } - - return false; - } - /** - * 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(); - } - - /* - * Overriding the custom_menu function ensures the custom menu is - * always shown, even if no menu items are configured in the global - * theme settings page. - */ - public function custom_menu($custommenuitems = '') { - global $CFG; - - if (empty($custommenuitems) && !empty($CFG->custommenuitems)) { - $custommenuitems = $CFG->custommenuitems; - } - $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; - } - $custommenu = new custom_menu($custommenuitems, current_language()); - $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); - } - - /* - * This renders the bootstrap top menu. - * - * This renderer is needed to enable the Bootstrap style navigation. - */ - protected function render_custom_menu(custom_menu $menu) { - global $CFG; - - $langs = get_string_manager()->get_list_of_translations(); - $haslangmenu = $this->lang_menu() != ''; - - if (!$menu->has_children() && !$haslangmenu) { - return ''; - } - - 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 = ''; - foreach ($menu->get_children() as $item) { - $context = $item->export_for_template($this); - $content .= $this->render_from_template('core/custom_menu_item', $context); - } - - return $content; - } - - /** - * This code renders the navbar button to control the display of the custom menu - * on smaller screens. - * - * Do not display the button if the menu is empty. - * - * @return string HTML fragment - */ - public function navbar_button() { - global $CFG; - - if (empty($CFG->custommenuitems) && $this->lang_menu() == '') { - return ''; - } - - $iconbar = html_writer::tag('span', '', array('class' => 'icon-bar')); - $button = html_writer::tag('a', $iconbar . "\n" . $iconbar. "\n" . $iconbar, array( - 'class' => 'btn btn-navbar', - 'data-toggle' => 'collapse', - 'data-target' => '.nav-collapse' - )); - return $button; - } - - /** - * Renders tabtree - * - * @param tabtree $tabtree - * @return string - */ - protected function render_tabtree(tabtree $tabtree) { - if (empty($tabtree->subtree)) { - return ''; - } - $data = $tabtree->export_for_template($this); - return $this->render_from_template('core/tabtree', $data); - } - - /** - * 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. - * - * @param tabobject $tabobject - * @return string HTML fragment - */ - protected function render_tabobject(tabobject $tab) { - throw new coding_exception('Tab objects should not be directly rendered.'); - } - - /** - * 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. - */ - public function block(block_contents $bc, $region) { - $bc = clone($bc); // Avoid messing up the object passed in. - if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) { - $bc->collapsible = block_contents::NOT_HIDEABLE; - } - - $id = !empty($bc->attributes['id']) ? $bc->attributes['id'] : uniqid('block-'); - $context = new stdClass(); - $context->skipid = $bc->skipid; - $context->blockinstanceid = $bc->blockinstanceid; - $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'; - $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); - } - - return $this->render_from_template('core/block', $context); - } - - /** - * Returns the CSS classes to apply to the body tag. - * - * @since Moodle 2.5.1 2.6 - * @param array $additionalclasses Any additional classes to apply. - * @return string - */ - public function body_css_classes(array $additionalclasses = array()) { - return $this->page->bodyclasses . ' ' . implode(' ', $additionalclasses); - } - - /** - * Renders preferences groups. - * - * @param preferences_groups $renderable The renderable - * @return string The output. - */ - public function render_preferences_groups(preferences_groups $renderable) { - return $this->render_from_template('core/preferences_groups', $renderable); - } - - /** - * Renders an action menu component. - * - * @param action_menu $menu - * @return string HTML - */ - public function render_action_menu(action_menu $menu) { - - // 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 ''; - } - $context = $menu->export_for_template($this); - - return $this->render_from_template('core/action_menu', $context); - } - - /** - * Implementation of user image rendering. - * - * @param help_icon $helpicon A help icon instance - * @return string HTML fragment - */ - protected function render_help_icon(help_icon $helpicon) { - $context = $helpicon->export_for_template($this); - return $this->render_from_template('core/help_icon', $context); - } - - /** - * Renders a single button widget. - * - * This will return HTML to display a form containing a single button. - * - * @param single_button $button - * @return string HTML fragment - */ - protected function render_single_button(single_button $button) { - return $this->render_from_template('core/single_button', $button->export_for_template($this)); - } - - /** - * Renders the login form. - * - * @param \core_auth\output\login $form The renderable. - * @return string - */ - public function render_login(\core_auth\output\login $form) { - global $CFG, $SITE; - - $context = $form->export_for_template($this); - - // Override because rendering is not supported in template yet. - if ($CFG->rememberusername == 0) { - $context->cookieshelpiconformatted = $this->help_icon('cookiesenabledonlysession'); - } else { - $context->cookieshelpiconformatted = $this->help_icon('cookiesenabled'); - } - $context->errorformatted = $this->error_text($context->error); - $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]); - - return $this->render_from_template('core/loginform', $context); - } - - /** - * Render the login signup form into a nice template for the theme. - * - * @param mform $form - * @return string - */ - public function render_login_signup_form($form) { - global $SITE; - - $context = $form->export_for_template($this); - $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]); - - return $this->render_from_template('core/signup_form_layout', $context); - } - - /** - * 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', '')); - $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', '')); - $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); - } - - /** - * 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); - } - - /** - * 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; - } - - /** - * Secure login info. - * - * @return string - */ - public function secure_login_info() { - return $this->login_info(false); - } -} +} \ No newline at end of file diff --git a/theme/bootstrapbase/classes/output/block_settings_renderer.php b/theme/bootstrapbase/classes/output/block_settings_renderer.php index d1c4cb3fee9..b30335a7d3c 100644 --- a/theme/bootstrapbase/classes/output/block_settings_renderer.php +++ b/theme/bootstrapbase/classes/output/block_settings_renderer.php @@ -39,13 +39,13 @@ use moodle_url; class block_settings_renderer extends \block_settings_renderer { public function search_form(moodle_url $formtarget, $searchvalue) { - $content = html_writer::start_tag('form', array('class'=>'adminsearchform', 'method'=>'get', 'action'=>$formtarget, 'role' => 'search')); - $content .= html_writer::start_tag('div'); - $content .= html_writer::tag('label', s(get_string('searchinsettings', 'admin')), array('for'=>'adminsearchquery', 'class'=>'accesshide')); - $content .= html_writer::empty_tag('input', array('id'=>'adminsearchquery', 'type'=>'text', 'name'=>'query', 'value'=>s($searchvalue))); - $content .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>s(get_string('search')))); - $content .= html_writer::end_tag('div'); - $content .= html_writer::end_tag('form'); + $content = \html_writer::start_tag('form', array('class'=>'adminsearchform', 'method'=>'get', 'action'=>$formtarget, 'role' => 'search')); + $content .= \html_writer::start_tag('div'); + $content .= \html_writer::tag('label', s(get_string('searchinsettings', 'admin')), array('for'=>'adminsearchquery', 'class'=>'accesshide')); + $content .= \html_writer::empty_tag('input', array('id'=>'adminsearchquery', 'type'=>'text', 'name'=>'query', 'value'=>s($searchvalue))); + $content .= \html_writer::empty_tag('input', array('type'=>'submit', 'value'=>s(get_string('search')))); + $content .= \html_writer::end_tag('div'); + $content .= \html_writer::end_tag('form'); return $content; } diff --git a/theme/bootstrapbase/classes/output/core/course_renderer.php b/theme/bootstrapbase/classes/output/core/course_renderer.php index a79a6ef6b3a..a05ad7123c4 100644 --- a/theme/bootstrapbase/classes/output/core/course_renderer.php +++ b/theme/bootstrapbase/classes/output/core/course_renderer.php @@ -71,18 +71,18 @@ class course_renderer extends \core_course_renderer { $strsearchcourses= get_string("searchcourses"); $searchurl = new moodle_url('/course/search.php'); - $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get')); - $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset')); - $output .= html_writer::tag('label', $strsearchcourses.': ', array('for' => $inputid)); - $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid, + $output = \html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get')); + $output .= \html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset')); + $output .= \html_writer::tag('label', $strsearchcourses.': ', array('for' => $inputid)); + $output .= \html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid, 'size' => $inputsize, 'name' => 'search', 'value' => s($value))); - $output .= html_writer::empty_tag('input', array('type' => 'submit', + $output .= \html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go'))); - $output .= html_writer::end_tag('fieldset'); + $output .= \html_writer::end_tag('fieldset'); if ($format != 'navbar') { $output .= $this->output->help_icon("coursesearch", "core"); } - $output .= html_writer::end_tag('form'); + $output .= \html_writer::end_tag('form'); return $output; } diff --git a/theme/bootstrapbase/classes/output/core/files_renderer.php b/theme/bootstrapbase/classes/output/core/files_renderer.php index fbf04487fa4..4022c5bda09 100644 --- a/theme/bootstrapbase/classes/output/core/files_renderer.php +++ b/theme/bootstrapbase/classes/output/core/files_renderer.php @@ -417,11 +417,11 @@ class files_renderer extends \core_files_renderer { * Default contents is one text input field with name="s" */ public function repository_default_searchform() { - $searchinput = html_writer::label(get_string('searchrepo', 'repository'), + $searchinput = \html_writer::label(get_string('searchrepo', 'repository'), 'reposearch', false, array('class' => 'accesshide')); - $searchinput .= html_writer::empty_tag('input', array('type' => 'text', + $searchinput .= \html_writer::empty_tag('input', array('type' => 'text', 'id' => 'reposearch', 'name' => 's', 'value' => get_string('search', 'repository'))); - $str = html_writer::tag('div', $searchinput, array('class' => "fp-def-search")); + $str = \html_writer::tag('div', $searchinput, array('class' => "fp-def-search")); return $str; } diff --git a/theme/bootstrapbase/classes/output/core_course/management/renderer.php b/theme/bootstrapbase/classes/output/core_course/management/renderer.php index c804c5204fd..a63e443d2a9 100644 --- a/theme/bootstrapbase/classes/output/core_course/management/renderer.php +++ b/theme/bootstrapbase/classes/output/core_course/management/renderer.php @@ -66,7 +66,7 @@ class renderer extends \core_course_management_renderer { if (!is_null($id)) { $attributes['id'] = $id; } - return html_writer::start_div($class, $attributes); + return \html_writer::start_div($class, $attributes); } /** @@ -112,7 +112,7 @@ class renderer extends \core_course_management_renderer { if (!is_null($id)) { $attributes['id'] = $id; } - return html_writer::start_div($class, $attributes); + return \html_writer::start_div($class, $attributes); } /** @@ -125,13 +125,13 @@ class renderer extends \core_course_management_renderer { $details = \core_course\management\helper::get_course_detail_array($course); $fullname = $details['fullname']['value']; - $html = html_writer::start_div('course-detail'); - $html .= html_writer::tag('h3', $fullname, array('id' => 'course-detail-title', 'tabindex' => '0')); + $html = \html_writer::start_div('course-detail'); + $html .= \html_writer::tag('h3', $fullname, array('id' => 'course-detail-title', 'tabindex' => '0')); $html .= $this->course_detail_actions($course); foreach ($details as $class => $data) { $html .= $this->detail_pair($data['key'], $data['value'], $class); } - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); return $html; } @@ -167,16 +167,16 @@ class renderer extends \core_course_management_renderer { $strsearchcourses = get_string("searchcourses"); $searchurl = new moodle_url('/course/management.php'); - $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get', + $output = \html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get', 'class' => 'form-inline')); - $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset m-y-1')); - $output .= html_writer::tag('label', $strsearchcourses, array('for' => $inputid)); - $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid, 'size' => $inputsize, + $output .= \html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset m-y-1')); + $output .= \html_writer::tag('label', $strsearchcourses, array('for' => $inputid)); + $output .= \html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid, 'size' => $inputsize, 'name' => 'search', 'value' => s($value), 'class' => 'form-control m-x-1')); - $output .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go'), + $output .= \html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go'), 'class' => 'btn btn-secondary')); - $output .= html_writer::end_tag('fieldset'); - $output .= html_writer::end_tag('form'); + $output .= \html_writer::end_tag('fieldset'); + $output .= \html_writer::end_tag('form'); return $output; } @@ -209,10 +209,10 @@ class renderer extends \core_course_management_renderer { 'aria-labelledby' => 'category-listing-title' ); - $html = html_writer::start_div('category-listing'); - $html .= html_writer::tag('h3', get_string('categories'), array('id' => 'category-listing-title')); + $html = \html_writer::start_div('category-listing'); + $html .= \html_writer::tag('h3', get_string('categories'), array('id' => 'category-listing-title')); $html .= $this->category_listing_actions($category); - $html .= html_writer::start_tag('ul', $attributes); + $html .= \html_writer::start_tag('ul', $attributes); foreach ($listing as $listitem) { // Render each category in the listing. $subcategories = array(); @@ -227,9 +227,9 @@ class renderer extends \core_course_management_renderer { $selectedparents ); } - $html .= html_writer::end_tag('ul'); + $html .= \html_writer::end_tag('ul'); $html .= $this->category_bulk_actions($category); - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); return $html; } @@ -286,7 +286,7 @@ class renderer extends \core_course_management_renderer { $viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id)); if ($isexpanded) { $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon', 'title' => '')); - $icon = html_writer::link( + $icon = \html_writer::link( $viewcaturl, $icon, array( @@ -298,7 +298,7 @@ class renderer extends \core_course_management_renderer { ); } else if ($isexpandable) { $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon', 'title' => '')); - $icon = html_writer::link( + $icon = \html_writer::link( $viewcaturl, $icon, array( @@ -313,16 +313,16 @@ class renderer extends \core_course_management_renderer { '', 'moodle', array('class' => 'tree-icon')); - $icon = html_writer::span($icon, 'float-left'); + $icon = \html_writer::span($icon, 'float-left'); } $actions = \core_course\management\helper::get_category_listitem_actions($category); $hasactions = !empty($actions) || $category->can_create_course(); - $html = html_writer::start_tag('li', $attributes); - $html .= html_writer::start_div('clearfix'); - $html .= html_writer::start_div('float-left ba-checkbox'); - $html .= html_writer::empty_tag('input', $bcatinput).' '; - $html .= html_writer::end_div(); + $html = \html_writer::start_tag('li', $attributes); + $html .= \html_writer::start_div('clearfix'); + $html .= \html_writer::start_div('float-left ba-checkbox'); + $html .= \html_writer::empty_tag('input', $bcatinput).' '; + $html .= \html_writer::end_div(); $html .= $icon; if ($hasactions) { $textattributes = array('class' => 'float-left categoryname'); @@ -332,26 +332,26 @@ class renderer extends \core_course_management_renderer { if (isset($textlabel)) { $textattributes['aria-label'] = $textlabel; } - $html .= html_writer::link($viewcaturl, $text, $textattributes); - $html .= html_writer::start_div('float-right'); + $html .= \html_writer::link($viewcaturl, $text, $textattributes); + $html .= \html_writer::start_div('float-right'); if ($category->idnumber) { - $html .= html_writer::tag('span', s($category->idnumber), array('class' => 'dimmed idnumber')); + $html .= \html_writer::tag('span', s($category->idnumber), array('class' => 'dimmed idnumber')); } if ($hasactions) { $html .= $this->category_listitem_actions($category, $actions); } $countid = 'course-count-'.$category->id; - $html .= html_writer::span( - html_writer::span($category->get_courses_count()) . - html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid)) . + $html .= \html_writer::span( + \html_writer::span($category->get_courses_count()) . + \html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid)) . $courseicon, 'course-count dimmed', array('aria-labelledby' => $countid) ); - $html .= html_writer::end_div(); - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); + $html .= \html_writer::end_div(); if ($isexpanded) { - $html .= html_writer::start_tag('ul', + $html .= \html_writer::start_tag('ul', array('class' => 'ml', 'role' => 'group', 'id' => 'subcategoryof'.$category->id)); $catatlevel = \core_course\management\helper::get_expanded_categories($category->path); $catatlevel[] = array_shift($selectedcategories); @@ -366,9 +366,9 @@ class renderer extends \core_course_management_renderer { $selectedcategories ); } - $html .= html_writer::end_tag('ul'); + $html .= \html_writer::end_tag('ul'); } - $html .= html_writer::end_tag('li'); + $html .= \html_writer::end_tag('li'); return $html; } @@ -392,15 +392,15 @@ class renderer extends \core_course_management_renderer { if ($cancreatecategory) { $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id)); - $actions[] = html_writer::link($url, get_string('createnewcategory')); + $actions[] = \html_writer::link($url, get_string('createnewcategory')); } if (core_course_category::can_approve_course_requests()) { - $actions[] = html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending')); + $actions[] = \html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending')); } if (count($actions) === 0) { return ''; } - return html_writer::div(join(' | ', $actions), 'listing-actions category-listing-actions'); + return \html_writer::div(join(' | ', $actions), 'listing-actions category-listing-actions'); } /** @@ -418,11 +418,11 @@ class renderer extends \core_course_management_renderer { $viewmode = 'default') { if ($category === null) { - $html = html_writer::start_div('select-a-category'); - $html .= html_writer::tag('h3', get_string('courses'), + $html = \html_writer::start_div('select-a-category'); + $html .= \html_writer::tag('h3', get_string('courses'), array('id' => 'course-listing-title', 'tabindex' => '0')); $html .= $this->output->notification(get_string('selectacategory'), 'notifymessage'); - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); return $html; } @@ -446,25 +446,25 @@ class renderer extends \core_course_management_renderer { $class .= ' lastpage'; } - $html = html_writer::start_div('course-listing'.$class, array( + $html = \html_writer::start_div('course-listing'.$class, array( 'data-category' => $category->id, 'data-page' => $page, 'data-totalpages' => $totalpages, 'data-totalcourses' => $totalcourses, 'data-canmoveoutof' => $category->can_move_courses_out_of() && $category->can_move_courses_into() )); - $html .= html_writer::tag('h3', $category->get_formatted_name(), + $html .= \html_writer::tag('h3', $category->get_formatted_name(), array('id' => 'course-listing-title', 'tabindex' => '0')); $html .= $this->course_listing_actions($category, $course, $perpage); $html .= $this->listing_pagination($category, $page, $perpage, false, $viewmode); - $html .= html_writer::start_tag('ul', array('class' => 'ml course-list', 'role' => 'group')); + $html .= \html_writer::start_tag('ul', array('class' => 'ml course-list', 'role' => 'group')); foreach ($category->get_courses($options) as $listitem) { $html .= $this->course_listitem($category, $listitem, $courseid); } - $html .= html_writer::end_tag('ul'); + $html .= \html_writer::end_tag('ul'); $html .= $this->listing_pagination($category, $page, $perpage, true, $viewmode); $html .= $this->course_bulk_actions($category); - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); return $html; } @@ -503,26 +503,26 @@ class renderer extends \core_course_management_renderer { $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id)); - $html = html_writer::start_tag('li', $attributes); - $html .= html_writer::start_div('clearfix'); + $html = \html_writer::start_tag('li', $attributes); + $html .= \html_writer::start_div('clearfix'); if ($category->can_resort_courses()) { // In order for dnd to be available the user must be able to resort the category children.. - $html .= html_writer::div($this->output->pix_icon('i/move_2d', get_string('dndcourse')), 'float-left drag-handle'); + $html .= \html_writer::div($this->output->pix_icon('i/move_2d', get_string('dndcourse')), 'float-left drag-handle'); } - $html .= html_writer::start_div('ba-checkbox float-left'); - $html .= html_writer::empty_tag('input', $bulkcourseinput).' '; - $html .= html_writer::end_div(); - $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename')); - $html .= html_writer::start_div('float-right'); + $html .= \html_writer::start_div('ba-checkbox float-left'); + $html .= \html_writer::empty_tag('input', $bulkcourseinput).' '; + $html .= \html_writer::end_div(); + $html .= \html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename')); + $html .= \html_writer::start_div('float-right'); if ($course->idnumber) { - $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber')); + $html .= \html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber')); } $html .= $this->course_listitem_actions($category, $course); - $html .= html_writer::end_div(); - $html .= html_writer::end_div(); - $html .= html_writer::end_tag('li'); + $html .= \html_writer::end_div(); + $html .= \html_writer::end_div(); + $html .= \html_writer::end_tag('li'); return $html; } @@ -540,12 +540,12 @@ class renderer extends \core_course_management_renderer { $actions = array(); if ($category->can_create_course()) { $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'catmanage')); - $actions[] = html_writer::link($url, get_string('createnewcourse')); + $actions[] = \html_writer::link($url, get_string('createnewcourse')); } if ($category->can_request_course()) { // Request a new course. $url = new moodle_url('/course/request.php', array('return' => 'management')); - $actions[] = html_writer::link($url, get_string('requestcourse')); + $actions[] = \html_writer::link($url, get_string('requestcourse')); } if ($category->can_resort_courses()) { $params = $this->page->url->params(); @@ -604,7 +604,7 @@ class renderer extends \core_course_management_renderer { $menu->attributes['class'] .= ' courses-per-page'; $menu->set_menu_trigger(get_string('perpagea', 'moodle', $perpage)); $actions[] = $this->render($menu); - return html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions'); + return \html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions'); } /** @@ -631,15 +631,15 @@ class renderer extends \core_course_management_renderer { $last = false; $i = $page * $perpage; - $html = html_writer::start_div('course-listing', array( + $html = \html_writer::start_div('course-listing', array( 'data-category' => 'search', 'data-page' => $page, 'data-totalpages' => $totalpages, 'data-totalcourses' => $totalcourses )); - $html .= html_writer::tag('h3', get_string('courses')); + $html .= \html_writer::tag('h3', get_string('courses')); $html .= $this->search_pagination($totalcourses, $page, $perpage); - $html .= html_writer::start_tag('ul', array('class' => 'ml')); + $html .= \html_writer::start_tag('ul', array('class' => 'ml')); foreach ($courses as $listitem) { $i++; if ($i == $totalcourses) { @@ -648,10 +648,10 @@ class renderer extends \core_course_management_renderer { $html .= $this->search_listitem($listitem, $courseid, $first, $last); $first = false; } - $html .= html_writer::end_tag('ul'); + $html .= \html_writer::end_tag('ul'); $html .= $this->search_pagination($totalcourses, $page, $perpage, true, $search); $html .= $this->course_search_bulk_actions(); - $html .= html_writer::end_div(); + $html .= \html_writer::end_div(); return $html; } @@ -687,21 +687,21 @@ class renderer extends \core_course_management_renderer { $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id)); $categoryname = core_course_category::get($course->category)->get_formatted_name(); - $html = html_writer::start_tag('li', $attributes); - $html .= html_writer::start_div('clearfix'); - $html .= html_writer::start_div('float-left'); + $html = \html_writer::start_tag('li', $attributes); + $html .= \html_writer::start_div('clearfix'); + $html .= \html_writer::start_div('float-left'); if ($bulkcourseinput) { - $html .= html_writer::empty_tag('input', $bulkcourseinput).' '; + $html .= \html_writer::empty_tag('input', $bulkcourseinput).' '; } - $html .= html_writer::end_div(); - $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename')); - $html .= html_writer::tag('span', $categoryname, array('class' => 'float-left categoryname')); - $html .= html_writer::start_div('float-right'); + $html .= \html_writer::end_div(); + $html .= \html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename')); + $html .= \html_writer::tag('span', $categoryname, array('class' => 'float-left categoryname')); + $html .= \html_writer::start_div('float-right'); $html .= $this->search_listitem_actions($course); - $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber')); - $html .= html_writer::end_div(); - $html .= html_writer::end_div(); - $html .= html_writer::end_tag('li'); + $html .= \html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber')); + $html .= \html_writer::end_div(); + $html .= \html_writer::end_div(); + $html .= \html_writer::end_tag('li'); return $html; } @@ -714,10 +714,10 @@ class renderer extends \core_course_management_renderer { * @return string */ protected function detail_pair($key, $value, $class ='') { - $html = html_writer::start_div('detail-pair row yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class)); - $html .= html_writer::div(html_writer::span($key), 'pair-key span3 col-md-3 yui3-u-1-4'); - $html .= html_writer::div(html_writer::span($value), 'pair-value span9 col-md-9 m-b-1 yui3-u-3-4 form-inline'); - $html .= html_writer::end_div(); + $html = \html_writer::start_div('detail-pair row yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class)); + $html .= \html_writer::div(html_writer::span($key), 'pair-key span3 col-md-3 yui3-u-1-4'); + $html .= \html_writer::div(html_writer::span($value), 'pair-value span9 col-md-9 m-b-1 yui3-u-3-4 form-inline'); + $html .= \html_writer::end_div(); return $html; } @@ -736,7 +736,7 @@ class renderer extends \core_course_management_renderer { foreach ($actions as $action) { $options[] = $this->action_link($action['url'], $action['string']); } - return html_writer::div(join(' | ', $options), 'listing-actions course-detail-listing-actions'); + return \html_writer::div(join(' | ', $options), 'listing-actions course-detail-listing-actions'); } } diff --git a/theme/bootstrapbase/classes/output/core_renderer.php b/theme/bootstrapbase/classes/output/core_renderer.php new file mode 100644 index 00000000000..0a53221b52f --- /dev/null +++ b/theme/bootstrapbase/classes/output/core_renderer.php @@ -0,0 +1,470 @@ +. + +namespace theme_bootstrapbase\output; + +use coding_exception; +use \html_writer; +use tabobject; +use tabtree; +use custom_menu_item; +use custom_menu; +use block_contents; +use navigation_node; +use action_link; +use stdClass; +use moodle_url; +use preferences_groups; +use action_menu; +use help_icon; +use single_button; +use context_course; +use pix_icon; + +defined('MOODLE_INTERNAL') || die; + +/** + * Renderers to align Moodle's HTML with that expected by Bootstrap + * + * @package theme_bootstrapbase + * @copyright 2012 Bas Brands, www.basbrands.nl + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +class core_renderer extends \core_renderer { + + /** + * Wrapper for header elements. + * + * @return string HTML to display the main header. + */ + public function full_header() { + $html = \html_writer::start_tag('header', array('id' => 'page-header', 'class' => 'clearfix')); + $html .= $this->context_header(); + $html .= \html_writer::start_div('clearfix', array('id' => 'page-navbar')); + $html .= \html_writer::tag('div', $this->navbar(), array('class' => 'breadcrumb-nav')); + $html .= \html_writer::div($this->page_heading_button(), 'breadcrumb-button'); + $html .= html_writer::end_div(); + $html .= html_writer::tag('div', $this->course_header(), array('id' => 'course-header')); + $html .= html_writer::end_tag('header'); + return $html; + } + + /** + * Return the navbar content so that it can be echoed out by the layout + * + * @return string XHTML navbar + */ + public function navbar() { + $items = $this->page->navbar->get_items(); + $itemcount = count($items); + if ($itemcount === 0) { + return ''; + } + + $htmlblocks = array(); + // Iterate the navarray and display each node + $separator = get_separator(); + for ($i=0;$i < $itemcount;$i++) { + $item = $items[$i]; + $item->hideicon = true; + if ($i===0) { + $content = html_writer::tag('li', $this->render($item)); + } else { + $content = html_writer::tag('li', $separator.$this->render($item)); + } + $htmlblocks[] = $content; + } + + //accessibility: heading for navbar list (MDL-20446) + $navbarcontent = html_writer::tag('span', get_string('pagepath'), + array('class' => 'accesshide', 'id' => 'navbar-label')); + $navbarcontent .= html_writer::tag('nav', + html_writer::tag('ul', join('', $htmlblocks)), + array('aria-labelledby' => 'navbar-label')); + // XHTML + return $navbarcontent; + } + + /** + * Returns HTML to display a "Turn editing on/off" button in a form. + * + * @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) { + + $url->param('sesskey', sesskey()); + if ($this->page->user_is_editing()) { + $url->param('edit', 'off'); + $editstring = get_string('turneditingoff'); + } else { + $url->param('edit', 'on'); + $editstring = get_string('turneditingon'); + } + + return $this->single_button($url, $editstring); + } + + /** + * Get the compact logo URL. + * + * @return string + */ + public function get_compact_logo_url($maxwidth = 100, $maxheight = 100) { + return parent::get_compact_logo_url(null, 70); + } + + /* + * Overriding the custom_menu function ensures the custom menu is + * always shown, even if no menu items are configured in the global + * theme settings page. + */ + public function custom_menu($custommenuitems = '') { + global $CFG; + if (empty($custommenuitems) && !empty($CFG->custommenuitems)) { + $custommenuitems = $CFG->custommenuitems; + } + if (empty($custommenuitems)) { + return ''; + } + $custommenu = new custom_menu($custommenuitems, current_language()); + return $this->render($custommenu); + } + + /* + * This renders the bootstrap top menu. + * + * This renderer is needed to enable the Bootstrap style navigation. + */ + protected function render_custom_menu(custom_menu $menu) { + static $menucount = 0; + // If the menu has no children return an empty string + if (!$menu->has_children()) { + return ''; + } + // Increment the menu count. This is used for ID's that get worked with + // in JavaScript as is essential + $menucount++; + // Initialise this custom menu (the custom menu object is contained in javascript-static + $jscode = js_writer::function_call_with_Y('M.core_custom_menu.init', array('custom_menu_'.$menucount)); + $jscode = "(function(){{$jscode}})"; + $this->page->requires->yui_module('node-menunav', $jscode); + // Build the root nodes as required by YUI + $content = html_writer::start_tag('div', array('id'=>'custom_menu_'.$menucount, 'class'=>'yui3-menu yui3-menu-horizontal javascript-disabled custom-menu')); + $content .= html_writer::start_tag('div', array('class'=>'yui3-menu-content')); + $content .= html_writer::start_tag('ul'); + // Render each child + foreach ($menu->get_children() as $item) { + $content .= $this->render_custom_menu_item($item); + } + // Close the open tags + $content .= html_writer::end_tag('ul'); + $content .= html_writer::end_tag('div'); + $content .= html_writer::end_tag('div'); + // Return the custom menu + return $content; + } + + /** + * Renders tabtree + * + * @param tabtree $tabtree + * @return string + */ + protected function render_tabtree(tabtree $tabtree) { + if (empty($tabtree->subtree)) { + return ''; + } + $str = ''; + $str .= html_writer::start_tag('div', array('class' => 'tabtree')); + $str .= $this->render_tabobject($tabtree); + $str .= html_writer::end_tag('div'). + html_writer::tag('div', ' ', array('class' => 'clearer')); + return $str; + } + + /** + * 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. + * + * @param tabobject $tabobject + * @return string HTML fragment + */ + protected function render_tabobject(tabobject $tab) { + throw new coding_exception('Tab objects should not be directly rendered.'); + } + + /** + * Prints a nice side block with an optional header. + * + * The content is described + * by a {@link core_renderer::block_contents} object. + * + *
+ *
+ *
+ * ...CONTENT... + * + *
+ *
+ *
+ *
+ * + * @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. + */ + public function block(block_contents $bc, $region) { + $bc = clone($bc); // Avoid messing up the object passed in. + if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) { + $bc->collapsible = block_contents::NOT_HIDEABLE; + } + if (!empty($bc->blockinstanceid)) { + $bc->attributes['data-instanceid'] = $bc->blockinstanceid; + } + $skiptitle = strip_tags($bc->title); + if ($bc->blockinstanceid && !empty($skiptitle)) { + $bc->attributes['aria-labelledby'] = 'instance-'.$bc->blockinstanceid.'-header'; + } else if (!empty($bc->arialabel)) { + $bc->attributes['aria-label'] = $bc->arialabel; + } + if ($bc->dockable) { + $bc->attributes['data-dockable'] = 1; + } + if ($bc->collapsible == block_contents::HIDDEN) { + $bc->add_class('hidden'); + } + if (!empty($bc->controls)) { + $bc->add_class('block_with_controls'); + } + + + if (empty($skiptitle)) { + $output = ''; + $skipdest = ''; + } else { + $output = html_writer::link('#sb-'.$bc->skipid, get_string('skipa', 'access', $skiptitle), + array('class' => 'skip skip-block', 'id' => 'fsb-' . $bc->skipid)); + $skipdest = html_writer::span('', 'skip-block-to', + array('id' => 'sb-' . $bc->skipid)); + } + + $output .= html_writer::start_tag('div', $bc->attributes); + + $output .= $this->block_header($bc); + $output .= $this->block_content($bc); + + $output .= html_writer::end_tag('div'); + + $output .= $this->block_annotation($bc); + + $output .= $skipdest; + + $this->init_block_hider_js($bc); + return $output; + } + + /** + * Returns the CSS classes to apply to the body tag. + * + * @since Moodle 2.5.1 2.6 + * @param array $additionalclasses Any additional classes to apply. + * @return string + */ + public function body_css_classes(array $additionalclasses = array()) { + // Add a class for each block region on the page. + // We use the block manager here because the theme object makes get_string calls. + $usedregions = array(); + foreach ($this->page->blocks->get_regions() as $region) { + $additionalclasses[] = 'has-region-'.$region; + if ($this->page->blocks->region_has_content($region, $this)) { + $additionalclasses[] = 'used-region-'.$region; + $usedregions[] = $region; + } else { + $additionalclasses[] = 'empty-region-'.$region; + } + if ($this->page->blocks->region_completely_docked($region, $this)) { + $additionalclasses[] = 'docked-region-'.$region; + } + } + if (!$usedregions) { + // No regions means there is only content, add 'content-only' class. + $additionalclasses[] = 'content-only'; + } else if (count($usedregions) === 1) { + // Add the -only class for the only used region. + $region = array_shift($usedregions); + $additionalclasses[] = $region . '-only'; + } + foreach ($this->page->layout_options as $option => $value) { + if ($value) { + $additionalclasses[] = 'layout-option-'.$option; + } + } + $css = $this->page->bodyclasses .' '. join(' ', $additionalclasses); + return $css; + } + + /** + * Renders preferences groups. + * + * @param preferences_groups $renderable The renderable + * @return string The output. + */ + public function render_preferences_groups(preferences_groups $renderable) { + $html = ''; + $html .= html_writer::start_div('row-fluid'); + $html .= html_writer::start_tag('div', array('class' => 'span12 preferences-groups')); + $i = 0; + $open = false; + foreach ($renderable->groups as $group) { + if ($i == 0 || $i % 3 == 0) { + if ($open) { + $html .= html_writer::end_tag('div'); + } + $html .= html_writer::start_tag('div', array('class' => 'row-fluid')); + $open = true; + } + $html .= $this->render($group); + $i++; + } + + $html .= html_writer::end_tag('div'); + + $html .= html_writer::end_tag('ul'); + $html .= html_writer::end_tag('div'); + $html .= html_writer::end_div(); + return $html; + } + + /** + * Renders an action menu component. + * + * ARIA references: + * - http://www.w3.org/WAI/GL/wiki/Using_ARIA_menus + * - http://stackoverflow.com/questions/12279113/recommended-wai-aria-implementation-for-navigation-bar-menu + * + * @param action_menu $menu + * @return string HTML + */ + public function render_action_menu(action_menu $menu) { + $context = $menu->export_for_template($this); + return $this->render_from_template('core/action_menu', $context); + } + + /** + * Implementation of user image rendering. + * + * @param help_icon $helpicon A help icon instance + * @return string HTML fragment + */ + protected function render_help_icon(help_icon $helpicon) { + return $this->render_from_template('core/help_icon', $helpicon->export_for_template($this)); + } + + /** + * Renders a single button widget. + * + * This will return HTML to display a form containing a single button. + * + * @param single_button $button + * @return string HTML fragment + */ + protected function render_single_button(single_button $button) { + $attributes = array('type' => 'submit', + 'value' => $button->label, + 'disabled' => $button->disabled ? 'disabled' : null, + 'title' => $button->tooltip); + + if ($button->actions) { + $id = html_writer::random_id('single_button'); + $attributes['id'] = $id; + foreach ($button->actions as $action) { + $this->add_action_handler($action, $id); + } + } + + // first the input element + $output = html_writer::empty_tag('input', $attributes); + + // then hidden fields + $params = $button->url->params(); + if ($button->method === 'post') { + $params['sesskey'] = sesskey(); + } + foreach ($params as $var => $val) { + $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val)); + } + + // then div wrapper for xhtml strictness + $output = html_writer::tag('div', $output); + + // now the form itself around it + if ($button->method === 'get') { + $url = $button->url->out_omit_querystring(true); // url without params, the anchor part allowed + } else { + $url = $button->url->out_omit_querystring(); // url without params, the anchor part not allowed + } + if ($url === '') { + $url = '#'; // there has to be always some action + } + $attributes = array('method' => $button->method, + 'action' => $url, + 'id' => $button->formid); + $output = html_writer::tag('form', $output, $attributes); + + // and finally one more wrapper with class + return html_writer::tag('div', $output, array('class' => $button->class)); + } + + /** + * Renders the login form. + * + * @param \core_auth\output\login $form The renderable. + * @return string + */ + public function render_login(\core_auth\output\login $form) { + global $CFG; + + $context = $form->export_for_template($this); + + // Override because rendering is not supported in template yet. + if ($CFG->rememberusername == 0) { + $context->cookieshelpiconformatted = $this->help_icon('cookiesenabledonlysession'); + } else { + $context->cookieshelpiconformatted = $this->help_icon('cookiesenabled'); + } + $context->errorformatted = $this->error_text($context->error); + + return $this->render_from_template('core/loginform', $context); + } + + /** + * Render the login signup form into a nice template for the theme. + * + * @param mform $form + * @return string + */ + public function render_login_signup_form($form) { + $context = $form->export_for_template($this); + + return $this->render_from_template('core/signup_form_layout', $context); + } + +} diff --git a/theme/bootstrapbase/renderers.php b/theme/bootstrapbase/renderers.php index 1f90ef92cd3..e856a428c78 100644 --- a/theme/bootstrapbase/renderers.php +++ b/theme/bootstrapbase/renderers.php @@ -22,5 +22,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -require_once('renderers/core_renderer.php'); - +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/core_renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/block_settings_renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/gradereport_history_renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/core/course_renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/core/files_renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/core_course/management/renderer.php'); +require_once($CFG->dirroot . '/theme/bootstrapbase/renderers/core_question/bank_renderer.php'); diff --git a/theme/bootstrapbase/renderers/core_renderer.php b/theme/bootstrapbase/renderers/core_renderer.php index 2fa7ae7983b..9e009d8397a 100644 --- a/theme/bootstrapbase/renderers/core_renderer.php +++ b/theme/bootstrapbase/renderers/core_renderer.php @@ -77,6 +77,30 @@ class theme_bootstrapbase_core_renderer extends core_renderer { $list_items . ''; } + /** + * This code renders the navbar button to control the display of the custom menu + * on smaller screens. + * + * Do not display the button if the menu is empty. + * + * @return string HTML fragment + */ + protected function navbar_button() { + global $CFG; + + if (empty($CFG->custommenuitems) && $this->lang_menu() == '') { + return ''; + } + + $iconbar = html_writer::tag('span', '', array('class' => 'icon-bar')); + $button = html_writer::tag('a', $iconbar . "\n" . $iconbar. "\n" . $iconbar, array( + 'class' => 'btn btn-navbar', + 'data-toggle' => 'collapse', + 'data-target' => '.nav-collapse' + )); + return $button; + } + /* * Overriding the custom_menu function ensures the custom menu is * always shown, even if no menu items are configured in the global @@ -187,30 +211,6 @@ class theme_bootstrapbase_core_renderer extends core_renderer { return $content; } - /** - * This code renders the navbar button to control the display of the custom menu - * on smaller screens. - * - * Do not display the button if the menu is empty. - * - * @return string HTML fragment - */ - protected function navbar_button() { - global $CFG; - - if (empty($CFG->custommenuitems) && $this->lang_menu() == '') { - return ''; - } - - $iconbar = html_writer::tag('span', '', array('class' => 'icon-bar')); - $button = html_writer::tag('a', $iconbar . "\n" . $iconbar. "\n" . $iconbar, array( - 'class' => 'btn btn-navbar', - 'data-toggle' => 'collapse', - 'data-target' => '.nav-collapse' - )); - return $button; - } - /** * Renders tabtree * @@ -256,6 +256,340 @@ class theme_bootstrapbase_core_renderer extends core_renderer { return html_writer::tag('li', $link, $params); } } + + /** + * Wrapper for header elements. + * + * @return string HTML to display the main header. + */ + public function full_header() { + $html = html_writer::start_tag('header', array('id' => 'page-header', 'class' => 'clearfix')); + $html .= $this->context_header(); + $html .= html_writer::start_div('clearfix', array('id' => 'page-navbar')); + $html .= html_writer::tag('div', $this->navbar(), array('class' => 'breadcrumb-nav')); + $html .= html_writer::div($this->page_heading_button(), 'breadcrumb-button'); + $html .= html_writer::end_div(); + $html .= html_writer::tag('div', $this->course_header(), array('id' => 'course-header')); + $html .= html_writer::end_tag('header'); + return $html; + } + + /** + * Get the compact logo URL. + * + * @return string + */ + public function get_compact_logo_url($maxwidth = 100, $maxheight = 100) { + return parent::get_compact_logo_url(null, 70); + } + + /** + * Prints a nice side block with an optional header. + * + * The content is described + * by a {@link core_renderer::block_contents} object. + * + *
+ *
+ *
+ * ...CONTENT... + * + *
+ *
+ *
+ *
+ * + * @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. + */ + public function block(block_contents $bc, $region) { + $bc = clone($bc); // Avoid messing up the object passed in. + if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) { + $bc->collapsible = block_contents::NOT_HIDEABLE; + } + if (!empty($bc->blockinstanceid)) { + $bc->attributes['data-instanceid'] = $bc->blockinstanceid; + } + $skiptitle = strip_tags($bc->title); + if ($bc->blockinstanceid && !empty($skiptitle)) { + $bc->attributes['aria-labelledby'] = 'instance-'.$bc->blockinstanceid.'-header'; + } else if (!empty($bc->arialabel)) { + $bc->attributes['aria-label'] = $bc->arialabel; + } + if ($bc->dockable) { + $bc->attributes['data-dockable'] = 1; + } + if ($bc->collapsible == block_contents::HIDDEN) { + $bc->add_class('hidden'); + } + if (!empty($bc->controls)) { + $bc->add_class('block_with_controls'); + } + + + if (empty($skiptitle)) { + $output = ''; + $skipdest = ''; + } else { + $output = html_writer::link('#sb-'.$bc->skipid, get_string('skipa', 'access', $skiptitle), + array('class' => 'skip skip-block', 'id' => 'fsb-' . $bc->skipid)); + $skipdest = html_writer::span('', 'skip-block-to', + array('id' => 'sb-' . $bc->skipid)); + } + + $output .= html_writer::start_tag('div', $bc->attributes); + + $output .= $this->block_header($bc); + $output .= $this->block_content($bc); + + $output .= html_writer::end_tag('div'); + + $output .= $this->block_annotation($bc); + + $output .= $skipdest; + + $this->init_block_hider_js($bc); + return $output; + } + + /** + * Produces a header for a block + * + * @param block_contents $bc + * @return string + */ + protected function block_header(block_contents $bc) { + + $title = ''; + if ($bc->title) { + $attributes = array(); + if ($bc->blockinstanceid) { + $attributes['id'] = 'instance-'.$bc->blockinstanceid.'-header'; + } + $title = html_writer::tag('h2', $bc->title, $attributes); + } + + $blockid = null; + if (isset($bc->attributes['id'])) { + $blockid = $bc->attributes['id']; + } + $controlshtml = $this->block_controls($bc->controls, $blockid); + + $output = ''; + if ($title || $controlshtml) { + $output .= html_writer::tag('div', html_writer::tag('div', html_writer::tag('div', '', array('class'=>'block_action')). $title . $controlshtml, array('class' => 'title')), array('class' => 'header')); + } + return $output; + } + + /** + * Produces the content area for a block + * + * @param block_contents $bc + * @return string + */ + protected function block_content(block_contents $bc) { + $output = html_writer::start_tag('div', array('class' => 'content')); + if (!$bc->title && !$this->block_controls($bc->controls)) { + $output .= html_writer::tag('div', '', array('class'=>'block_action notitle')); + } + $output .= $bc->content; + $output .= $this->block_footer($bc); + $output .= html_writer::end_tag('div'); + + return $output; + } + + /** + * Produces the footer for a block + * + * @param block_contents $bc + * @return string + */ + protected function block_footer(block_contents $bc) { + $output = ''; + if ($bc->footer) { + $output .= html_writer::tag('div', $bc->footer, array('class' => 'footer')); + } + return $output; + } + + /** + * Produces the annotation for a block + * + * @param block_contents $bc + * @return string + */ + protected function block_annotation(block_contents $bc) { + $output = ''; + if ($bc->annotation) { + $output .= html_writer::tag('div', $bc->annotation, array('class' => 'blockannotation')); + } + return $output; + } + + /** + * Calls the JS require function to hide a block. + * + * @param block_contents $bc A block_contents object + */ + protected function init_block_hider_js(block_contents $bc) { + if (!empty($bc->attributes['id']) and $bc->collapsible != block_contents::NOT_HIDEABLE) { + $config = new stdClass; + $config->id = $bc->attributes['id']; + $config->title = strip_tags($bc->title); + $config->preference = 'block' . $bc->blockinstanceid . 'hidden'; + $config->tooltipVisible = get_string('hideblocka', 'access', $config->title); + $config->tooltipHidden = get_string('showblocka', 'access', $config->title); + + $this->page->requires->js_init_call('M.util.init_block_hider', array($config)); + user_preference_allow_ajax_update($config->preference, PARAM_BOOL); + } + } + + /** + * Returns the CSS classes to apply to the body tag. + * + * @since Moodle 2.5.1 2.6 + * @param array $additionalclasses Any additional classes to apply. + * @return string + */ + public function body_css_classes(array $additionalclasses = array()) { + // Add a class for each block region on the page. + // We use the block manager here because the theme object makes get_string calls. + $usedregions = array(); + foreach ($this->page->blocks->get_regions() as $region) { + $additionalclasses[] = 'has-region-'.$region; + if ($this->page->blocks->region_has_content($region, $this)) { + $additionalclasses[] = 'used-region-'.$region; + $usedregions[] = $region; + } else { + $additionalclasses[] = 'empty-region-'.$region; + } + if ($this->page->blocks->region_completely_docked($region, $this)) { + $additionalclasses[] = 'docked-region-'.$region; + } + } + if (!$usedregions) { + // No regions means there is only content, add 'content-only' class. + $additionalclasses[] = 'content-only'; + } else if (count($usedregions) === 1) { + // Add the -only class for the only used region. + $region = array_shift($usedregions); + $additionalclasses[] = $region . '-only'; + } + foreach ($this->page->layout_options as $option => $value) { + if ($value) { + $additionalclasses[] = 'layout-option-'.$option; + } + } + $css = $this->page->bodyclasses .' '. join(' ', $additionalclasses); + return $css; + } + + /** + * Renders preferences groups. + * + * @param preferences_groups $renderable The renderable + * @return string The output. + */ + public function render_preferences_groups(preferences_groups $renderable) { + $html = ''; + $html .= html_writer::start_div('row-fluid'); + $html .= html_writer::start_tag('div', array('class' => 'span12 preferences-groups')); + $i = 0; + $open = false; + foreach ($renderable->groups as $group) { + if ($i == 0 || $i % 3 == 0) { + if ($open) { + $html .= html_writer::end_tag('div'); + } + $html .= html_writer::start_tag('div', array('class' => 'row-fluid')); + $open = true; + } + $html .= $this->render($group); + $i++; + } + + $html .= html_writer::end_tag('div'); + + $html .= html_writer::end_tag('ul'); + $html .= html_writer::end_tag('div'); + $html .= html_writer::end_div(); + return $html; + } + + /** + * Renders an action menu component. + * + * ARIA references: + * - http://www.w3.org/WAI/GL/wiki/Using_ARIA_menus + * - http://stackoverflow.com/questions/12279113/recommended-wai-aria-implementation-for-navigation-bar-menu + * + * @param action_menu $menu + * @return string HTML + */ + public function render_action_menu(action_menu $menu) { + $context = $menu->export_for_template($this); + return $this->render_from_template('core/action_menu', $context); + } + + /** + * Renders a single button widget. + * + * This will return HTML to display a form containing a single button. + * + * @param single_button $button + * @return string HTML fragment + */ + protected function render_single_button(single_button $button) { + $attributes = array('type' => 'submit', + 'value' => $button->label, + 'disabled' => $button->disabled ? 'disabled' : null, + 'title' => $button->tooltip); + + if ($button->actions) { + $id = html_writer::random_id('single_button'); + $attributes['id'] = $id; + foreach ($button->actions as $action) { + $this->add_action_handler($action, $id); + } + } + + // first the input element + $output = html_writer::empty_tag('input', $attributes); + + // then hidden fields + $params = $button->url->params(); + if ($button->method === 'post') { + $params['sesskey'] = sesskey(); + } + foreach ($params as $var => $val) { + $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val)); + } + + // then div wrapper for xhtml strictness + $output = html_writer::tag('div', $output); + + // now the form itself around it + if ($button->method === 'get') { + $url = $button->url->out_omit_querystring(true); // url without params, the anchor part allowed + } else { + $url = $button->url->out_omit_querystring(); // url without params, the anchor part not allowed + } + if ($url === '') { + $url = '#'; // there has to be always some action + } + $attributes = array('method' => $button->method, + 'action' => $url, + 'id' => $button->formid); + $output = html_writer::tag('form', $output, $attributes); + + // and finally one more wrapper with class + return html_writer::tag('div', $output, array('class' => $button->class)); + } } /** diff --git a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_course.php b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_course.php index 595d3f63652..9191059fa4a 100644 --- a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_course.php +++ b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_course.php @@ -258,7 +258,7 @@ class behat_theme_bootstrapbase_behat_course extends behat_course { public function i_navigate_to_course_participants() { $coursestr = behat_context_helper::escape(get_string('courses')); $mycoursestr = behat_context_helper::escape(get_string('mycourses')); - $xpath = "//div[contains(@class,'block')]//li[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]"; + $xpath = "//section[contains(@class,'block')]//li[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]"; $this->execute('behat_general::i_click_on_in_the', [get_string('participants'), 'link', $xpath, 'xpath_element']); } } diff --git a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php index 6e7e6a53525..30388fe4d0e 100644 --- a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php +++ b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php @@ -96,7 +96,7 @@ class behat_theme_bootstrapbase_behat_navigation extends behat_navigation { public function should_exist_in_current_page_administration($element, $selectortype) { $parentnodes = array_map('trim', explode('>', $element)); // Find the name of the first category of the administration block tree. - $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; + $xpath = '//section[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; $node = $this->find('xpath', $xpath); array_unshift($parentnodes, $node->getText()); $lastnode = array_pop($parentnodes); @@ -110,7 +110,7 @@ class behat_theme_bootstrapbase_behat_navigation extends behat_navigation { public function should_not_exist_in_current_page_administration($element, $selectortype) { $parentnodes = array_map('trim', explode('>', $element)); // Find the name of the first category of the administration block tree. - $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; + $xpath = '//section[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; $node = $this->find('xpath', $xpath); array_unshift($parentnodes, $node->getText()); $lastnode = array_pop($parentnodes); @@ -124,7 +124,7 @@ class behat_theme_bootstrapbase_behat_navigation extends behat_navigation { public function i_navigate_to_in_current_page_administration($nodetext) { $parentnodes = array_map('trim', explode('>', $nodetext)); // Find the name of the first category of the administration block tree. - $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; + $xpath = '//section[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span'; $node = $this->find('xpath', $xpath); array_unshift($parentnodes, $node->getText()); $lastnode = array_pop($parentnodes);