From 99061152af3976dc276777463328a1295a65d4cb Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 6 Oct 2016 14:10:14 +0800 Subject: [PATCH] MDL-55074 theme_boost: Navigation and blocks This patch includes a big set of changes that are all designed to work together to provide a better way to navigate in the new theme, and a different way of working with blocks. Blocks have been moved to a "drawer" that can be opened and closed (this is remembered in a user pref). A new "flat navigation" element is also available in a drawer - which should let you do 90% of things without needing to open the "blocks" drawer. The flat navigation is build from specific parts of the nav tree - the top nodes like "calendar, dashboard" are hand picked. There is a mycourses node listing your enrolled courses. There is a node for the current course, built from the top nodes in the current course node in the nav tree. Administrators have a link to the Site admin settings here too. These nav elements are used by the templates for the new theme, which also has a resigned layout for login and signup. There have also been some additional fixes / improvements to the scss for the new theme which goes along with these layout changes. This set of changes is a collaboration between Martin, Damyon and Alberto (thanks!). --- admin/admin_settings_search_form.php | 48 ++ admin/search.php | 16 +- admin/settings/top.php | 2 +- course/admin.php | 48 ++ course/format/topics/lib.php | 3 - course/format/weeks/lib.php | 3 - course/lib.php | 23 + course/switchrole.php | 21 +- course/switchrole_form.php | 96 ++++ course/tests/externallib_test.php | 7 +- lang/en/admin.php | 1 + lang/en/block.php | 2 + lib/navigationlib.php | 415 ++++++++++++++++-- lib/outputcomponents.php | 9 + lib/outputrenderers.php | 24 +- lib/pagelib.php | 17 + lib/templates/settings_link_page.mustache | 43 ++ .../settings_link_page_single.mustache | 39 ++ lib/templates/signup_form_layout.mustache | 2 + login/signup.php | 5 +- login/signup_form.php | 19 +- theme/boost/amd/build/blocks_drawer.min.js | Bin 0 -> 1591 bytes theme/boost/amd/build/drawer.min.js | Bin 0 -> 2022 bytes theme/boost/amd/src/drawer.js | 139 ++++++ theme/boost/classes/output/core_renderer.php | 156 ++++++- theme/boost/config.php | 2 +- theme/boost/layout/columns1.php | 24 +- theme/boost/layout/columns2.php | 21 +- theme/boost/layout/login.php | 36 ++ theme/boost/layout/secure.php | 3 +- theme/boost/pix/blocksdrawer.png | Bin 0 -> 785 bytes theme/boost/pix/blocksdrawer.svg | 22 + theme/boost/scss/moodle.scss | 3 + theme/boost/scss/moodle/blocks.scss | 49 +++ theme/boost/scss/moodle/bootswatch.scss | 10 + theme/boost/scss/moodle/core.scss | 67 ++- theme/boost/scss/moodle/course.scss | 7 + theme/boost/scss/moodle/drawer.scss | 75 ++++ theme/boost/scss/moodle/icons.scss | 4 + theme/boost/scss/moodle/login.scss | 8 + theme/boost/scss/preset-default.scss | 41 +- theme/boost/templates/blocks-drawer.mustache | 3 + theme/boost/templates/columns1.mustache | 25 +- theme/boost/templates/columns2.mustache | 26 +- theme/boost/templates/core/login.mustache | 146 +++--- theme/boost/templates/core/navbar.mustache | 4 +- .../core/signup_form_layout.mustache | 17 + .../boost/templates/flat_navigation.mustache | 19 + theme/boost/templates/header.mustache | 36 +- theme/boost/templates/login.mustache | 44 ++ theme/boost/templates/nav-drawer.mustache | 3 + theme/boost/templates/secure.mustache | 7 +- 52 files changed, 1600 insertions(+), 240 deletions(-) create mode 100644 admin/admin_settings_search_form.php create mode 100644 course/admin.php create mode 100644 course/switchrole_form.php create mode 100644 lib/templates/settings_link_page.mustache create mode 100644 lib/templates/settings_link_page_single.mustache create mode 100644 lib/templates/signup_form_layout.mustache create mode 100644 theme/boost/amd/build/blocks_drawer.min.js create mode 100644 theme/boost/amd/build/drawer.min.js create mode 100644 theme/boost/amd/src/drawer.js create mode 100644 theme/boost/layout/login.php create mode 100644 theme/boost/pix/blocksdrawer.png create mode 100644 theme/boost/pix/blocksdrawer.svg create mode 100644 theme/boost/scss/moodle/bootswatch.scss create mode 100644 theme/boost/scss/moodle/drawer.scss create mode 100644 theme/boost/scss/moodle/login.scss create mode 100644 theme/boost/templates/blocks-drawer.mustache create mode 100644 theme/boost/templates/core/signup_form_layout.mustache create mode 100644 theme/boost/templates/flat_navigation.mustache create mode 100644 theme/boost/templates/login.mustache create mode 100644 theme/boost/templates/nav-drawer.mustache diff --git a/admin/admin_settings_search_form.php b/admin/admin_settings_search_form.php new file mode 100644 index 00000000000..ad42300b853 --- /dev/null +++ b/admin/admin_settings_search_form.php @@ -0,0 +1,48 @@ +. + +/** + * Admin settings search form + * + * @package admin + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once $CFG->libdir.'/formslib.php'; + +/** + * Admin settings search form + * + * @package admin + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_settings_search_form extends moodleform { + function definition () { + $mform = $this->_form; + + //$mform->addElement('header', 'settingsheader', get_string('search', 'admin')); + $elements = []; + $elements[] = $mform->createElement('text', 'query', get_string('query', 'admin')); + $elements[] = $mform->createElement('submit', 'search', get_string('search')); + $mform->addGroup($elements); + $mform->setType('query', PARAM_RAW); + $mform->setDefault('query', optional_param('query', '', PARAM_RAW)); + } +} diff --git a/admin/search.php b/admin/search.php index 6c255c6d336..0e9a531944e 100644 --- a/admin/search.php +++ b/admin/search.php @@ -36,6 +36,8 @@ if ($data = data_submitted() and confirm_sesskey()) { // to modify them echo $OUTPUT->header($focus); +echo $OUTPUT->heading(get_string('administrationsite')); + if ($errormsg !== '') { echo $OUTPUT->notification($errormsg); @@ -43,6 +45,18 @@ if ($errormsg !== '') { echo $OUTPUT->notification($statusmsg, 'notifysuccess'); } -echo admin_search_settings_html($query); +require_once("admin_settings_search_form.php"); +$form = new admin_settings_search_form(); +$form->display(); +echo '
'; + +if ($query) { + echo admin_search_settings_html($query); +} else { + $node = $PAGE->settingsnav->find('root', navigation_node::TYPE_SITE_ADMIN); + if ($node) { + echo $OUTPUT->render_from_template('core/settings_link_page', ['node' => $node]); + } +} echo $OUTPUT->footer(); diff --git a/admin/settings/top.php b/admin/settings/top.php index 223af0cfc59..59a6102e9ae 100644 --- a/admin/settings/top.php +++ b/admin/settings/top.php @@ -47,4 +47,4 @@ $ADMIN->add('root', new admin_category('development', new lang_string('developme $ADMIN->add('root', new admin_category('unsupported', new lang_string('unsupported', 'admin'), true)); // hidden search script -$ADMIN->add('root', new admin_externalpage('search', new lang_string('searchresults'), "$CFG->wwwroot/$CFG->admin/search.php", 'moodle/site:config', true)); +$ADMIN->add('root', new admin_externalpage('search', new lang_string('search', 'admin'), "$CFG->wwwroot/$CFG->admin/search.php", 'moodle/site:config', true)); diff --git a/course/admin.php b/course/admin.php new file mode 100644 index 00000000000..b89b382332e --- /dev/null +++ b/course/admin.php @@ -0,0 +1,48 @@ +. + +/** + * Listing of the course administration pages for this course. + * + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once("../config.php"); + +$courseid = required_param('courseid', PARAM_INT); + +$PAGE->set_url('/course/admin.php', array('courseid'=>$courseid)); + +$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); + +require_login($course); +$context = context_course::instance($course->id); + +$PAGE->set_pagelayout('incourse'); +$PAGE->set_title(get_string('courseadministration')); +$PAGE->set_heading($course->fullname); +$PAGE->navbar->add(get_string('courseadministration')); +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('courseadministration')); + +$node = $PAGE->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE); +if ($node) { + echo $OUTPUT->render_from_template('core/settings_link_page', ['node' => $node]); +} + +echo $OUTPUT->footer(); diff --git a/course/format/topics/lib.php b/course/format/topics/lib.php index da48e81017c..4f01d7db5e6 100644 --- a/course/format/topics/lib.php +++ b/course/format/topics/lib.php @@ -121,9 +121,6 @@ class format_topics extends format_base { if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) { $url->param('section', $sectionno); } else { - if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) { - return null; - } $url->set_anchor('section-'.$sectionno); } } diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php index d32664cc8a7..db096788708 100644 --- a/course/format/weeks/lib.php +++ b/course/format/weeks/lib.php @@ -124,9 +124,6 @@ class format_weeks extends format_base { if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) { $url->param('section', $sectionno); } else { - if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) { - return null; - } $url->set_anchor('section-'.$sectionno); } } diff --git a/course/lib.php b/course/lib.php index e731709b266..8d89b8dd804 100644 --- a/course/lib.php +++ b/course/lib.php @@ -3510,6 +3510,29 @@ function course_get_user_navigation_options($context, $course = null) { $options->participants = has_capability('moodle/course:viewparticipants', $context); $options->badges = !empty($CFG->enablebadges) && !empty($CFG->badges_allowcoursebadges) && has_capability('moodle/badges:viewbadges', $context); + // Add view grade report is permitted. + $grades = false; + if (has_capability('moodle/grade:viewall', $context)) { + $grades = true; + } else if (!empty($course->showgrades)) { + $reports = core_component::get_plugin_list('gradereport'); + if (is_array($reports) && count($reports) > 0) { // Get all installed reports. + arsort($reports); // User is last, we want to test it first. + foreach ($reports as $plugin => $plugindir) { + if (has_capability('gradereport/'.$plugin.':view', $context)) { + // Stop when the first visible plugin is found. + $grades = true; + break; + } + } + } + } + $options->grades = $grades; + } + + if (\core_competency\api::is_enabled()) { + $capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage'); + $options->competencies = has_any_capability($capabilities, $context); } return $options; } diff --git a/course/switchrole.php b/course/switchrole.php index a682c37a842..4e7c2379a28 100644 --- a/course/switchrole.php +++ b/course/switchrole.php @@ -46,9 +46,11 @@ if (strpos($returnurl, '?') === false) { $returnurl = clean_param($returnurl, PARAM_URL); } -$PAGE->set_url('/course/switchrole.php', array('id'=>$id)); +$PAGE->set_url('/course/switchrole.php', array('id'=>$id, 'switchrole'=>$switchrole)); -require_sesskey(); +if ($switchrole >= 0) { + require_sesskey(); +} if (!$course = $DB->get_record('course', array('id'=>$id))) { redirect(new moodle_url('/')); @@ -70,6 +72,21 @@ if ($switchrole > 0 && has_capability('moodle/role:switchroles', $context)) { if (is_array($aroles) && isset($aroles[$switchrole])) { role_switch($switchrole, $context); } +} else if ($switchrole < 0) { + + $PAGE->set_title(get_string('switchroleto')); + $PAGE->set_heading($course->fullname); + $PAGE->set_pagelayout('incourse'); + + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('switchroleto')); + + require_once($CFG->dirroot.'/course/switchrole_form.php'); + $form = new switchrole_form(null, ['course' => $course]); + $form->display(); + + echo $OUTPUT->footer(); + exit; } redirect($returnurl); diff --git a/course/switchrole_form.php b/course/switchrole_form.php new file mode 100644 index 00000000000..57886564c04 --- /dev/null +++ b/course/switchrole_form.php @@ -0,0 +1,96 @@ +. + +/** + * Switch roles form. + * + * @package core_course + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/formslib.php'); + +/** + * Defines the course completion settings form. + */ +class switchrole_form extends moodleform { + + /** + * Determine whether the user is assuming another role + * + * This function checks to see if the user is assuming another role by means of + * role switching. In doing this we compare each RSW key (context path) against + * the current context path. This ensures that we can provide the switching + * options against both the course and any page shown under the course. + * + * @param context $context + * @return bool|int The role(int) if the user is in another role, false otherwise + */ + protected function in_alternative_role($context) { + global $USER, $PAGE; + if (!empty($USER->access['rsw']) && is_array($USER->access['rsw'])) { + if (!empty($PAGE->context) && !empty($USER->access['rsw'][$PAGE->context->path])) { + return $USER->access['rsw'][$PAGE->context->path]; + } + foreach ($USER->access['rsw'] as $key=>$role) { + if (strpos($context->path, $key)===0) { + return $role; + } + } + } + return false; + } + + /** + * Defines the form fields. + */ + public function definition() { + global $USER, $CFG, $DB; + + $mform = $this->_form; + $course = $this->_customdata['course']; + + // Overall criteria aggregation. + $context = context_course::instance($course->id); + $roles = array(); + $assumedrole = -1; + if (is_role_switched($course->id)) { + $roles[0] = get_string('switchrolereturn'); + $assumedrole = $USER->access['rsw'][$context->path]; + } + $availableroles = get_switchable_roles($context); + if (is_array($availableroles)) { + foreach ($availableroles as $key=>$role) { + if ($assumedrole == (int)$key) { + continue; + } + $roles[$key] = $role; + } + } + $mform->addElement('select', 'switchrole', get_string('role'), $roles); + + // Add common action buttons. + $this->add_action_buttons(); + + // Add hidden fields. + $mform->addElement('hidden', 'id', $course->id); + $mform->setType('id', PARAM_INT); + } +} diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index 9015dbe654a..0e07208d5a4 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -1796,7 +1796,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $navoptions->{$option['name']} = $option['available']; } if ($course['id'] == SITEID) { - $this->assertCount(7, $course['options']); + $this->assertCount(8, $course['options']); $this->assertTrue($navoptions->blogs); $this->assertFalse($navoptions->notes); $this->assertFalse($navoptions->participants); @@ -1804,12 +1804,15 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertTrue($navoptions->tags); $this->assertFalse($navoptions->search); $this->assertTrue($navoptions->calendar); + $this->assertTrue($navoptions->competencies); } else { - $this->assertCount(4, $course['options']); + $this->assertCount(6, $course['options']); $this->assertTrue($navoptions->blogs); $this->assertFalse($navoptions->notes); $this->assertTrue($navoptions->participants); $this->assertTrue($navoptions->badges); + $this->assertTrue($navoptions->grades); + $this->assertTrue($navoptions->competencies); } } } diff --git a/lang/en/admin.php b/lang/en/admin.php index 0f833aca334..50cae402f68 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -889,6 +889,7 @@ $string['proxypassword'] = 'Proxy password'; $string['proxyport'] = 'Proxy port'; $string['proxytype'] = 'Proxy type'; $string['proxyuser'] = 'Proxy username'; +$string['query'] = 'Query'; $string['question'] = 'Question'; $string['questionbehaviours'] = 'Question behaviours'; $string['questioncwqpfscheck'] = 'One or more \'random\' questions in a quiz are set up to select questions from a mixture of shared and unshared question categories. There is a more detailed report here and see Moodle Docs page here.'; diff --git a/lang/en/block.php b/lang/en/block.php index 7b318595184..ad04aef94cc 100644 --- a/lang/en/block.php +++ b/lang/en/block.php @@ -26,6 +26,7 @@ $string['addtodock'] = 'Move this to the dock'; $string['anypagematchingtheabove'] = 'Any page matching the above'; $string['appearsinsubcontexts'] = 'Appears in sub-contexts'; $string['assignrolesinblock'] = 'Assign roles in {$a} block'; +$string['blocksdrawertoggle'] = 'Hide/show blocks drawer'; $string['blocksettings'] = 'Block settings'; $string['bracketfirst'] = '{$a} (first)'; $string['bracketlast'] = '{$a} (last)'; @@ -51,6 +52,7 @@ $string['moveblockafter'] = 'Move block to after {$a} block'; $string['moveblockbefore'] = 'Move block to before {$a} block'; $string['moveblockinregion'] = 'Move block to {$a} region'; $string['movingthisblockcancel'] = 'Moving this block ({$a})'; +$string['myblocks'] = 'My blocks'; $string['onthispage'] = 'On this page'; $string['pagetypes'] = 'Page types'; $string['pagetypewarning'] = 'The previously specified page type is no longer selectable. Please choose the most appropriate page type below.'; diff --git a/lib/navigationlib.php b/lib/navigationlib.php index b6ebf72769d..c12cf634f5d 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -143,6 +143,8 @@ class navigation_node implements renderable { public $includesectionnum = false; /** @var bool does the node need to be loaded via ajax */ public $requiresajaxloading = false; + /** @var bool If set to true this node will be added to the "flat" navigation */ + public $showinflatnavigation = false; /** * Constructs a new navigation_node @@ -236,6 +238,65 @@ class navigation_node implements renderable { return false; } + /** + * True if this nav node has siblings in the tree. + * + * @return bool + */ + public function has_siblings() { + if (empty($this->parent) || empty($this->parent->children)) { + return false; + } + if ($this->parent->children instanceof navigation_node_collection) { + $count = $this->parent->children->count(); + } else { + $count = count($this->parent->children); + } + return ($count > 1); + } + + /** + * Recursively walk the tree looking for a node with a valid action. + * Depth first search. + * + * @return bool + */ + public function resolve_action() { + if ($this->action) { + return $this->action; + } + if (!empty($this->children)) { + foreach ($this->children as $child) { + $action = $child->resolve_action(); + if (!empty($action)) { + return $action; + } + } + } + return false; + } + + /** + * Get a list of sibling navigation nodes at the same level as this one. + * + * @return bool|array of navigation_node + */ + public function get_siblings() { + // Returns a list of the siblings of the current node for display in a flat navigation element. Either + // the in-page links or the breadcrumb links. + $siblings = false; + + if ($this->has_siblings()) { + $siblings = []; + foreach ($this->parent->children as $child) { + if ($child->display) { + $siblings[] = $child; + } + } + } + return $siblings; + } + /** * This sets the URL that the URL of new nodes get compared to when locating * the active node. @@ -381,6 +442,24 @@ class navigation_node implements renderable { return $this->children->find($key, $type); } + /** + * Walk the tree building up a list of all the flat navigation nodes. + * + * @param flat_navigation $nodes List of the found flat navigation nodes. + */ + public function build_flat_navigation_list(flat_navigation $nodes) { + if ($this->showinflatnavigation) { + $indent = 0; + if ($this->type == self::TYPE_COURSE) { + $indent = 1; + } + $nodes->add(new flat_navigation_node($this, $indent)); + } + foreach ($this->children as $child) { + $child->build_flat_navigation_list($nodes); + } + } + /** * Get the child of this node that has the given key + (optional) type. * @@ -1053,6 +1132,7 @@ class global_navigation extends navigation_node { // Use the parents constructor.... good good reuse parent::__construct($properties); + $this->showinflatnavigation = true; // Initalise and set defaults $this->page = $page; @@ -1128,15 +1208,18 @@ class global_navigation extends navigation_node { $this->load_course_sections($SITE, $this->rootnodes['site']); $course = $this->page->course; + $this->load_courses_enrolled(); // $issite gets set to true if the current pages course is the sites frontpage course $issite = ($this->page->course->id == $SITE->id); + // Determine if the user is enrolled in any course. $enrolledinanycourse = enrol_user_sees_own_courses(); $this->rootnodes['currentcourse']->mainnavonly = true; if ($enrolledinanycourse) { $this->rootnodes['mycourses']->isexpandable = true; + $this->rootnodes['mycourses']->showinflatnavigation = true; if ($CFG->navshowallcourses) { // When we show all courses we need to show both the my courses and the regular courses branch. $this->rootnodes['courses']->isexpandable = true; @@ -2428,6 +2511,14 @@ class global_navigation extends navigation_node { // This is the name that will be shown for the course. $coursename = empty($CFG->navshowfullcoursenames) ? $shortname : $fullname; + if ($coursetype == self::COURSE_CURRENT) { + if ($coursenode = $this->rootnodes['mycourses']->find($course->id, self::TYPE_COURSE)) { + return $coursenode; + } else { + $coursetype = self::COURSE_OTHER; + } + } + // Can the user expand the course to see its content. $canexpandcourse = true; if ($issite) { @@ -2468,6 +2559,7 @@ class global_navigation extends navigation_node { } $coursenode = $parent->add($coursename, $url, self::TYPE_COURSE, $shortname, $course->id); + $coursenode->showinflatnavigation = $coursetype == self::COURSE_MY; $coursenode->hidden = (!$course->visible); $coursenode->title(format_string($course->fullname, true, array('context' => $coursecontext, 'escape' => false))); if ($canexpandcourse) { @@ -2582,11 +2674,21 @@ class global_navigation extends navigation_node { if ($navoptions->badges) { $url = new moodle_url('/badges/view.php', array('type' => 2, 'id' => $course->id)); - $coursenode->add(get_string('coursebadges', 'badges'), null, - navigation_node::TYPE_CONTAINER, null, 'coursebadges'); - $coursenode->get('coursebadges')->add(get_string('badgesview', 'badges'), $url, + $coursenode->add(get_string('coursebadges', 'badges'), $url, navigation_node::TYPE_SETTING, null, 'badgesview', - new pix_icon('i/badge', get_string('badgesview', 'badges'))); + new pix_icon('i/badge', get_string('coursebadges', 'badges'))); + } + + // Check access to the course and competencies page. + if ($navoptions->competencies) { + // Just a link to course competency. + $title = get_string('competencies', 'core_competency'); + $path = new moodle_url("/admin/tool/lp/coursecompetencies.php", array('courseid' => $course->id)); + $coursenode->add($title, $path, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/competencies', '')); + } + if ($navoptions->grades) { + $url = new moodle_url('/grade/report/index.php', array('id'=>$course->id)); + $gradenode = $coursenode->add(get_string('grades'), $url, self::TYPE_SETTING, null, 'grades', new pix_icon('i/grades', '')); } return true; @@ -2603,7 +2705,7 @@ class global_navigation extends navigation_node { * @return bool True for successfull generation */ public function add_front_page_course_essentials(navigation_node $coursenode, stdClass $course) { - global $CFG; + global $CFG, $USER; require_once($CFG->dirroot . '/course/lib.php'); if ($coursenode == false || $coursenode->get('frontpageloaded', navigation_node::TYPE_CUSTOM)) { @@ -2657,7 +2759,18 @@ class global_navigation extends navigation_node { if ($navoptions->calendar) { // Calendar $calendarurl = new moodle_url('/calendar/view.php', array('view' => 'month')); - $coursenode->add(get_string('calendar', 'calendar'), $calendarurl, self::TYPE_CUSTOM, null, 'calendar'); + $node = $coursenode->add(get_string('calendar', 'calendar'), $calendarurl, self::TYPE_CUSTOM, null, 'calendar'); + $node->showinflatnavigation = true; + } + + if (isloggedin()) { + $usercontext = context_user::instance($USER->id); + if (has_capability('moodle/user:manageownfiles', $usercontext)) { + $url = new moodle_url('/user/files.php'); + $node = $coursenode->add(get_string('privatefiles'), $url, self::TYPE_SETTING); + $node->display = false; + $node->showinflatnavigation = true; + } } return true; @@ -3090,13 +3203,12 @@ class navbar extends navigation_node { public $includesettingsbase = false; /** @var breadcrumb_navigation_node[] $prependchildren */ protected $prependchildren = array(); + /** * The almighty constructor * * @param moodle_page $page */ - - public function __construct(moodle_page $page) { global $CFG; if (during_initial_install()) { @@ -3255,6 +3367,10 @@ class navbar extends navigation_node { $items = array_merge($items, array_reverse($this->prependchildren)); } + $last = reset($items); + if ($last) { + $last->set_last(true); + } $this->items = array_reverse($items); return $this->items; } @@ -3416,6 +3532,9 @@ class navbar extends navigation_node { */ class breadcrumb_navigation_node extends navigation_node { + /** @var $last boolean A flag indicating this is the last item in the list of breadcrumbs. */ + private $last = false; + /** * A proxy constructor * @@ -3436,6 +3555,251 @@ class breadcrumb_navigation_node extends navigation_node { } } + /** + * Getter for "last" + * @return boolean + */ + public function is_last() { + return $this->last; + } + + /** + * Setter for "last" + * @param $val boolean + */ + public function set_last($val) { + $this->last = $val; + } +} + +/** + * Subclass of navigation_node allowing different rendering for the flat navigation + * in particular allowing dividers and indents. + * + * @package core + * @category navigation + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class flat_navigation_node extends navigation_node { + + /** @var $indent integer The indent level */ + private $indent = 0; + + /** @var $showdivider bool Show a divider before this element */ + private $showdivider = false; + + /** @var $smscreenonly bool Only show this menu item on xs and sm screen sizes */ + private $smscreenonly = false; + + /** + * A proxy constructor + * + * @param mixed $navnode A navigation_node or an array + */ + public function __construct($navnode, $indent) { + if (is_array($navnode)) { + parent::__construct($navnode); + } else if ($navnode instanceof navigation_node) { + + // Just clone everything. + $objvalues = get_object_vars($navnode); + foreach ($objvalues as $key => $value) { + $this->$key = $value; + } + } else { + throw coding_exception('Not a valid flat_navigation_node'); + } + $this->indent = $indent; + } + + /** + * Getter for "showdivider" + * @return boolean + */ + public function showdivider() { + return $this->showdivider; + } + + /** + * Setter for "showdivider" + * @param $val boolean + */ + public function set_showdivider($val) { + $this->showdivider = $val; + } + + /** + * Getter for "indent" + * @return boolean + */ + public function get_indent() { + return $this->indent; + } + + /** + * Setter for "indent" + * @param $val boolean + */ + public function set_indent($val) { + $this->indent = $val; + } + + /** + * Getter for "smscreenonly" + * @return boolean + */ + public function get_smscreenonly() { + return $this->smscreenonly; + } + + /** + * Setter for "smscreenonly" + * @param $val boolean + */ + public function set_smscreenonly($val) { + $this->smscreenonly = $val; + } +} + +/** + * Class used to generate a collection of navigation nodes most closely related + * to the current page. + * + * @package core + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class flat_navigation extends navigation_node_collection { + /** @var moodle_page the moodle page that the navigation belongs to */ + protected $page; + + /** + * Constructor. + * + * @param moodle_page $page + */ + public function __construct(moodle_page &$page) { + if (during_initial_install()) { + return false; + } + $this->page = $page; + } + + /** + * Build the list of navigation nodes based on the current navigation and settings trees. + * + */ + public function initialise() { + global $PAGE, $USER, $OUTPUT, $CFG; + if (during_initial_install()) { + return; + } + + $current = false; + + $course = $PAGE->course; + + $PAGE->navigation->build_flat_navigation_list($this); + + // First walk the nav tree looking for "flat_navigation" nodes. + if ($course->id > 1) { + // It's a real course. + // 'dh' is an unused param used to give this node a different url to the default. + // This is so we don't have 2 nodes in the flat have with the same url (both would be highlighted). + // 'dh' means "don't highlight". + $url = new moodle_url('/course/view.php', array('id' => $course->id, 'dh' => 1)); + $flat = new flat_navigation_node(navigation_node::create($course->shortname, $url), 0); + $flat->key = 'coursehome'; + $flat->set_showdivider(true); + $this->add($flat); + + $coursenode = $PAGE->navigation->find_active_node(); + if ($coursenode) { + foreach ($coursenode->children as $child) { + if ($child->action) { + $flat = new flat_navigation_node($child, 1); + $this->add($flat); + } + } + } + + } + $admin = $PAGE->settingsnav->find('siteadministration', navigation_node::TYPE_SITE_ADMIN); + if (!$admin) { + // Try again - crazy nav tree! + $admin = $PAGE->settingsnav->find('root', navigation_node::TYPE_SITE_ADMIN); + } + if ($admin) { + $flat = new flat_navigation_node($admin, 0); + $flat->set_showdivider(true); + $flat->key = 'sitesettings'; + $this->add($flat); + } + + // Add the lang menu for small screens (it doesn't fit in the header). + if (!empty($CFG->langmenu)) { + if ($PAGE->course == SITEID || empty($PAGE->course->lang)) { + $langs = get_string_manager()->get_list_of_translations(); + if (count($langs) >= 2) { + $first = true; + foreach ($langs as $lang => $name) { + $url = clone($PAGE->url); + $url->params(['lang' => $lang]); + $flat = new flat_navigation_node(navigation_node::create($name, $url), 0); + if ($first) { + $flat->set_showdivider(true); + $first = false; + } + $flat->set_smscreenonly(true); + $flat->key = $lang; + $this->add($flat); + } + } + } + } + + // Add the custom menu for small screens (it doesn't fit in the header). + $customcount = 0; + if (!empty($CFG->custommenuitems)) { + $custommenus = custom_menu::convert_text_to_menu_nodes($CFG->custommenuitems, current_language()); + $first = true; + foreach ($custommenus as $menu) { + if (preg_match("/^#+$/", $menu->get_text())) { + $first = true; + } else { + $url = $menu->get_url(); + if ($url) { + $url = $url->out(false); + } + $flat = new flat_navigation_node(navigation_node::create($menu->get_text(), $url), 0); + if ($first) { + $flat->set_showdivider(true); + $first = false; + } + $flat->set_smscreenonly(true); + $flat->key = 'c-' . $customcount++; + $this->add($flat); + } + foreach ($menu->get_children() as $child) { + if (preg_match("/^#+$/", $child->get_text())) { + $first = true; + } else { + $url = $child->get_url(); + if ($url) { + $url = $url->out(false); + } + $flat = new flat_navigation_node(navigation_node::create($child->get_text(), $url), 1); + $flat->set_smscreenonly(true); + $flat->key = 'c-' . $customcount++; + $this->add($flat); + } + } + } + + } + } + } /** @@ -3480,6 +3844,7 @@ class settings_navigation extends navigation_node { $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); $this->children = new navigation_node_collection(); } + /** * Initialise the settings navigation based on the current context * @@ -3561,7 +3926,7 @@ class settings_navigation extends navigation_node { $adminsettings->remove(); $adminsettings = false; } - $siteadminnode = $this->add(get_string('administrationsite'), new moodle_url('/admin'), self::TYPE_SITE_ADMIN, null, 'siteadministration'); + $siteadminnode = $this->add(get_string('administrationsite'), new moodle_url('/admin/search.php'), self::TYPE_SITE_ADMIN, null, 'siteadministration'); $siteadminnode->id = 'expandable_branch_'.$siteadminnode->type.'_'.clean_param($siteadminnode->key, PARAM_ALPHANUMEXT); $siteadminnode->requiresajaxloading = 'true'; } @@ -3681,7 +4046,7 @@ class settings_navigation extends navigation_node { // Disable the navigation from automatically finding the active node navigation_node::$autofindactive = false; - $referencebranch = $this->add(get_string('administrationsite'), null, self::TYPE_SITE_ADMIN, null, 'root'); + $referencebranch = $this->add(get_string('administrationsite'), '/admin/search.php', self::TYPE_SITE_ADMIN, null, 'root'); foreach ($adminroot->children as $adminbranch) { $this->load_administration_settings($referencebranch, $adminbranch); } @@ -3789,29 +4154,6 @@ class settings_navigation extends navigation_node { $coursenode->force_open(); } - if ($this->page->user_allowed_editing()) { - // Add the turn on/off settings - - if ($this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) { - // We are on the course page, retain the current page params e.g. section. - $baseurl = clone($this->page->url); - $baseurl->param('sesskey', sesskey()); - } else { - // Edit on the main course page. - $baseurl = new moodle_url('/course/view.php', array('id'=>$course->id, 'return'=>$this->page->url->out_as_local_url(false), 'sesskey'=>sesskey())); - } - - $editurl = clone($baseurl); - if ($this->page->user_is_editing()) { - $editurl->param('edit', 'off'); - $editstring = get_string('turneditingoff'); - } else { - $editurl->param('edit', 'on'); - $editstring = get_string('turneditingon'); - } - $coursenode->add($editstring, $editurl, self::TYPE_SETTING, null, 'turneditingonoff', new pix_icon('i/edit', '')); - } - if ($adminoptions->update) { // Add the course settings link $url = new moodle_url('/course/edit.php', array('id'=>$course->id)); @@ -3959,7 +4301,8 @@ class settings_navigation extends navigation_node { } } if (is_array($roles) && count($roles)>0) { - $switchroles = $this->add(get_string('switchroleto'), null, self::TYPE_CONTAINER, null, 'switchroleto'); + $url = new moodle_url('/course/switchrole.php', array('id'=>$course->id, 'switchrole'=>'-1', 'returnurl'=>$this->page->url->out_as_local_url(false))); + $switchroles = $coursenode->add(get_string('switchroleto'), $url, self::TYPE_CONTAINER, null, 'switchroleto'); if ((count($roles)==1 && array_key_exists(0, $roles))|| $assumedrole!==false) { $switchroles->force_open(); } @@ -4393,7 +4736,7 @@ class settings_navigation extends navigation_node { if (($currentuser || is_siteadmin($USER) || !is_siteadmin($user)) && has_capability('moodle/user:update', $systemcontext)) { $url = new moodle_url('/user/editadvanced.php', array('id'=>$user->id, 'course'=>$course->id)); - $useraccount->add(get_string('editmyprofile'), $url, self::TYPE_SETTING); + $useraccount->add(get_string('editmyprofile'), $url, self::TYPE_SETTING, null, 'editprofile'); } else if ((has_capability('moodle/user:editprofile', $usercontext) && !is_siteadmin($user)) || ($currentuser && has_capability('moodle/user:editownprofile', $systemcontext))) { if ($userauthplugin && $userauthplugin->can_edit_profile()) { @@ -4401,7 +4744,7 @@ class settings_navigation extends navigation_node { if (empty($url)) { $url = new moodle_url('/user/edit.php', array('id'=>$user->id, 'course'=>$course->id)); } - $useraccount->add(get_string('editmyprofile'), $url, self::TYPE_SETTING); + $useraccount->add(get_string('editmyprofile'), $url, self::TYPE_SETTING, null, 'editprofile'); } } } diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index edea6df9c58..157bb158d09 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -3905,6 +3905,15 @@ class action_menu implements renderable, templatable { $this->menutrigger = $trigger; } + /** + * Return true if there is at least one visible link in the menu. + * + * @return bool + */ + public function is_empty() { + return !count($this->primaryactions) && !count($this->secondaryactions); + } + /** * Initialises JS required fore the action menu. * The JS is only required once as it manages all action menu's on the page. diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index c5d29ca21f9..c4d7bb8f466 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -4086,18 +4086,6 @@ EOD; return $html; } - /** - * Returns the header bar. - * - * @since Moodle 2.9 - * @param array $headerinfo An array of header information, dependant on what type of header is being displayed. The following - * array example is user specific. - * heading => Override the page heading. - * user => User object. - * usercontext => user context. - * @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 $DB, $USER, $CFG; $context = $this->page->context; @@ -4390,6 +4378,18 @@ EOD; } } + /** + * 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); + } + /** * Renders a progress bar. * diff --git a/lib/pagelib.php b/lib/pagelib.php index 061335d9d9e..df546f5aa7d 100644 --- a/lib/pagelib.php +++ b/lib/pagelib.php @@ -286,6 +286,11 @@ class moodle_page { */ protected $_settingsnav = null; + /** + * @var flat_navigation Contains a list of nav nodes, most closely related to the current page. + */ + protected $_flatnav = null; + /** * @var navbar Contains the navbar structure. */ @@ -726,6 +731,18 @@ class moodle_page { return $this->_settingsnav; } + /** + * Returns the flat navigation object + * @return flat_navigation + */ + protected function magic_get_flatnav() { + if ($this->_flatnav === null) { + $this->_flatnav = new flat_navigation($this); + $this->_flatnav->initialise(); + } + return $this->_flatnav; + } + /** * Returns request IP address. * diff --git a/lib/templates/settings_link_page.mustache b/lib/templates/settings_link_page.mustache new file mode 100644 index 00000000000..608e64939e3 --- /dev/null +++ b/lib/templates/settings_link_page.mustache @@ -0,0 +1,43 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_admin/admin_settings_links + + Displays the admin tree as a list of grouped links. + + Example context (json): + { + "name": "test", + "id": "test0", + "checked": true, + "label": "Do you like crackers?" + } +}} + +
+
+
+
+
+ {{#node}} + {{> core/settings_link_page_single }} + {{/node}} +
+
+
+
+
diff --git a/lib/templates/settings_link_page_single.mustache b/lib/templates/settings_link_page_single.mustache new file mode 100644 index 00000000000..525e7bfb3bb --- /dev/null +++ b/lib/templates/settings_link_page_single.mustache @@ -0,0 +1,39 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core/settings_link_page_single + + Displays the admin tree as a list of grouped links. + + Example context (json): + { + } +}} + + diff --git a/lib/templates/signup_form_layout.mustache b/lib/templates/signup_form_layout.mustache new file mode 100644 index 00000000000..80bd360b253 --- /dev/null +++ b/lib/templates/signup_form_layout.mustache @@ -0,0 +1,2 @@ +

{{#str}}newaccount{{/str}}

+{{{formhtml}}} diff --git a/login/signup.php b/login/signup.php index 1e54597dbac..abe977e0e0b 100644 --- a/login/signup.php +++ b/login/signup.php @@ -87,10 +87,11 @@ $login = get_string('login'); $PAGE->navbar->add($login); $PAGE->navbar->add($newaccount); +$PAGE->set_pagelayout('login'); $PAGE->set_title($newaccount); $PAGE->set_heading($SITE->fullname); echo $OUTPUT->header(); -echo $OUTPUT->heading($newaccount); -$mform_signup->display(); + +echo $OUTPUT->render($mform_signup); echo $OUTPUT->footer(); diff --git a/login/signup_form.php b/login/signup_form.php index 185cb69ac66..07980d8bc0f 100644 --- a/login/signup_form.php +++ b/login/signup_form.php @@ -30,7 +30,7 @@ require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/user/profile/lib.php'); require_once($CFG->dirroot . '/user/editlib.php'); -class login_signup_form extends moodleform { +class login_signup_form extends moodleform implements renderable, templatable { function definition() { global $USER, $CFG; @@ -142,4 +142,21 @@ class login_signup_form extends moodleform { return $errors; } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param renderer_base $output Used to do a final render of any components that need to be rendered for export. + * @return array + */ + public function export_for_template(renderer_base $output) { + ob_start(); + $this->display(); + $formhtml = ob_get_contents(); + ob_end_clean(); + $context = [ + 'formhtml' => $formhtml + ]; + return $context; + } } diff --git a/theme/boost/amd/build/blocks_drawer.min.js b/theme/boost/amd/build/blocks_drawer.min.js new file mode 100644 index 0000000000000000000000000000000000000000..6abe8582ffcec105a0e442b952572cff4f65f83d GIT binary patch literal 1591 zcmb_cTWi}e6#gqlFSS6?-V@AdlMo7BGD==XC_$Ev9j%U(Npia2`oB-M(qbp3+t^-= zk25XqHb$uyCsY$i-T~l>#e!ZAj%fiZce~eD zPT?nqLxVf6_Fyf`VT``|ZzOHBm)1&^M=S~rqW~=mfKiP}Q@w}sUrPso!yN)8|EWVE z)w4%1X|zp~L6$s`*Z4^0RWK!>BuOcA&cuir$woUV1So__-X=AI;7Rs5_CZ2S3ubMM zc%dx_9|J>&L*+>5h4DDfJ2^f5NBYn<9?s8LVF6)G+)i5BC};gEm^b!+XnqwK8{>Ln zz?DWtKo3Sc?M?@vL5sWA)w9;rU1NPj93LVsuo;Ny63_<&tQohJS zFM#m%^||NVd(0sb62e=w`cZ;MY&I}tUNbRNwamDUI4NQ@yIiUF| zX`|0BW}RqvdW2lqz-{V8&(3b9li3h4CD@VTiukXCQR27Fr9L1}t#60*5ki zC87$GPHI=z|EA7`ynB||(TgdtI17DQUI&O+7-MR6=7&04FeHgk!g38Lihwd>ROpz5 zhYgb)IKm|LD~)_waIk+fo0Zd=Q#?vO9p6jZ+;|vflyQEMGp)nD@Wlq?0KG{ud2P2* zr9BE)bXj9Jna~)M`bBGkwe8JG-IQ1Lrv#-#T#Xc%Qpo`5Mir(oj~k$IUT8SW^WRAR zZ}jc4cezOq+*qFW5&aIuMAT75T&`^iGzBxsq5eF7yyRhK3tjo0P410?1Qf{FQZnl0 z^jdkoNopBd7h5>Wcw*1)YMlwrLE6c7eAkO!tA@L}3qI4*h@5ILi&BGH9N>g2e{N4H zJ7gUZ++>WKnLh}zbXE~tDwfnRWof`pFKt5P{GxS$o&n4)B8cK{6@K=yZR*#V1pP|v+YlKH430L@+0%e%&LG?F#qDj&% z{ked4@vj6FKJN&u7c}mD*WnP;d%;Z>;*THRy&v73oHR4IAFJ?nv~5Ej2kU&ts?kB) z^4VI0L*(29FsTT$C%tMsk;Da-HuykU|yGPZ-o~N z#9t=PWP6t*-Zk8Hr@>|@lt_hSd1GgoJo{m%s5yHK2`h7BU9M}071;?X%+MP@-nAYm L;XZ(>s_^J1A%?e3 literal 0 HcmV?d00001 diff --git a/theme/boost/amd/src/drawer.js b/theme/boost/amd/src/drawer.js new file mode 100644 index 00000000000..74c307cf3f6 --- /dev/null +++ b/theme/boost/amd/src/drawer.js @@ -0,0 +1,139 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Contain the logic for a drawer. + * + * @package theme_boost + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', 'core/custom_interaction_events', 'core/notification'], + function($, CustomEvents, Notification) { + + var SELECTORS = { + TOGGLE_REGION: '[data-region="drawer-toggle"]', + TOGGLE_ACTION: '[data-action="toggle-drawer"]', + TOGGLE_TARGET: 'aria-controls', + TOGGLE_SIDE: 'left', + BODY: 'body' + }; + + /** + * Constructor for the Drawer. + * + * @param {object} root The root jQuery element for the modal + */ + var Drawer = function() { + + if (!$(SELECTORS.TOGGLE_REGION).length) { + Notification.exception({message: 'Page is missing a drawer toggle region'}); + } + if (!$(SELECTORS.TOGGLE_ACTION).length) { + Notification.exception({message: 'Page is missing a drawer toggle link'}); + } + $(SELECTORS.TOGGLE_REGION).each(function(index, ele) { + var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION); + var hidden = trigger.attr('aria-expanded') == 'false'; + var side = trigger.attr('data-side'); + var body = $(SELECTORS.BODY); + + if (!hidden) { + body.addClass('drawer-open-' + side); + trigger.attr('aria-expanded', 'true'); + } else { + trigger.attr('aria-expanded', 'false'); + } + }.bind(this)); + + this.registerEventListeners(); + }; + + Drawer.prototype.closeAll = function() { + $(SELECTORS.TOGGLE_REGION).each(function(index, ele) { + var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION); + var side = trigger.attr('data-side'); + var body = $(SELECTORS.BODY); + var drawerid = trigger.attr('aria-controls'); + var drawer = $(document.getElementById(drawerid)); + var preference = trigger.attr('data-preference'); + + trigger.attr('aria-expanded', 'false'); + body.removeClass('drawer-open-' + side); + drawer.attr('aria-hidden', 'true'); + drawer.addClass('closed'); + M.util.set_user_preference(preference, 'false'); + }.bind(this)); + }; + + /** + * Open / close the blocks drawer. + * + * @method toggleDrawer + * @param {Event} e + */ + Drawer.prototype.toggleDrawer = function(e) { + var trigger = $(e.target).closest('[data-action=toggle-drawer]'); + var drawerid = trigger.attr('aria-controls'); + var drawer = $(document.getElementById(drawerid)); + var body = $(SELECTORS.BODY); + var side = trigger.attr('data-side'); + var preference = trigger.attr('data-preference'); + + body.addClass('drawer-ease'); + var open = trigger.attr('aria-expanded') == 'true'; + if (!open) { + var small = $(document).width() < 512; + if (small) { + this.closeAll(); + } + // Open. + trigger.attr('aria-expanded', 'true'); + drawer.attr('aria-hidden', 'false'); + body.addClass('drawer-open-' + side); + drawer.removeClass('closed'); + M.util.set_user_preference(preference, 'true'); + } else { + // Close. + body.removeClass('drawer-open-' + side); + trigger.attr('aria-expanded', 'false'); + drawer.attr('aria-hidden', 'true'); + drawer.addClass('closed'); + M.util.set_user_preference(preference, 'false'); + } + }; + + /** + * Set up all of the event handling for the modal. + * + * @method registerEventListeners + */ + Drawer.prototype.registerEventListeners = function() { + var body = $(SELECTORS.BODY); + + CustomEvents.define(body, [CustomEvents.events.activate]); + + body.on(CustomEvents.events.activate, SELECTORS.TOGGLE_ACTION, function(e, data) { + this.toggleDrawer(data.originalEvent); + data.originalEvent.preventDefault(); + }.bind(this)); + }; + + return { + 'init': function() { + return new Drawer(); + } + }; +}); diff --git a/theme/boost/classes/output/core_renderer.php b/theme/boost/classes/output/core_renderer.php index afe5a60c9a9..ffd652ea960 100644 --- a/theme/boost/classes/output/core_renderer.php +++ b/theme/boost/classes/output/core_renderer.php @@ -23,6 +23,8 @@ 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; @@ -73,6 +75,7 @@ class core_renderer extends \core_renderer { public function full_header() { $html = html_writer::start_tag('header', array('id' => 'page-header', 'class' => 'row')); $html .= html_writer::start_div('col-xs-12 p-t-1 p-b-1'); + $html .= html_writer::div($this->context_header_settings_menu(), 'pull-xs-right context-header-settings-menu'); $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')); @@ -319,7 +322,7 @@ class core_renderer extends \core_renderer { * @return string */ public function body_css_classes(array $additionalclasses = array()) { - return $this->page->bodyclasses; + return $this->page->bodyclasses . ' ' . implode(' ', $additionalclasses); } /** @@ -347,6 +350,9 @@ class core_renderer extends \core_renderer { } } + if ($menu->is_empty()) { + return ''; + } $context = $menu->export_for_template($this); // We do not want the icon with the caret, the caret is added by Bootstrap. @@ -441,4 +447,152 @@ class core_renderer extends \core_renderer { } return $this->render_from_template('core/pix_icon', $data); } + + /** + * Renders the login form. + * + * @param \core_auth\output\login $form The renderable. + * @return string + */ + public function render_login(\core_auth\output\login $form) { + global $SITE; + + $context = $form->export_for_template($this); + + // Override because rendering is not supported in template yet. + $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, array('context' => context_course::instance(SITEID))); + + return $this->render_from_template('core/login', $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, array('context' => context_course::instance(SITEID))); + + 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(); + if ($context->contextlevel == CONTEXT_COURSE) { + // Get the course admin node from the settings navigation. + $items = $this->page->navbar->get_items(); + $node = end($items); + if (($node->type == navigation_node::TYPE_COURSE)) { + $node = $this->page->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE); + if ($node) { + // Build an action menu based on the visible nodes from this navigation tree. + $this->build_action_menu_from_navigation($menu, $node, false, true); + + $text = get_string('courseadministration'); + $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 ($context->contextlevel == CONTEXT_USER) { + $items = $this->page->navbar->get_items(); + $node = end($items); + if ($node->key === 'myprofile') { + // Get the course admin node from the settings navigation. + $node = $this->page->settingsnav->find('useraccount', 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); + } + + /** + * 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) { + + $node = $this->page->navigation->find_active_node(); + if (($node->type == navigation_node::TYPE_ACTIVITY || + $node->type == navigation_node::TYPE_RESOURCE)) { + // 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); + } + } + } + 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 + */ + private function build_action_menu_from_navigation(action_menu $menu, + navigation_node $node, + $indent = false, + $onlytopleafnodes = 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()) { + continue; + } + if ($menuitem->action) { + $text = $menuitem->text; + $link = new action_link($menuitem->action, $menuitem->text, null, null, $menuitem->icon); + if ($indent) { + $link->add_class('m-l-1'); + } + } else { + if ($onlytopleafnodes) { + continue; + } + $link = $menuitem->text; + } + $menu->add_secondary_action($link); + $this->build_action_menu_from_navigation($menu, $menuitem, true); + } + } + } + } diff --git a/theme/boost/config.php b/theme/boost/config.php index f52949d803f..65f04bf32fe 100644 --- a/theme/boost/config.php +++ b/theme/boost/config.php @@ -88,7 +88,7 @@ $THEME->layouts = [ 'defaultregion' => 'side-pre', ), 'login' => array( - 'file' => 'columns1.php', + 'file' => 'login.php', 'regions' => array(), 'options' => array('langmenu' => true), ), diff --git a/theme/boost/layout/columns1.php b/theme/boost/layout/columns1.php index 618eb77ed38..8b0b48281f2 100644 --- a/theme/boost/layout/columns1.php +++ b/theme/boost/layout/columns1.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * A one column layout for the boost theme. + * A two column layout for the boost theme. * * @package theme_boost * @copyright 2016 Damyon Wiese @@ -24,9 +24,29 @@ defined('MOODLE_INTERNAL') || die(); +user_preference_allow_ajax_update('drawer-open-blocks', PARAM_ALPHA); +user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA); + +$navdraweropen = (get_user_preferences('drawer-open-nav', 'true') == 'true'); +$blocksdraweropen = (get_user_preferences('drawer-open-blocks', 'true') == 'true'); +$extraclasses = []; +if ($blocksdraweropen) { + $extraclasses[] = 'drawer-open-right'; +} +if ($navdraweropen) { + $extraclasses[] = 'drawer-open-left'; +} +$bodyattributes = $OUTPUT->body_attributes($extraclasses); + $templatecontext = [ 'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))), - 'output' => $OUTPUT + 'output' => $OUTPUT, + 'sidepreblocks' => $OUTPUT->blocks('side-pre'), + 'bodyattributes' => $bodyattributes, + 'blocksdraweropen' => $blocksdraweropen, + 'navdraweropen' => $navdraweropen ]; +$templatecontext['flatnavigation'] = $PAGE->flatnav; echo $OUTPUT->render_from_template('theme_boost/columns1', $templatecontext); + diff --git a/theme/boost/layout/columns2.php b/theme/boost/layout/columns2.php index 90c4fc1e18e..e0664b9286b 100644 --- a/theme/boost/layout/columns2.php +++ b/theme/boost/layout/columns2.php @@ -24,10 +24,29 @@ defined('MOODLE_INTERNAL') || die(); +user_preference_allow_ajax_update('drawer-open-blocks', PARAM_ALPHA); +user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA); + +$navdraweropen = (get_user_preferences('drawer-open-nav', 'true') == 'true'); +$blocksdraweropen = (get_user_preferences('drawer-open-blocks', 'true') == 'true'); +$extraclasses = []; +if ($blocksdraweropen) { + $extraclasses[] = 'drawer-open-right'; +} +if ($navdraweropen) { + $extraclasses[] = 'drawer-open-left'; +} +$bodyattributes = $OUTPUT->body_attributes($extraclasses); + $templatecontext = [ 'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))), 'output' => $OUTPUT, - 'sidepreblocks' => $OUTPUT->blocks('side-pre', 'col-md-4 pull-md-8'), + 'sidepreblocks' => $OUTPUT->blocks('side-pre'), + 'bodyattributes' => $bodyattributes, + 'blocksdraweropen' => $blocksdraweropen, + 'navdraweropen' => $navdraweropen ]; +$templatecontext['flatnavigation'] = $PAGE->flatnav; echo $OUTPUT->render_from_template('theme_boost/columns2', $templatecontext); + diff --git a/theme/boost/layout/login.php b/theme/boost/layout/login.php new file mode 100644 index 00000000000..0ae675d0050 --- /dev/null +++ b/theme/boost/layout/login.php @@ -0,0 +1,36 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +/** + * A two column layout for the boost theme. + * + * @package theme_boost + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$bodyattributes = $OUTPUT->body_attributes(); + +$templatecontext = [ + 'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))), + 'output' => $OUTPUT, + 'bodyattributes' => $bodyattributes +]; + +echo $OUTPUT->render_from_template('theme_boost/login', $templatecontext); + diff --git a/theme/boost/layout/secure.php b/theme/boost/layout/secure.php index c1217901b7d..b0ae36aad9a 100644 --- a/theme/boost/layout/secure.php +++ b/theme/boost/layout/secure.php @@ -27,8 +27,7 @@ defined('MOODLE_INTERNAL') || die(); $templatecontext = [ 'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))), 'output' => $OUTPUT, - 'sidepreblocks' => $OUTPUT->blocks('side-pre', 'col-md-4 pull-md-8 col-lg-3 pull-lg-9'), - 'sidepostblocks' => $OUTPUT->blocks('side-post', 'col-md-3') + 'sidepreblocks' => $OUTPUT->blocks('side-pre'), ]; echo $OUTPUT->render_from_template('theme_boost/secure', $templatecontext); diff --git a/theme/boost/pix/blocksdrawer.png b/theme/boost/pix/blocksdrawer.png new file mode 100644 index 0000000000000000000000000000000000000000..a296436a70553b3d2d82daf8f4d764745938359b GIT binary patch literal 785 zcmV+s1Md8ZP)R8bViKj*&ZbB6E-+NN+(ApHP~NOPQ#QKMCVK${3!RuDx*n;;@WTGYx- zyP{nPZGvzUF-L0${QRqH0B&Raj>TSGrd!#h9!m`jPL5}@ky0F@P3xw2^SV~kw#^K+ z&5;#!2*)kX2oMv)gLPkdh{|^G`|Ua8BNHqX3Nygv^17eTPXH73gL*3k6NyB#I1QI9 z#3VG2d_VghpeL2;a@0AkqNc(s&5DL2D@V-M1l94SgV(E#rQntr?`h@9NzmxI(Nmns zWR`%J<@I8*_zieoPx02HF4<{~^8_U(qBkb;`B8vGDt!jH30moe&8llo6|>o?h$(gs zVmZ7V3^@Ueu5{8s=eAgBZ5J>V$zp}CKmed3AJ@{&r)_dFSovo(i~U@#1bF53iSgVp zFkBBwnS$Se-9TGkU;1e1>RffN!WzfY!t%J9b4A5h(E~tCjh2k9wq^orv(z_^vDbkw zHR$e)SaU@HjtmX8HU_~3R1S%3eoy%~4igHz(=fR2`~Ea_)KNzr|2h5ui)8G%4Hme- P00000NkvXXu0mjf3#V#- literal 0 HcmV?d00001 diff --git a/theme/boost/pix/blocksdrawer.svg b/theme/boost/pix/blocksdrawer.svg new file mode 100644 index 00000000000..eac1f212149 --- /dev/null +++ b/theme/boost/pix/blocksdrawer.svg @@ -0,0 +1,22 @@ + + + + + + diff --git a/theme/boost/scss/moodle.scss b/theme/boost/scss/moodle.scss index 9080050c6a0..c92deac3508 100644 --- a/theme/boost/scss/moodle.scss +++ b/theme/boost/scss/moodle.scss @@ -17,14 +17,17 @@ $breadcrumb-divider-rtl: "◀" !default; @import "moodle/icons"; @import "moodle/admin"; @import "moodle/blocks"; +@import "moodle/bootswatch"; @import "moodle/calendar"; @import "moodle/course"; +@import "moodle/drawer"; @import "moodle/filemanager"; @import "moodle/message"; @import "moodle/question"; @import "moodle/user"; @import "moodle/search"; @import "moodle/forms"; +@import "moodle/login"; @import "moodle/modules"; @import "moodle/chat"; @import "moodle/reports"; diff --git a/theme/boost/scss/moodle/blocks.scss b/theme/boost/scss/moodle/blocks.scss index f1504fa528e..9b15639f970 100644 --- a/theme/boost/scss/moodle/blocks.scss +++ b/theme/boost/scss/moodle/blocks.scss @@ -27,3 +27,52 @@ } } } + +#nav-drawer.closed { + left: -285px; +} +#blocks-drawer.closed { + right: -285px; +} + +/* Use a variable for the drawer background colors. */ +$drawer-bg: $gray-lighter !default; + +[data-region="drawer"] { + position: fixed; + padding: 20px; + width: 265px; + top: ($navbar-padding-y * 2) + ($navbar-brand-padding-y * 2) + $font-size-lg * 1.5; + height: 100%; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + z-index: 100; + background-color: $drawer-bg; + -webkit-transition: right 0.5s ease, left 0.5s ease; + -moz-transition: right 0.5s ease, left 0.5s ease; + transition: right 0.5s ease, left 0.5s ease; +} +#nav-drawer { + right: auto; + left: 0px; +} +#blocks-drawer { + left: auto; + right: 0px; +} +body.drawer-ease { + -webkit-transition: margin-left 0.5s ease, margin-right 0.5s ease; + -moz-transition: margin-left 0.5s ease, margin-right 0.5s ease; + transition: margin-left 0.5s ease, margin-right 0.5s ease; +} + +body.drawer-open-left { + @include media-breakpoint-up(md) { + margin-left: 265px; + } +} +body.drawer-open-right { + @include media-breakpoint-up(md) { + margin-right: 265px; + } +} diff --git a/theme/boost/scss/moodle/bootswatch.scss b/theme/boost/scss/moodle/bootswatch.scss new file mode 100644 index 00000000000..60afab5b269 --- /dev/null +++ b/theme/boost/scss/moodle/bootswatch.scss @@ -0,0 +1,10 @@ +// Adjustments to make bootstrap 4 work better with some variables from bootstrap 3 bootswatches +$navbar-height: 60px !default; + +.navbar { + max-height: $navbar-height; +} + +.navbar-brand { + margin-top: (($font-size-base * $line-height-base) - ($font-size-lg * $line-height-base)) / 2; +} diff --git a/theme/boost/scss/moodle/core.scss b/theme/boost/scss/moodle/core.scss index ffa9548305d..12443b56944 100644 --- a/theme/boost/scss/moodle/core.scss +++ b/theme/boost/scss/moodle/core.scss @@ -1,5 +1,20 @@ /* core.less */ + +.context-header-settings-menu, +.region-main-settings-menu { + float: right; + width: 4em; + height: 2em; + display: block; +} + +.context-header-settings-menu .dropdown-toggle > .icon, +.region-main-settings-menu .dropdown-toggle > .icon { + height: 24px; + width: auto; +} + /** Page layout CSS starts **/ .layout-option-noheader #page-header, .layout-option-nonavbar #page-navbar, @@ -1227,11 +1242,6 @@ body#page-lib-editor-tinymce-plugins-moodlemedia-preview { } } -/** Moodle modals. */ -.modal.show { - display: block; -} - /* Moodle Dialogue Settings (moodle-core-dialogue) */ .moodle-dialogue-base .moodle-dialogue-lightbox { background-color: $gray; @@ -1920,38 +1930,6 @@ img#persona_signin { .breadcrumb-nav .breadcrumb { margin: 0; } -/** Navbar */ -.navbar-brand { - .logo { - display: inline-block; - margin: -$navbar-brand-padding-y 0; - } - - .site-name { - display: inline-block; - } - - &.has-logo { - .site-name { - margin-left: $spacer / 2; - } - } -} - -@include media-breakpoint-down(xs) { - .navbar { - .navbar-brand { - max-width: 80%; - margin-right: 0; - - &.has-logo { - .site-name { - display: none; - } - } - } - } -} /** Page header */ #page-header { @@ -2207,3 +2185,18 @@ ul { } } } + +#region-flat-nav { + padding-right: 0; + padding-left: 0; + .nav { + margin-right: $grid-gutter-width / 2; + background-color: $card-bg; + } + @include media-breakpoint-down(sm) { + .nav { + margin-top: $grid-gutter-width; + margin-right: 0; + } + } +} diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss index 3e7e55b123e..1e0a54a70f5 100644 --- a/theme/boost/scss/moodle/course.scss +++ b/theme/boost/scss/moodle/course.scss @@ -54,7 +54,9 @@ &.right { float: right; + clear: right; } + margin-top: 0.5rem; } .spinner { @@ -1204,3 +1206,8 @@ span.editinstructions { } } } + +.page-settings-menu .menubar > a > .icon { + width: auto; + height: 32px; +} diff --git a/theme/boost/scss/moodle/drawer.scss b/theme/boost/scss/moodle/drawer.scss new file mode 100644 index 00000000000..7c215e1a56f --- /dev/null +++ b/theme/boost/scss/moodle/drawer.scss @@ -0,0 +1,75 @@ +/* Anchor link offset fix. This makes hash links scroll 60px down to account for the fixed header. */ +$fixed-header-y: $navbar-height; + +$drawer-width: 285px; +$drawer-padding-x: 20px; +$drawer-padding-y: 20px; +$drawer-offscreen-gutter: 20px; + +:target { + padding-top: ($fixed-header-y + 30px) ! important; /* negative fixed header height */ + margin-top: -$fixed-header-y ! important; /* negative fixed header height */ +} + +#nav-drawer.closed { + left: - ($drawer-width + $drawer-offscreen-gutter); +} +#blocks-drawer.closed { + right: - ($drawer-width + $drawer-offscreen-gutter); +} +#page { + margin-top: $navbar-height; +} + +/* Use a variable for the drawer background colors. */ +$drawer-bg: $gray-lighter !default; + +[data-region="drawer"] { + position: fixed; + padding: $drawer-padding-x $drawer-padding-y; + width: $drawer-width; + top: $fixed-header-y; + height: 100%; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + z-index: $zindex-dropdown - 1; + background-color: $drawer-bg; + -webkit-transition: right 0.5s ease, left 0.5s ease; + -moz-transition: right 0.5s ease, left 0.5s ease; + transition: right 0.5s ease, left 0.5s ease; + .dropdown-menu { + max-width: $drawer-width - $drawer-padding-x * 4; + } + .dropdown-item { + max-width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; + } +} +#nav-drawer { + right: auto; + left: 0px; +} +#blocks-drawer { + left: auto; + right: 0px; +} +#page { + margin-top: $fixed-header-y; +} +body.drawer-ease { + -webkit-transition: margin-left 0.5s ease, margin-right 0.5s ease; + -moz-transition: margin-left 0.5s ease, margin-right 0.5s ease; + transition: margin-left 0.5s ease, margin-right 0.5s ease; +} + +body.drawer-open-left { + @include media-breakpoint-up(lg) { + margin-left: $drawer-width; + } +} +body.drawer-open-right { + @include media-breakpoint-up(lg) { + margin-right: $drawer-width; + } +} diff --git a/theme/boost/scss/moodle/icons.scss b/theme/boost/scss/moodle/icons.scss index eb2f8029412..70887e8044e 100644 --- a/theme/boost/scss/moodle/icons.scss +++ b/theme/boost/scss/moodle/icons.scss @@ -36,3 +36,7 @@ a:first-of-type > .icon { margin-left: 0; } } + +[data-action=toggle-drawer] .icon { + margin: 0px; +} diff --git a/theme/boost/scss/moodle/login.scss b/theme/boost/scss/moodle/login.scss new file mode 100644 index 00000000000..c96744bd9fe --- /dev/null +++ b/theme/boost/scss/moodle/login.scss @@ -0,0 +1,8 @@ +.pagelayout-login .card-img-top { + max-width: 100%; +} + +.pagelayout-login #region-main { + border: 0; + background-color: inherit; +} diff --git a/theme/boost/scss/preset-default.scss b/theme/boost/scss/preset-default.scss index c61bb463348..e7224c3222c 100644 --- a/theme/boost/scss/preset-default.scss +++ b/theme/boost/scss/preset-default.scss @@ -18,6 +18,7 @@ $brand-info: #9954bb !default; $brand-warning: #ff8800 !default; $brand-danger: #ff4136 !default; $brand-inverse: $gray-dark !default; +$navbar-height: 50px !default; // Spacing $spacer: 1rem !default; @@ -166,26 +167,6 @@ $breadcrumb-divider-rtl: "/" !default; border-top: none; } -/** - * Login page. - */ -.pagelayout-login { - &.notloggedin { - #region-main { - background-color: transparent; - border: 0; - padding: 0; - } - .login-wrapper { - background-color: #fff; - border: $border-width solid $card-border-color; - } - } - #notice { - text-align: center; - } -} - /** * Dashboard styling. */ @@ -239,3 +220,23 @@ $breadcrumb-divider-rtl: "/" !default; .form-inline .form-group { margin-top: 0; } + + +// Custom transitions for open/close of menus. +.dropdown-menu { + display: block; + max-height: 0px; + overflow: hidden; + opacity: 0; + -webkit-transition: max-height 1s ease, opacity 0.4s ease; + -moz-transition: max-height 1s ease, opacity 0.4s ease; + transition: max-height 1s ease, opacity 0.4s ease; +} +.open { + // Show the menu + > .dropdown-menu { + max-height: 2048px; + opacity: 100; + } +} + diff --git a/theme/boost/templates/blocks-drawer.mustache b/theme/boost/templates/blocks-drawer.mustache new file mode 100644 index 00000000000..e353e034e37 --- /dev/null +++ b/theme/boost/templates/blocks-drawer.mustache @@ -0,0 +1,3 @@ +
+ {{{ sidepreblocks }}} +
diff --git a/theme/boost/templates/columns1.mustache b/theme/boost/templates/columns1.mustache index 67dbd8a1acc..c96e422f34e 100644 --- a/theme/boost/templates/columns1.mustache +++ b/theme/boost/templates/columns1.mustache @@ -7,7 +7,7 @@ - +
@@ -15,21 +15,24 @@ {{>theme_boost/header}} -
+
{{{ output.full_header }}}
-
-
-
- {{{ output.course_content_header }}} - {{{ output.main_content }}} - {{{ output.course_content_footer }}} -
-
+
+
+ + {{{ output.page_settings_menu }}} + + {{{ output.course_content_header }}} + {{{ output.main_content }}} + {{{ output.course_content_footer }}} +
+ {{> theme_boost/blocks-drawer }} + {{> theme_boost/nav-drawer }}
@@ -42,7 +45,6 @@ {{{ output.login_info }}} {{{ output.home_link }}} {{{ output.standard_footer_html }}} - {{{ output.standard_end_of_body_html }}}
@@ -51,4 +53,5 @@ {{#js}} require(['theme_boost/loader'], function() {}); +require(['theme_boost/drawer'], function(mod) {mod.init();}); {{/js}} diff --git a/theme/boost/templates/columns2.mustache b/theme/boost/templates/columns2.mustache index 98668cc58f5..9b0335cdf61 100644 --- a/theme/boost/templates/columns2.mustache +++ b/theme/boost/templates/columns2.mustache @@ -7,7 +7,7 @@ - +
@@ -15,23 +15,24 @@ {{>theme_boost/header}} -
+
{{{ output.full_header }}}
-
-
-
- {{{ output.course_content_header }}} - {{{ output.main_content }}} - {{{ output.course_content_footer }}} -
-
+
+
+ + {{{ output.region_main_settings_menu }}} + + {{{ output.course_content_header }}} + {{{ output.main_content }}} + {{{ output.course_content_footer }}} +
- {{{ sidepreblocks }}}
-
+ {{> theme_boost/blocks-drawer }} + {{> theme_boost/nav-drawer }}