diff --git a/Changes.md b/Changes.md index 5555000..47e1637 100644 --- a/Changes.md +++ b/Changes.md @@ -1,3 +1,53 @@ +Change Log in version 403.1.5 (2023111806) +=========================================== +1. Reduce drawer flicker on page load when the navbar is sticky. +2. Fix 'itemid' not being regenerated for 'customjsfiles'. +3. Add ability to move custom menus into a single menu to save navbar space by giving 'custommenutitle' setting a value. + Note: At bottom of the 'Navbar' settings tab. +4. Improve custom menu code to facilitate simple inclusion of Font Awesome icons. +5. Fix dropdown menu id's. +6. Remember the tab currently selected when saving settings. +7. Functionality using a 'custom menu' such as 'Custom menu items', 'Header menus' and 'Tools menu' now supports the + following format: + text|url|title|langs|fontawesome classes or name|capability. + where: + text - Text to show for the entry. + url - URL of the entry. + title - Title attribute in the link tag value. + langs - Only show on the given language codes - separate more than one with a comma. + fontawesome classes or name - State the Font Awesome classes or name for the icon that is placed before the text. + capability - State the capability to check that the user has on the page before showing, for example + 'moodle/course:manageactivities' to only show to editing teachers. +8. Renamed 'Tools menu dropdown' setting tab to 'Tools menu'. +9. Add 'customusermenuitems' to the user menu. +10. Add FontAwesome support to 'customusermenuitems' as the third parameter, name or CSS classes. +11. Add duplicate entries for 'custommenuitems' and 'customusermenuitems' on a new 'Custom menu' settings tab. +12. Settings language strings tidy. +13. Social icons improvement to use either icon name or CSS classes. +14. Replace 'menuhovercolor' with 'menubkhovercolor' and 'menufonthovercolor' to fix menu hovering. +15. Add 'courseindexenabled' setting on the 'Course index' tab to enable / disable the 'Course Index'. +16. Fix header information overridden when set in the context header leading to missing output. +17. Add 'Information blocks'. This is designed to be a replacement for the marketing blocks that are + now deprecated. The code that they use has always been problematic and subject to contained file + based content being lost on occasion, or rather not lost but misplaced in terms of linkage at times. + I've never been able to entirely rectify or resolve the issues. Therefore instead of reinventing the + wheel, I've gone for a block region based solution whereby proven and tested block code is employed. + Instead of defining the content in the theme settings, you'll create blocks and place them in the + 'information' region on the front page. Of course you'll find all of the other settings in place + to control the layout and visiblity as before, on the theme's 'Information blocks' settings tab. + When editing you'll see the full block header with each block having the same width to allow easy + manipulation, i.e. the layout you set is not applied. Its only when editing is off that the layout + is applied and the block titles removed. If you see the word 'Overflow' then that means that you have + more blocks than you've defined in the layout rows to allow. Admins will see 'Marketing blocks are + deprecated, please migrate to using the Information block region.'. When editing, block regions will + have their name at the top. In testing with re-ordering the blocks I did find this problematic and + could not work out entiry why as the theme code is doing what it should do, the core code is telling + the backend via an AJAX call about the reordering, just that after doing so then it seemed that cron + had to run for the changes to become permanent. It is my intention that other settings employing the + same code as the marketing blocks will also migrate to this improved solution. +18. Improve the look of the calendar block controls and fix the next and previous icons. +19. Improve the layout builder to have new clearer responsive images. + Change Log in version 403.1.4 (2023111805) =========================================== 1. New setting 'mobileprimarynav' to control the display of the mobile primary navigation. This is found on the diff --git a/Support.md b/SupportAndSponsorship.md similarity index 74% rename from Support.md rename to SupportAndSponsorship.md index a084d99..392e849 100644 --- a/Support.md +++ b/SupportAndSponsorship.md @@ -13,11 +13,30 @@ If you would like to sponsor me, get support or fund improvements, then please d - GitHub | Please outline your issue / improvement on '[Adaptable issues](https://github.com/gjbarnard/moodle-theme_adaptable/issues)'. - @gjbarnard | [X](https://x.com/gjbarnard). -Bespoke work ------------- +Sponsorship information +----------------------- +By becoming a sponsor you help to keep the theme alive and working as underlying technologies change and the core Moodle software moves +forward. In return I’ll provide you access to additional functionality for the theme and prioritise questions from sponsors through a +sponsors only area on GitHub. + +Sponsorship is not support or a contract to provide such, nor is it a commitment to provide updates, future versions or improvements. +What it is though is an incentive to me to keep the theme going and maintained, and intend to do so for as long as possible. + +How much you offer for sponsorship is up to you to decide and for us to then agree upon based upon the use and value of the theme to you. +Sponsorship can be a one off payment or many payments over time, the choice is yours. + +Please contact me using the details above along with how much you'd like to offer and an outline of who you are and how much you +use the theme. I will then outline the steps that need to be taken so that you can become a sponsor. Note that I can only do +business to business transactions. + +Improvements and bespoke work +----------------------------- +Adaptable is already a complicated theme and therefore I am reluctant to add any major features, indeed my aim is to reduce the +complexity without actually removing functionality. There is though the possibility of improving what is already there and / or +creating a child theme that adds the specific functionality that you require. If you would like your own customised version or a new child theme of Boost / Classic, then typically with a setting and a few style -changes this is usually two to three hours work. At my current rate this will be between 88 and 132 GBP. I charge by the minute with +changes this is usually two to three hours work. At my current rate this will be between 94 and 141 GBP. I charge by the minute with a minimum time of fifteen. Please contact me for further details with an outline of what you would like. Open source software diff --git a/amd/build/admin_settings.min.js b/amd/build/admin_settings.min.js new file mode 100644 index 0000000..dca8977 --- /dev/null +++ b/amd/build/admin_settings.min.js @@ -0,0 +1,13 @@ +define("theme_adaptable/admin_settings",["exports","core/log"],(function(_exports,_log){var obj; +/** + * Admin settings. Go back to the current tab after save. + * + * @module theme_adaptable/admin_settings + * @copyright 2024 G J Barnard. + * @author G J Barnard - + * {@link https://moodle.org/user/profile.php?id=442195} + * {@link https://gjbarnard.co.uk} + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_log=(obj=_log)&&obj.__esModule?obj:{default:obj};_exports.init=()=>{const stickyTabs=()=>{const tabels=document.querySelectorAll('a[data-toggle="tab"]');if(tabels.length){const action=document.querySelector("form#adminsettings");tabels.forEach((tabel=>{tabel.addEventListener("click",(event=>{_log.default.debug("Tab clicky! "+event.target.href),action.setAttribute("action",event.target.href)}),!1)}))}};"loading"!==document.readyState?(_log.default.debug("Adaptable Admin settings JS DOM content already loaded"),stickyTabs()):(_log.default.debug("Adaptable Admin settings JS DOM content not loaded"),document.addEventListener("DOMContentLoaded",(function(){_log.default.debug("Adaptable Admin settings JS DOM content loaded"),stickyTabs()})))}})); + +//# sourceMappingURL=admin_settings.min.js.map \ No newline at end of file diff --git a/amd/build/admin_settings.min.js.map b/amd/build/admin_settings.min.js.map new file mode 100644 index 0000000..b249d7e --- /dev/null +++ b/amd/build/admin_settings.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin_settings.min.js","sources":["../src/admin_settings.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Admin settings. Go back to the current tab after save.\n *\n * @module theme_adaptable/admin_settings\n * @copyright 2024 G J Barnard.\n * @author G J Barnard -\n * {@link https://moodle.org/user/profile.php?id=442195}\n * {@link https://gjbarnard.co.uk}\n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.\n */\n\nimport log from 'core/log';\n\n/**\n * Initialise this module.\n */\nexport const init = () => {\n const stickyTabs = () => {\n const tabels = document.querySelectorAll('a[data-toggle=\"tab\"]');\n if (tabels.length) {\n const action = document.querySelector('form#adminsettings');\n tabels.forEach(tabel => {\n tabel.addEventListener(\n \"click\",\n (event) => {\n log.debug(\"Tab clicky! \" + event.target.href);\n action.setAttribute('action', event.target.href);\n },\n false,\n );\n });\n }\n };\n\n if (document.readyState !== 'loading') {\n log.debug(\"Adaptable Admin settings JS DOM content already loaded\");\n stickyTabs();\n } else {\n log.debug(\"Adaptable Admin settings JS DOM content not loaded\");\n document.addEventListener('DOMContentLoaded', function() {\n log.debug(\"Adaptable Admin settings JS DOM content loaded\");\n stickyTabs();\n });\n }\n};\n"],"names":["stickyTabs","tabels","document","querySelectorAll","length","action","querySelector","forEach","tabel","addEventListener","event","debug","target","href","setAttribute","readyState"],"mappings":";;;;;;;;;;kJA+BoB,WACVA,WAAa,WACTC,OAASC,SAASC,iBAAiB,2BACrCF,OAAOG,OAAQ,OACTC,OAASH,SAASI,cAAc,sBACtCL,OAAOM,SAAQC,QACXA,MAAMC,iBACF,SACCC,qBACOC,MAAM,eAAiBD,MAAME,OAAOC,MACxCR,OAAOS,aAAa,SAAUJ,MAAME,OAAOC,SAE/C,QAMY,YAAxBX,SAASa,yBACLJ,MAAM,0DACVX,4BAEIW,MAAM,sDACVT,SAASO,iBAAiB,oBAAoB,wBACtCE,MAAM,kDACVX"} \ No newline at end of file diff --git a/amd/build/bsoptions.min.js b/amd/build/bsoptions.min.js index 1c99fd9..efdbc61 100644 --- a/amd/build/bsoptions.min.js +++ b/amd/build/bsoptions.min.js @@ -1,4 +1,4 @@ // @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. -define("theme_adaptable/bsoptions",["jquery","theme_adaptable/util","core/log"],(function($,AdaptableUtil,log){return log.debug("Adaptable Bootstrap AMD opt in functions"),{init:function(data){if($(document).ready((function($){var body=$("body"),navbar=document.getElementById("main-navbar");if(data.stickynavbar){const screenmd=992,screensm=768;var currentWindowSize,windowWidth=$(window).width();currentWindowSize=windowWidth0&&void 0!==arguments[0]&&arguments[0];if(pageScrollTop=page.scrollTop,windowWidth=aboveHeaderHeight)return;pageScrollTop=aboveHeaderHeight,newHeaderTop=0,newPageMarginTop=0}else{if(!update&¤tPageScrollTop==headerNoNavbar&&pageScrollTop>=headerNoNavbar)return;update&&(headerHeight=header.getBoundingClientRect().height,null!==navbar&&(navbarHeight=navbar.getBoundingClientRect().height),headerNoNavbar=headerHeight-navbarHeight),pageScrollTop>headerNoNavbar&&(pageScrollTop=headerNoNavbar),newHeaderTop=-pageScrollTop,newPageMarginTop=headerHeight-pageScrollTop}if(currentPageScrollTop=pageScrollTop,(update||newHeaderTop!=headerTop)&&(header.style.top=newHeaderTop+"px",headerTop=newHeaderTop),(update||newPageMarginTop!=pageMarginTop)&&(page.style.marginTop=newPageMarginTop+"px",pageMarginTop=newPageMarginTop),(courseIndex||sidePost)&&(newDrawerPaddingTop=windowWidth0&&void 0!==arguments[0]&&arguments[0];if(pageScrollTop=page.scrollTop,windowWidth=aboveHeaderHeight)return;pageScrollTop=aboveHeaderHeight,newHeaderTop=0,newPageMarginTop=0}else{if(!update&¤tPageScrollTop==headerNoNavbar&&pageScrollTop>=headerNoNavbar)return;update&&(headerHeight=header.getBoundingClientRect().height,null!==navbar&&(navbarHeight=navbar.getBoundingClientRect().height),headerNoNavbar=headerHeight-navbarHeight),pageScrollTop>headerNoNavbar&&(pageScrollTop=headerNoNavbar),newHeaderTop=-pageScrollTop,newPageMarginTop=headerHeight-pageScrollTop}if(currentPageScrollTop=pageScrollTop,(update||newHeaderTop!=headerTop)&&(header.style.top=newHeaderTop+"px",headerTop=newHeaderTop),(update||newPageMarginTop!=pageMarginTop)&&(page.style.marginTop=newPageMarginTop+"px",pageMarginTop=newPageMarginTop),(courseIndex||sidePost)&&(newDrawerPaddingTop=windowWidth. + +/** + * Admin settings. Go back to the current tab after save. + * + * @module theme_adaptable/admin_settings + * @copyright 2024 G J Barnard. + * @author G J Barnard - + * {@link https://moodle.org/user/profile.php?id=442195} + * {@link https://gjbarnard.co.uk} + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. + */ + +import log from 'core/log'; + +/** + * Initialise this module. + */ +export const init = () => { + const stickyTabs = () => { + const tabels = document.querySelectorAll('a[data-toggle="tab"]'); + if (tabels.length) { + const action = document.querySelector('form#adminsettings'); + tabels.forEach(tabel => { + tabel.addEventListener( + "click", + (event) => { + log.debug("Tab clicky! " + event.target.href); + action.setAttribute('action', event.target.href); + }, + false, + ); + }); + } + }; + + if (document.readyState !== 'loading') { + log.debug("Adaptable Admin settings JS DOM content already loaded"); + stickyTabs(); + } else { + log.debug("Adaptable Admin settings JS DOM content not loaded"); + document.addEventListener('DOMContentLoaded', function() { + log.debug("Adaptable Admin settings JS DOM content loaded"); + stickyTabs(); + }); + } +}; diff --git a/amd/src/bsoptions.js b/amd/src/bsoptions.js index 60ba9d7..331b679 100644 --- a/amd/src/bsoptions.js +++ b/amd/src/bsoptions.js @@ -109,40 +109,6 @@ define(['jquery', 'theme_adaptable/util', 'core/log'], function($, AdaptableUtil body.removeClass("page-header-margin"); } - $(window).resize(function() { - windowWidth = $(window).width(); - if (windowWidth < screensm) { - if (currentWindowSize != 1) { - makeNavbarSticky(true); - currentWindowSize = 1; - } - } else if (windowWidth < screenmd) { - if (currentWindowSize != 2) { - makeNavbarSticky(true); - currentWindowSize = 2; - } - } else { - if (currentWindowSize != 3) { - currentWindowSize = 3; - } - // At screenmd and above, window width changes can change the height of the header. - makeNavbarSticky(true); - } - if (windowWidth < screenmd) { - if (isFixed === 0) { - header.classList.remove("sticky"); - body.addClass("page-header-margin"); - isFixed = 1; - } - } else { - if (isFixed === 1) { - header.classList.add("sticky"); - body.removeClass("page-header-margin"); - isFixed = 0; - } - } - }); - var makeNavbarSticky = function(update = false) { pageScrollTop = page.scrollTop; @@ -211,9 +177,49 @@ define(['jquery', 'theme_adaptable/util', 'core/log'], function($, AdaptableUtil } }; makeNavbarSticky(true); + if (courseIndex) { + courseIndex.classList.remove("d-none"); + } + if (sidePost) { + sidePost.classList.remove("d-none"); + } // When the user scrolls the page, execute makeNavbarSticky(). page.onscroll = function() {makeNavbarSticky();}; + + $(window).resize(function() { + windowWidth = $(window).width(); + if (windowWidth < screensm) { + if (currentWindowSize != 1) { + makeNavbarSticky(true); + currentWindowSize = 1; + } + } else if (windowWidth < screenmd) { + if (currentWindowSize != 2) { + makeNavbarSticky(true); + currentWindowSize = 2; + } + } else { + if (currentWindowSize != 3) { + currentWindowSize = 3; + } + // At screenmd and above, window width changes can change the height of the header. + makeNavbarSticky(true); + } + if (windowWidth < screenmd) { + if (isFixed === 0) { + header.classList.remove("sticky"); + body.addClass("page-header-margin"); + isFixed = 1; + } + } else { + if (isFixed === 1) { + header.classList.add("sticky"); + body.removeClass("page-header-margin"); + isFixed = 0; + } + } + }); } $('.moodlewidth').click(function() { diff --git a/classes/admin_settingspage.php b/classes/admin_settingspage.php index ee56c30..c45953d 100644 --- a/classes/admin_settingspage.php +++ b/classes/admin_settingspage.php @@ -50,8 +50,8 @@ public function __construct($name, $visiblename, $local = false, $reqcapability parent::__construct($name, $visiblename, $reqcapability, $hidden, $context); if (($local) && (\theme_adaptable\toolbox::get_local_toolbox() === false)) { $localadaptableheading = 'Sponsors only'; - $localadaptableheadingdesc = 'These settings and functionlity require the \'local_adaptable\' plugin'. - ', which is available to sponsors only. Please consider sponsoring, see the \'Information\' tab.'; + $localadaptableheadingdesc = 'These settings and functionlity are available to sponsors only, '. + 'please see the \'Information\' tab.'; $this->disabled = true; $this->add(new \admin_setting_heading( diff --git a/classes/output/core_renderer_layout.php b/classes/output/core_renderer_layout.php index b47c742..1d1fb46 100644 --- a/classes/output/core_renderer_layout.php +++ b/classes/output/core_renderer_layout.php @@ -218,34 +218,7 @@ public function yesheader($sidepostdrawer) { // Display user profile menu. // Only used when user is logged in and not on the secure layout. if ((isloggedin()) && ($this->page->pagelayout != 'secure')) { - // User icon. - $userpic = $this->user_picture($USER, ['link' => false, 'visibletoscreenreaders' => false, - 'size' => 35, 'class' => 'userpicture', ]); - // User name. - $username = format_string(fullname($USER)); - - // User menu dropdown. - if (!empty($themesettings->usernameposition)) { - $usernameposition = $themesettings->usernameposition; - if ($usernameposition == 'right') { - $usernamepositionleft = false; - } else { - $usernamepositionleft = true; - } - } else { - $usernamepositionleft = true; - } - - // Set template context. - $usermenucontext = [ - 'username' => $username, - 'userpic' => $userpic, - 'showusername' => $themesettings->showusername, - 'usernamepositionleft' => $usernamepositionleft, - 'userprofilemenu' => $this->user_profile_menu(), - ]; - $usermenu = $this->render_from_template('theme_adaptable/usermenu', $usermenucontext); - $headercontext['loginoruser'] = ''; + $headercontext['loginoruser'] = ''; } else { $headercontext['loginoruser'] = ''; } @@ -270,9 +243,7 @@ public function yesheader($sidepostdrawer) { // Navbar Menu. if ($shownavbar) { $headercontext['shownavbar'] = [ - 'disablecustommenu' => (!empty($themesettings->disablecustommenu)), 'navigationmenu' => $this->navigation_menu('main-navigation'), - 'navigationmenudrawer' => $this->navigation_menu('main-navigation-drawer'), 'output' => $this, 'toolsmenu' => $this->tools_menu(), ]; @@ -517,29 +488,37 @@ protected function head($bodyclasses) { */ protected function courseindexheader() { global $CFG; - require_once($CFG->dirroot . '/course/lib.php'); - $left = \theme_adaptable\toolbox::get_setting('blockside'); + $courseindex = \theme_adaptable\toolbox::get_setting('courseindexenabled'); - if (isloggedin()) { - $courseindexopen = (get_user_preferences('drawer-open-index', true) == true); - } else { - $courseindexopen = false; + if ($courseindex) { + require_once($CFG->dirroot . '/course/lib.php'); + $courseindex = core_course_drawer(); } - $courseindex = core_course_drawer(); - if (!$courseindex) { $courseindexopen = false; - } + $courseindexmarkup = ''; + $courseindextogglemarkup = ''; + } else { + if (isloggedin()) { + $courseindexopen = (get_user_preferences('drawer-open-index', true) == true); + } else { + $courseindexopen = false; + } - $templatecontext = [ - 'courseindexopen' => $courseindexopen, - 'courseindex' => $courseindex, - 'left' => $left, - ]; + $left = \theme_adaptable\toolbox::get_setting('blockside'); + $stickynavbar = \theme_adaptable\toolbox::get_setting('stickynavbar'); - $courseindexmarkup = $this->render_from_template('theme_adaptable/courseindex', $templatecontext); - $courseindextogglemarkup = $this->render_from_template('theme_adaptable/courseindextoggle', $templatecontext); + $templatecontext = [ + 'courseindexopen' => $courseindexopen, + 'courseindex' => $courseindex, + 'left' => $left, + 'stickynavbar' => $stickynavbar, + ]; + + $courseindexmarkup = $this->render_from_template('theme_adaptable/courseindex', $templatecontext); + $courseindextogglemarkup = $this->render_from_template('theme_adaptable/courseindextoggle', $templatecontext); + } return [$courseindexopen, $courseindex, $courseindexmarkup, $courseindextogglemarkup]; } @@ -549,6 +528,7 @@ protected function courseindexheader() { */ protected function sidepostheader() { $left = \theme_adaptable\toolbox::get_setting('blockside'); + $stickynavbar = \theme_adaptable\toolbox::get_setting('stickynavbar'); if (isloggedin()) { $sidepostopen = (get_user_preferences('drawer-open-block', true) == true); @@ -575,6 +555,7 @@ protected function sidepostheader() { 'left' => $left, 'sidepostopen' => $sidepostopen, 'sidepost' => $sideposthtml, + 'stickynavbar' => $stickynavbar, ]; $sideposttogglecontext = [ @@ -1221,6 +1202,11 @@ public function frontpage_layout() { echo ''; } + // If Information Blocks are enabled then let's show them. + if (!empty($themesettings->informationblocksenabled)) { + echo $this->get_flexible_blocks('information'); + } + // If Marketing Blocks are enabled then let's show them. if (!empty($themesettings->frontpagemarketenabled)) { echo $this->get_marketing_blocks(); diff --git a/classes/output/core_renderer_toolbox.php b/classes/output/core_renderer_toolbox.php index 5aff0be..88e8b22 100644 --- a/classes/output/core_renderer_toolbox.php +++ b/classes/output/core_renderer_toolbox.php @@ -31,7 +31,6 @@ use block_contents; use context_course; -use custom_menu; use custom_menu_item; use html_writer; use moodle_url; @@ -80,88 +79,343 @@ public function box_start($classes = 'generalbox', $id = null, $attributes = []) } /** - * Returns user profile menu + * Returns user profile menu items. + * + * returns array of objects suitable for adding to an action_menu as items. */ - public function user_profile_menu() { + protected function user_profile_menu_items() { global $CFG, $COURSE; - $retval = ''; + $retval = []; /* False or theme setting name to first array param (not all links have settings). + Entry type: link, divider or user. False or Moodle version number to second param (only some links check version). URL for link in third param. Link text in fourth parameter. Icon in fifth param. */ $usermenuitems = []; - $usermenuitems[] = ['enablemy', false, $CFG->wwwroot . '/my', get_string('myhome'), + $usermenuitems[] = ['enablemy', 'link', false, new moodle_url('/my'), get_string('myhome'), \theme_adaptable\toolbox::getfontawesomemarkup('dashboard', ['mr-1']), ]; - $usermenuitems[] = ['enableprofile', false, $CFG->wwwroot . '/user/profile.php', get_string('viewprofile'), + $usermenuitems[] = ['enableprofile', 'link', false, new moodle_url('/user/profile.php'), get_string('viewprofile'), \theme_adaptable\toolbox::getfontawesomemarkup('user', ['mr-1']), ]; - $usermenuitems[] = ['enableeditprofile', false, $CFG->wwwroot . '/user/edit.php', get_string('editmyprofile'), + $usermenuitems[] = ['enableeditprofile', 'link', false, new moodle_url('/user/edit.php'), get_string('editmyprofile'), \theme_adaptable\toolbox::getfontawesomemarkup('cog', ['mr-1']), ]; - $usermenuitems[] = ['enableaccesstool', false, $CFG->wwwroot . '/local/accessibilitytool/manage.php', + $usermenuitems[] = ['enableaccesstool', 'link', false, new moodle_url('/local/accessibilitytool/manage.php'), get_string('enableaccesstool', 'theme_adaptable'), \theme_adaptable\toolbox::getfontawesomemarkup('low-vision', ['mr-1']), ]; - $usermenuitems[] = ['enableprivatefiles', false, $CFG->wwwroot . '/user/files.php', + $usermenuitems[] = ['enableprivatefiles', 'link', false, new moodle_url('/user/files.php'), get_string('privatefiles', 'block_private_files'), \theme_adaptable\toolbox::getfontawesomemarkup('file', ['mr-1']), ]; if (\theme_adaptable\toolbox::kalturaplugininstalled()) { - $usermenuitems[] = [false, false, $CFG->wwwroot . '/local/mymedia/mymedia.php', + $usermenuitems[] = [false, 'link', false, new moodle_url('/local/mymedia/mymedia.php'), get_string('nav_mymedia', 'local_mymedia'), $this->pix_icon('my-media', '', 'local_mymedia'), ]; } - $usermenuitems[] = ['enablegrades', false, $CFG->wwwroot . '/grade/report/overview/index.php', get_string('grades'), + $usermenuitems[] = ['enablegrades', 'link', false, new moodle_url('/grade/report/overview/index.php'), get_string('grades'), \theme_adaptable\toolbox::getfontawesomemarkup('list-alt', ['mr-1']), ]; - $usermenuitems[] = ['enablebadges', false, $CFG->wwwroot . '/badges/mybadges.php', get_string('badges'), + $usermenuitems[] = ['enablebadges', 'link', false, new moodle_url('/badges/mybadges.php'), get_string('badges'), \theme_adaptable\toolbox::getfontawesomemarkup('certificate', ['mr-1']), ]; - $usermenuitems[] = ['enablepref', '2015051100', $CFG->wwwroot . '/user/preferences.php', get_string('preferences'), + $usermenuitems[] = ['enablepref', 'link', '2015051100', new moodle_url('/user/preferences.php'), get_string('preferences'), \theme_adaptable\toolbox::getfontawesomemarkup('cog', ['mr-1']), ]; - $usermenuitems[] = ['enablenote', false, $CFG->wwwroot . '/message/edit.php', get_string('notifications'), + $usermenuitems[] = ['enablenote', 'link', false, new moodle_url('/message/edit.php'), get_string('notifications'), \theme_adaptable\toolbox::getfontawesomemarkup('paper-plane', ['mr-1']), ]; - $usermenuitems[] = ['enableblog', false, $CFG->wwwroot . '/blog/index.php', get_string('enableblog', 'theme_adaptable'), + $usermenuitems[] = [false, 'divider']; + $usermenuitems[] = ['enableblog', 'link', false, new moodle_url('/blog/index.php'), get_string('enableblog', 'theme_adaptable'), \theme_adaptable\toolbox::getfontawesomemarkup('rss', ['mr-1']), ]; - $usermenuitems[] = ['enableposts', false, $CFG->wwwroot . '/mod/forum/user.php', + $usermenuitems[] = ['enableposts', 'link', false, new moodle_url('/mod/forum/user.php'), get_string('enableposts', 'theme_adaptable'), \theme_adaptable\toolbox::getfontawesomemarkup('commenting', ['mr-1']), ]; - $usermenuitems[] = ['enablefeed', false, $CFG->wwwroot . '/report/myfeedback/index.php', + $usermenuitems[] = ['enablefeed', 'link', false, new moodle_url('/report/myfeedback/index.php'), get_string('enablefeed', 'theme_adaptable'), \theme_adaptable\toolbox::getfontawesomemarkup('bullhorn', ['mr-1']), ]; - $usermenuitems[] = ['enablecalendar', false, $CFG->wwwroot . '/calendar/view.php', + $usermenuitems[] = ['enablecalendar', 'link', false, new moodle_url('/calendar/view.php'), get_string('pluginname', 'block_calendar_month'), \theme_adaptable\toolbox::getfontawesomemarkup('calendar', ['mr-1']), ]; - $returnurl = $this->page->url->out_as_local_url(false); - $context = context_course::instance($COURSE->id); - if ((!is_role_switched($COURSE->id)) && (has_capability('moodle/role:switchroles', $context))) { - $url = $CFG->wwwroot . '/course/switchrole.php?id=' . $COURSE->id . '&switchrole=-1&returnurl=' . $returnurl; - $usermenuitems[] = [false, false, $url, get_string('switchroleto'), - \theme_adaptable\toolbox::getfontawesomemarkup('user-o', ['mr-1']), ]; - } + // Custom user menu items postion. + $usermenuitems[] = [false, 'user']; + + // Return. if (is_role_switched($COURSE->id)) { - $url = $CFG->wwwroot . '/course/switchrole.php?id=' . $COURSE->id . '&sesskey=' . sesskey() . - '&switchrole=0&returnurl=' . $returnurl; - $usermenuitems[] = [false, false, $url, get_string('switchrolereturn'), + $returnurl = $this->page->url->out_as_local_url(false); + $url = new moodle_url('/course/switchrole.php', ['id' => $COURSE->id, 'sesskey' => sesskey(), + 'switchrole' => '0', 'returnurl' => $returnurl]); + $usermenuitems[] = [false, 'link', false, $url, get_string('switchrolereturn'), \theme_adaptable\toolbox::getfontawesomemarkup('user-o', ['mr-1']), ]; + } else { + $context = context_course::instance($COURSE->id); + if (has_capability('moodle/role:switchroles', $context)) { + $returnurl = $this->page->url->out_as_local_url(false); + $url = new moodle_url('/course/switchrole.php', ['id' => $COURSE->id, 'switchrole' => '-1', 'returnurl' => $returnurl]); + $usermenuitems[] = [false, 'link', false, $url, get_string('switchroleto'), + \theme_adaptable\toolbox::getfontawesomemarkup('user-o', ['mr-1']), ]; + } } - $usermenuitems[] = [false, false, $CFG->wwwroot . '/login/logout.php?sesskey=' . sesskey(), get_string('logout'), + $usermenuitems[] = [false, 'link', false, new moodle_url('/login/logout.php', ['sesskey' => sesskey()]), get_string('logout'), \theme_adaptable\toolbox::getfontawesomemarkup('sign-out', ['mr-1']), ]; - for ($i = 0; $i < count($usermenuitems); $i++) { - $additem = true; + foreach ($usermenuitems as $usermenuitem) { + switch($usermenuitem[1]) { + case 'link': + $additem = true; + + // If theme setting is specified in array but not enabled in theme settings do not add to menu. + if (!empty($usermenuitem[0])) { + $usermenuitemname = $usermenuitem[0]; + if (empty($this->page->theme->settings->$usermenuitemname)) { + $additem = false; + } + } - // If theme setting is specified in array but not enabled in theme settings do not add to menu. - $usermenuitem = $usermenuitems[$i][0]; - if (empty($this->page->theme->settings->$usermenuitem) && $usermenuitems[$i][0]) { - $additem = false; + // If item requires version number and moodle is below that version to not add to menu. + if ($usermenuitem[2] && $CFG->version < $usermenuitem[2]) { + $additem = false; + } + + if ($additem) { + $item = new stdClass; + $item->itemtype = 'link'; + $item->url = $usermenuitem[3]; + $item->title = $usermenuitem[5] . $usermenuitem[4]; + $retval[] = $item; + } + break; + case 'divider': + $item = new stdClass; + $item->itemtype = 'divider'; + $retval[] = $item; + break; + case 'user': + $customitems = $this->user_convert_text_to_menu_items($CFG->customusermenuitems); + if ($customitems[0]) { + $divider = new stdClass(); + $divider->itemtype = 'divider'; + $retval[] = $divider; + foreach ($customitems[1] as $item) { + $retval[] = $item; + } + $retval[] = $divider; + } + break; } + } + return $retval; + } - // If item requires version number and moodle is below that version to not add to menu. - if ($usermenuitems[$i][1] && $CFG->version < $usermenuitems[$i][1]) { - $additem = false; + /** + * Converts a string into a flat array of menu items, where each menu items is a + * stdClass with fields type, url, title. + * + * @param string $text the menu items definition + * @return array [hasitems - bool, items - array]. + */ + protected function user_convert_text_to_menu_items($text) { + $hasitems = false; + $lines = explode("\n", $text); + $children = []; + foreach ($lines as $line) { + $line = trim($line); + $bits = explode('|', $line, 3); + $itemtype = 'link'; + if (preg_match("/^#+$/", $line)) { + $itemtype = 'divider'; + } else if (!array_key_exists(0, $bits) || empty($bits[0])) { + // Every item must have a name to be valid. + continue; + } else { + $bits[0] = ltrim($bits[0], '-'); + } + + // Create the child. + $child = new stdClass(); + $child->itemtype = $itemtype; + if ($itemtype === 'divider') { + // Add the divider to the list of children and skip link processing. + $children[] = $child; + continue; + } + + // Name processing. + $namebits = explode(',', $bits[0], 2); + if (count($namebits) == 2) { + $namebits[1] = $namebits[1] ?: 'core'; + // Check the validity of the identifier part of the string. + if (clean_param($namebits[0], PARAM_STRINGID) !== '' && clean_param($namebits[1], PARAM_COMPONENT) !== '') { + // Treat this as a language string. + $child->title = get_string($namebits[0], $namebits[1]); + $child->titleidentifier = implode(',', $namebits); + } + } + if (empty($child->title)) { + // Use it as is, don't even clean it. + $child->title = $bits[0]; + $child->titleidentifier = str_replace(" ", "-", $bits[0]); + } + + // URL processing. + if (!array_key_exists(1, $bits) || empty($bits[1])) { + // Unlike core, if invaild then skip. + unset($child); + continue; + } else { + // Nasty hack to replace the grades with the direct url. + if (strpos($bits[1], '/grade/report/mygrades.php') !== false) { + $bits[1] = user_mygrades_url(); + } + + // Make sure the url is a moodle url. + $bits[1] = new moodle_url(trim($bits[1])); } + $child->url = $bits[1]; - if ($additem) { - $retval .= ''; - $retval .= $usermenuitems[$i][4] . $usermenuitems[$i][3] . ''; + // Font Awesome processing. + if (array_key_exists(2, $bits)) { + $fa = trim($bits[2]); + $child->title = \theme_adaptable\toolbox::getfontawesomemarkup($fa, ['mr-1']) . $child->title; } + + // Add this child to the list of children. + $children[] = $child; + $hasitems = true; } - return $retval; + return [$hasitems, $children]; + } + + /** + * Construct a user menu, returning HTML that can be echoed out by a + * layout file. + * + * @param stdClass $user A user object, usually $USER. + * @param bool $withlinks true if a dropdown should be built. + * @return string HTML fragment. + */ + public function user_menu($user = null, $withlinks = null) { + global $USER, $CFG; + require_once($CFG->dirroot . '/user/lib.php'); + + if (is_null($user)) { + $user = $USER; + } + + // Note: This behaviour is intended to match that of core_renderer::login_info, + // but should not be considered to be good practice; layout options are + // intended to be theme-specific. Please don't copy this snippet anywhere else. + if (is_null($withlinks)) { + $withlinks = empty($this->page->layout_options['nologinlinks']); + } + + // Add a class for when $withlinks is false. + $usermenuclasses = 'usermenu'; + if (!$withlinks) { + $usermenuclasses .= ' withoutlinks'; + } + + $returnstr = ""; + + // If during initial install, return the empty return string. + if (during_initial_install()) { + return $returnstr; + } + + // Adaptable modified. + $themesettings = \theme_adaptable\toolbox::get_settings(); + + $avatarclasses = "avatars"; + $userpic = $this->user_picture($user, ['link' => false, 'visibletoscreenreaders' => false, + 'size' => 35, 'class' => 'userpicture', ]); + $avatarcontents = html_writer::span($userpic, 'avatar current'); + $usertextcontents = format_string(fullname($user)); + + // User menu dropdown. + if (!empty($themesettings->usernameposition)) { + $usernameposition = $themesettings->usernameposition; + if ($usernameposition == 'right') { + $usernamepositionleft = false; + } else { + $usernamepositionleft = true; + } + } else { + $usernamepositionleft = true; + } + + if ($usernamepositionleft) { + $returnstr .= html_writer::span( + html_writer::span($usertextcontents, 'usertext mr-1') . + html_writer::span($avatarcontents, $avatarclasses), + 'userbutton' + ); + } else { + $returnstr .= html_writer::span( + html_writer::span($avatarcontents, $avatarclasses) . + html_writer::span($usertextcontents, 'usertext mr-1'), + 'userbutton' + ); + } + + $navitems = $this->user_profile_menu_items(); + + // Create a divider (well, a filler). + $divider = new \action_menu_filler(); + $divider->primary = false; + + $am = new \action_menu(); + $am->set_menu_trigger( + $returnstr, + 'nav-link' + ); + $am->set_action_label(get_string('usermenu')); + $am->set_nowrap_on_items(); + if ($withlinks) { + $navitemcount = count($navitems); + $idx = 0; + foreach ($navitems as $key => $value) { + + switch ($value->itemtype) { + case 'divider': + // If the nav item is a divider, add one and skip link processing. + $am->add($divider); + break; + + case 'invalid': + // Silently skip invalid entries (should we post a notification?). + break; + + case 'link': + // Process this as a link item. + $pix = null; + if (isset($value->pix) && !empty($value->pix)) { + $pix = new pix_icon($value->pix, '', null, ['class' => 'iconsmall']); + } else if (isset($value->imgsrc) && !empty($value->imgsrc)) { + $value->title = html_writer::img( + $value->imgsrc, + $value->title, + ['class' => 'iconsmall'] + ) . $value->title; + } + + $al = new \action_menu_link_secondary( + $value->url, + $pix, + $value->title, + ['class' => 'icon'] + ); + if (!empty($value->titleidentifier)) { + $al->attributes['data-title'] = $value->titleidentifier; + } + $am->add($al); + break; + } + + $idx++; + + // Add dividers after the first item and before the last item. + if ($idx == 1 || $idx == $navitemcount - 1) { + $am->add($divider); + } + } + } + + return html_writer::div( + $this->render($am), + $usermenuclasses + ); } /** @@ -213,19 +467,21 @@ public function block(block_contents $bc, $region) { $bc->add_class('mb-3'); if (empty($skiptitle)) { - $output = ''; - $skipdest = ''; - } else { - $output = html_writer::link( - '#sb-' . $bc->skipid, - get_string('skipa', 'access', $skiptitle), - ['class' => 'skip skip-block', 'id' => 'fsb-' . $bc->skipid] - ); - $skipdest = html_writer::span( - '', - 'skip-block-to', - ['id' => 'sb-' . $bc->skipid] - ); + $skiptitle = get_string('skipblock', 'theme_adaptable', $bc->blockinstanceid); + } + $output = html_writer::link( + '#sb-' . $bc->skipid, + get_string('skipa', 'access', $skiptitle), + ['class' => 'skip skip-block', 'id' => 'fsb-' . $bc->skipid] + ); + $skipdest = html_writer::span( + '', + 'skip-block-to', + ['id' => 'sb-' . $bc->skipid] + ); + + if (!empty($bc->attributes['notitle'])) { + $bc->title = ''; } $output .= html_writer::start_tag('section', $bc->attributes); @@ -570,25 +826,21 @@ protected function process_message($message) { * @return string */ public function socialicons() { - if (!isset($this->page->theme->settings->socialiconlist)) { + $socialiconlist = \theme_adaptable\toolbox::get_setting('socialiconlist'); + if (empty($socialiconlist)) { return ''; } - $target = '_blank'; - if (isset($this->page->theme->settings->socialtarget)) { - $target = $this->page->theme->settings->socialtarget; - } + $target = \theme_adaptable\toolbox::get_setting('socialtarget', false, null, '_blank'); $retval = ''; - - $socialiconlist = $this->page->theme->settings->socialiconlist; $lines = explode("\n", $socialiconlist); foreach ($lines as $line) { if (strstr($line, '|')) { $fields = explode('|', $line); $retval .= ''; - $retval .= ''; + $retval .= \theme_adaptable\toolbox::getfontawesomemarkup($fields[2]); $retval .= ''; } } @@ -777,16 +1029,185 @@ public function get_missing_block_regions($blocksarray, $classes = [], $displaya } /** - * Renders marketing blocks on front page + * Get the HTML for block title in the given region. + * + * @param string $region The region to get HTML for. + * + * @return string HTML. + */ + protected function block_region_title($region) { + return html_writer::tag( + 'p', + get_string('region-' . $region, 'theme_adaptable'), + ['class' => 'block-region-title col-12 text-center font-italic font-weight-bold'] + ); + } + + /** + * Renders flexible blocks on front page. + * + * @param string $region + * @param string $layoutrow + * @param string $settingname + * @param array $classes + * @param string $tag + * @return string Markup. + */ + public function get_flexible_blocks( + $region, + $layoutrow = 'informationblockslayoutrow', + $settingname = 'information', + $classes = [], + $tag = 'aside') { + $editing = $this->page->user_is_editing(); + $themesettings = \theme_adaptable\toolbox::get_settings(); + + if (!$editing) { + $visiblestate = 3; + if (!empty($themesettings->informationblocksvisible)) { + $visiblestate = $themesettings->informationblocksvisible; + } + if ($visiblestate != 3) { + $loggedin = isloggedin(); + if ((($visiblestate == 1) && ($loggedin)) || (($visiblestate == 2) && (!$loggedin))) { + return ''; + } + } + } + + $content = ''; + $classes = (array)$classes; + $classes[] = 'block-region'; + + if ($editing) { + $content .= $this->block_region_title($region); + $classes[] = 'editing-flexible-blocks'; + } + + $attributes = [ + 'id' => 'block-region-' . $region, + 'class' => join(' ', $classes), + 'data-blockregion' => $region, + 'data-droptarget' => '1', + ]; + + if ($this->page->blocks->region_has_content($region, $this)) { + $content .= html_writer::tag('h2', get_string('blocks'), ['class' => 'sr-only']); + + $blockcontents = $this->page->blocks->get_content_for_region($region, $this); + $lastblock = null; + $zones = []; + foreach ($blockcontents as $bc) { + if ($bc instanceof block_contents) { + $zones[] = $bc->title; + } + } + + if (!$editing) { + $blockrows = []; + $blocksequence = []; + $blocksequencecount = 0; + $blockspacescount = 0; + + $content .= '
'; + + for ($i = 1; $i <= 5; $i++) { + $blockrowsetting = $layoutrow . $i; + $blockrowvalue = $themesettings->$blockrowsetting; + if ($blockrowvalue != '0-0-0-0') { + $blockrows[] = $blockrowvalue; + } + } + + foreach ($blockrows as $blockrow) { + $blocksequence[] = '+'; // Row start. + $vals = explode('-', $blockrow); + foreach ($vals as $val) { + if ($val > 0) { + $blocksequence[] = $val; // Block width. + $blockspacescount++; + } + } + $blocksequence[] = '-'; // Row end. + } + } + + $blockspacesexceeded = false; + $blockcount = 0; + foreach ($blockcontents as $bc) { + if ($bc instanceof block_contents) { + if (!$editing) { + if (!empty($blocksequence[$blocksequencecount])) { + if ($blocksequence[$blocksequencecount] == '+') { + $content .= '
'; + $blocksequencecount++; + } + $bc->attributes['class'] .= ' col-'.$blocksequence[$blocksequencecount]; // Will be a number. + } else { + if ((!$blockspacesexceeded) && ($blockcount >= $blockspacescount)) { + $blockspacesexceeded = true; + html_writer::tag( + 'p', + get_string('flexibleblocksoverflow', 'theme_adaptable'), + ['class' => 'block-region-overflow col-12 text-center font-italic font-weight-bold'] + ); + $content .= '
'; + if (is_siteadmin()) { + $content .= html_writer::tag( + 'p', + get_string('flexibleblocksoverflow', 'theme_adaptable'), + ['class' => 'block-region-overflow col-12 text-center font-italic font-weight-bold'] + ); + } + } + $bc->attributes['class'] .= ' col-4'; + } + $bc->attributes['notitle'] = true; + } + $content .= $this->block($bc, $region); + $lastblock = $bc->title; + $blockcount++; + if ((!$editing) && (!$blockspacesexceeded)) { + $blocksequencecount++; + // Could be a end of row next. + if ($blocksequence[$blocksequencecount] == '-') { + $content .= '
'; + $blocksequencecount++; + } + } + } else if ($bc instanceof block_move_target) { + $content .= $this->block_move_target($bc, $zones, $lastblock, $region); + } else { + throw new coding_exception( + 'Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.'); + } + } + + if (!$editing) { + if ($blockspacesexceeded) { + $content .= '
'; // End of flexible-blocks-overflow. + } + $content .= '
'; // End of container. + } + } else { + $content .= html_writer::tag('h2', get_string('blocks'), ['class' => 'sr-only']); + } + + return html_writer::tag($tag, $content, $attributes); + } + + /** + * Renders marketing blocks on front page. * * @param string $layoutrow * @param string $settingname * @return string Markup. */ public function get_marketing_blocks($layoutrow = 'marketlayoutrow', $settingname = 'market') { + $themesettings = \theme_adaptable\toolbox::get_settings(); $visiblestate = 3; - if (!empty($this->page->theme->settings->marketingvisible)) { - $visiblestate = $this->page->theme->settings->marketingvisible; + if (!empty($themesettings->marketingvisible)) { + $visiblestate = $themesettings->marketingvisible; } if ($visiblestate != 3) { $loggedin = isloggedin(); @@ -798,13 +1219,21 @@ public function get_marketing_blocks($layoutrow = 'marketlayoutrow', $settingnam $fields = []; $blockcount = 0; - $extramarketclass = $this->page->theme->settings->frontpagemarketoption; + $extramarketclass = $themesettings->frontpagemarketoption; $retval = '
'; + if (is_siteadmin()) { + $retval .= html_writer::tag( + 'p', + get_string('marketingdeprecated', 'theme_adaptable'), + ['class' => 'marketing-deprecated col-12 text-center font-italic font-weight-bold'] + ); + } + for ($i = 1; $i <= 5; $i++) { $marketrow = $layoutrow . $i; - $marketrow = $this->page->theme->settings->$marketrow; + $marketrow = $themesettings->$marketrow; if ($marketrow != '0-0-0-0') { $fields[] = $marketrow; } @@ -818,7 +1247,7 @@ public function get_marketing_blocks($layoutrow = 'marketlayoutrow', $settingnam $retval .= '
'; $blockcount++; $fieldname = $settingname . $blockcount; - if (isset($this->page->theme->settings->$fieldname)) { + if (isset($themesettings->$fieldname)) { // Add HTML format. $retval .= \theme_adaptable\toolbox::get_setting($fieldname, 'format_moodle'); } @@ -1180,7 +1609,7 @@ protected static function timeaccesscompare($a, $b) { * @return menu object. */ public function navigation_menu_content() { - global $COURSE; + global $CFG, $COURSE; $menu = new custom_menu(); $access = true; @@ -1207,7 +1636,7 @@ public function navigation_menu_content() { $branchlabel = ''; $branchtitle = get_string('home', 'theme_adaptable'); if ($navbardisplayicons) { - $branchlabel .= ''; + $branchlabel .= \theme_adaptable\toolbox::getfontawesomemarkup('home', ['fa-lg', 'mr-1']); } $branchlabel .= $branchtitle; @@ -1223,7 +1652,7 @@ public function navigation_menu_content() { $branchlabel = ''; $branchtitle = get_string('myhome'); if ($navbardisplayicons) { - $branchlabel .= ''; + $branchlabel .= \theme_adaptable\toolbox::getfontawesomemarkup('dashboard', ['fa-lg', 'mr-1']); } $branchlabel .= $branchtitle; $branchurl = new moodle_url('/my/index.php'); @@ -1235,7 +1664,7 @@ public function navigation_menu_content() { $branchlabel = ''; $branchtitle = get_string('courses'); if ($navbardisplayicons) { - $branchlabel .= ''; + $branchlabel .= \theme_adaptable\toolbox::getfontawesomemarkup('th', ['fa-lg', 'mr-1']); } $branchlabel .= $branchtitle; $branchurl = new moodle_url('/my/courses.php'); @@ -1247,7 +1676,7 @@ public function navigation_menu_content() { $branchlabel = ''; $branchtitle = get_string('events', 'theme_adaptable'); if ($navbardisplayicons) { - $branchlabel .= ''; + $branchlabel .= \theme_adaptable\toolbox::getfontawesomemarkup('calendar', ['fa-lg', 'mr-1']); } $branchlabel .= $branchtitle; @@ -1302,7 +1731,7 @@ public function navigation_menu_content() { $branchtitle = get_string('thiscourse', 'theme_adaptable'); if ($navbardisplayicons) { $branchlabel .= - \theme_adaptable\toolbox::getfontawesomemarkup('sitemap', ['fa-lg']) . ''; + \theme_adaptable\toolbox::getfontawesomemarkup('sitemap', ['mr-1', 'fa-lg']) . ''; } $branchlabel .= $branchtitle; if ($navbardisplayicons) { @@ -1329,7 +1758,7 @@ public function navigation_menu_content() { $branchtitle = get_string('people', 'theme_adaptable'); $branchlabel = \theme_adaptable\toolbox::getfontawesomemarkup( 'users', - ['icon', 'mr-2'], + ['icon', 'mr-1'], [], '', $branchtitle @@ -1401,7 +1830,7 @@ public function navigation_menu_content() { } if ($navbardisplayicons) { - $helpicon = ''; + $helpicon = \theme_adaptable\toolbox::getfontawesomemarkup('life-ring', ['fa-lg']); } else { $helpicon = ''; } @@ -1441,6 +1870,36 @@ public function navigation_menu_content() { } } + // Custom menu. + if ((!empty($CFG->custommenuitems)) && + (empty($this->page->theme->settings->disablecustommenu))) { + $custommenutitle = \theme_adaptable\toolbox::get_setting('custommenutitle', 'format_plain'); + $branch = null; + if (!empty($custommenutitle)) { + $branchlabel = ''; + $branchtitle = $custommenutitle; + if ($navbardisplayicons) { + $branchlabel .= + \theme_adaptable\toolbox::getfontawesomemarkup('bars', ['mr-1', 'fa-lg']) . ''; + } + $branchlabel .= $branchtitle; + if ($navbardisplayicons) { + $branchlabel .= ''; + } + + // Check the option of displaying a sub-menu arrow symbol. + if (!empty($this->page->theme->settings->navbardisplaysubmenuarrow)) { + $branchlabel .= \theme_adaptable\toolbox::getfontawesomemarkup('caret-down', ['ml-1']); + } + + $branchurl = $this->page->url; + $branchsort++; + $branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort); + } + + $menu->add_custom_menu_items($CFG->custommenuitems, current_language(), $branch); + } + return $menu; } @@ -1707,16 +2166,17 @@ protected function get_course_title() { } /** - * Currently not called, but will leave for reference! - * - * @param array $headerinfo Array of things, see parent. - * @param int $headinglevel Heading level, see parent. + * Renders the context header for the page. * - * @return string Markup. + * @param array $headerinfo Heading information. + * @param int $headinglevel What 'h' level to make the heading. + * @return string A rendered context header. */ public function context_header($headerinfo = null, $headinglevel = 1): string { - $headerinfo = []; - $headerinfo['heading'] = $this->get_course_title(); + if (empty($headerinfo)) { + $headerinfo = []; + $headerinfo['heading'] = $this->get_course_title(); + } return parent::context_header($headerinfo, $headinglevel); } @@ -2164,8 +2624,7 @@ public function custom_menu_drawer() { } /** - * This renders the bootstrap top menu. - * This renderer is needed to enable the Bootstrap style navigation. + * Render custom menu. * * @param custom_menu $menu * @param string $wrappre @@ -2174,7 +2633,7 @@ public function custom_menu_drawer() { * * @return string */ - public function render_custom_menu(custom_menu $menu, $wrappre = '', $wrappost = '', $menuid = '') { + public function render_custom_menu(\custom_menu $menu, $wrappre = '', $wrappost = '', $menuid = '') { if (!$menu->has_children()) { return ''; } @@ -2188,6 +2647,7 @@ public function render_custom_menu(custom_menu $menu, $wrappre = '', $wrappost = } } $content = $wrappre . $content . $wrappost; + return $content; } @@ -2210,6 +2670,7 @@ protected function render_custom_menu_item(custom_menu_item $menunode, $level = $url = '#'; } if ($menunode->has_children()) { + $submenucount++; $content = '