From 3406acdef93980325e2521c9ebe466f2990dca54 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Mon, 19 Apr 2010 06:30:30 +0000 Subject: [PATCH] navigation MDL-22044 Major navigation structure changes. Navigation has now been re-ordered in accordance with http://docs.moodle.org/en/Development:Navigation_2.0_structure. This has led to several changes in the way in which navigation is generated included API changes, most notably using the navigation_nodes add method now returns the newly added node rather than the key. At the same time the global_navigation_tree block has been renamed to navigation and settings_navigation_tree to settings. --- blocks/dock.js | 31 +- blocks/global_navigation_tree/styles.css | 20 - blocks/moodleblock.class.php | 11 - .../block_navigation.php} | 116 +- .../db/upgrade.php | 6 +- .../edit_form.php | 17 +- .../lang/en/block_navigation.php} | 14 +- .../navigation.js | 27 +- blocks/navigation/renderer.php | 101 + blocks/navigation/styles.css | 20 + .../block_settings.php} | 44 +- .../edit_form.php | 10 +- .../lang/en/block_settings.php} | 7 +- blocks/settings/renderer.php | 79 + blocks/settings/styles.css | 20 + blocks/settings_navigation_tree/styles.css | 20 - blog/lib.php | 5 +- config-dist.php | 2 +- course/format/topics/lib.php | 4 +- course/format/weeks/lib.php | 4 +- course/loginas.php | 108 +- lang/en/moodle.php | 1 + lib/adminlib.php | 4 +- lib/ajax/getnavbranch.php | 20 +- lib/blocklib.php | 4 +- lib/db/upgrade.php | 20 + lib/navigationlib.php | 3042 +++++++---------- lib/outputrenderers.php | 64 +- lib/outputrequirementslib.php | 3 +- lib/pagelib.php | 4 +- lib/simpletest/testnavigationlib.php | 503 +-- .../type/online/assignment.class.php | 10 +- .../type/upload/assignment.class.php | 16 +- .../type/uploadsingle/assignment.class.php | 14 +- mod/chat/lib.php | 9 +- mod/choice/lib.php | 2 +- mod/data/lib.php | 9 +- mod/feedback/lib.php | 20 +- mod/forum/lib.php | 32 +- mod/glossary/lib.php | 2 +- mod/lesson/lib.php | 12 +- mod/quiz/lib.php | 4 +- mod/survey/lib.php | 12 +- mod/workshop/lib.php | 4 +- my/index.php | 1 - notes/index.php | 25 +- theme/standard/style/dock.css | 4 + user/editadvanced.php | 30 +- version.php | 2 +- 49 files changed, 1877 insertions(+), 2662 deletions(-) delete mode 100644 blocks/global_navigation_tree/styles.css rename blocks/{global_navigation_tree/block_global_navigation_tree.php => navigation/block_navigation.php} (71%) rename blocks/{global_navigation_tree => navigation}/db/upgrade.php (89%) rename blocks/{global_navigation_tree => navigation}/edit_form.php (63%) rename blocks/{global_navigation_tree/lang/en/block_global_navigation_tree.php => navigation/lang/en/block_navigation.php} (74%) rename blocks/{global_navigation_tree => navigation}/navigation.js (94%) create mode 100644 blocks/navigation/renderer.php create mode 100644 blocks/navigation/styles.css rename blocks/{settings_navigation_tree/block_settings_navigation_tree.php => settings/block_settings.php} (73%) rename blocks/{settings_navigation_tree => settings}/edit_form.php (78%) rename blocks/{settings_navigation_tree/lang/en/block_settings_navigation_tree.php => settings/lang/en/block_settings.php} (79%) create mode 100644 blocks/settings/renderer.php create mode 100644 blocks/settings/styles.css delete mode 100644 blocks/settings_navigation_tree/styles.css diff --git a/blocks/dock.js b/blocks/dock.js index b92bc4507ed23..ae6535072a566 100644 --- a/blocks/dock.js +++ b/blocks/dock.js @@ -336,7 +336,7 @@ M.core_dock = { } } - var moveto = this.Y.Node.create(''); + var moveto = this.Y.Node.create(''); moveto.on('movetodock|click', this.move_to_dock, this, commands); var blockaction = node.one('.block_action'); @@ -483,10 +483,13 @@ M.core_dock = { this.resize_block_space(this.cachedcontentnode); + var commands = this.cachedcontentnode.one('.commands'); - commands.all('.hidepanelicon').remove(); - commands.all('.moveto').remove(); - commands.remove(); + if (commands) { + commands.all('.hidepanelicon').remove(); + commands.all('.moveto').remove(); + commands.remove(); + } this.cachedcontentnode.one('.title').append(commands); this.cachedcontentnode = null; M.util.set_user_preference('docked_block_instance_'+this.id, 0); @@ -540,13 +543,6 @@ M.core_dock = { dockitem.addClass('firstdockitem'); } dockitem.append(dockitemtitle); - if (this.commands.hasChildNodes) { - if (this.contents.ancestor().one('.footer')) { - this.contents.ancestor().one('.footer').appendChild(this.commands); - } else { - this.contents.appendChild(this.commands); - } - } M.core_dock.append(dockitem); var position = dockitemtitle.getXY(); @@ -573,7 +569,9 @@ M.core_dock = { this.panel.showMaskEvent.subscribe(function(){ this.Y.one(this.panel.mask).setStyle('zIndex', this.cfg.panel.modalzindex); }, this, true); - this.panel.renderEvent.subscribe(this.resize_panel, this, true); + if (this.commands.hasChildNodes) { + this.panel.setHeader(this.Y.Node.getDOMNode(this.commands)); + } this.panel.setBody(this.Y.Node.getDOMNode(this.contents)); this.panel.render(M.core_dock.node); this.Y.one(this.panel.body).addClass(this.blockclass); @@ -693,10 +691,14 @@ M.core_dock = { */ resize_panel : function() { this.fire('dockeditem:resizestart'); + + var panelheader = this.Y.one(this.panel.header); + panelheader = (panelheader)?panelheader.get('offsetHeight'):0; var panelbody = this.Y.one(this.panel.body); + var buffer = this.cfg.buffer; var screenheight = parseInt(this.Y.get(document.body).get('winHeight')); - var panelheight = parseInt(panelbody.get('offsetHeight')); + var panelheight = parseInt(panelheader + panelbody.get('offsetHeight')); var paneltop = parseInt(this.panel.cfg.getProperty('y')); var titletop = parseInt(this.Y.one('#dock_item_'+this.id+'_title').getY()); var scrolltop = window.pageYOffset || document.body.scrollTop || 0; @@ -720,7 +722,7 @@ M.core_dock = { // This makes the panel constrain to the screen's height if the panel is big if (paneltop <= buffer && ((panelheight+paneltop*2) > screenheight || panelbody.hasClass('oversized_content'))) { this.panel.cfg.setProperty('height', screenheight-(buffer*3)); - panelbody.setStyle('height', (screenheight-(buffer*3)-10)+'px'); + panelbody.setStyle('height', (screenheight-panelheader-(buffer*3)-10)+'px'); panelbody.addClass('oversized_content'); } this.fire('dockeditem:resizecomplete'); @@ -754,6 +756,7 @@ M.core_dock.genericblock.prototype.fix_title_orientation = M.core_dock.abstrac * @param {this.Y.Node} title * @param {this.Y.Node} contents * @param {this.Y.Node} commands + * @param {string} blockclass */ M.core_dock.item = function(Y, uid, title, contents, commands, blockclass){ this.Y = Y; diff --git a/blocks/global_navigation_tree/styles.css b/blocks/global_navigation_tree/styles.css deleted file mode 100644 index 33055a222e054..0000000000000 --- a/blocks/global_navigation_tree/styles.css +++ /dev/null @@ -1,20 +0,0 @@ -/** JavaScript state rules **/ -.jsenabled .block_global_navigation_tree.dock_on_load, -.block_global_navigation_tree .block_tree_box .requiresjs {display:none;} -.jsenabled .block_global_navigation_tree .block_tree_box .requiresjs {display:inline;} - -/** General display rules **/ -.block_global_navigation_tree .block_tree {margin:5px;padding-left:0px;overflow-x:auto;overflow-y:visible;} -.block_global_navigation_tree .block_tree li {margin:0;list-style: none;} -.block_global_navigation_tree .block_tree li ul {padding-left:16px;margin:0;} -.block_global_navigation_tree .block_tree .tree_item {padding-left: 16px;margin:3px 0px;text-align:left;} -.block_global_navigation_tree .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: center left;background-repeat: no-repeat;} -.block_global_navigation_tree .block_tree .root_node.leaf {padding-left:0px;} -.block_global_navigation_tree .block_tree .current_branch {font-weight:bold;} -.jsenabled .block_global_navigation_tree .block_tree .tree_item.branch {cursor:pointer;} -.jsenabled .block_global_navigation_tree .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: center left;background-repeat: no-repeat;} -.jsenabled .block_global_navigation_tree .block_tree .collapsed ul {display: none;} -.jsenabled .block_global_navigation_tree .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);} - -/** Internet explorer specific rules **/ -.ie6 .block_global_navigation_tree .block_tree .tree_item {width:100%;} \ No newline at end of file diff --git a/blocks/moodleblock.class.php b/blocks/moodleblock.class.php index cef359015125e..63b5ed0af920f 100644 --- a/blocks/moodleblock.class.php +++ b/blocks/moodleblock.class.php @@ -113,8 +113,6 @@ class block_base { var $cron = NULL; - static $dockinitialised = false; - /// Class Functions /** @@ -562,7 +560,6 @@ function _load_instance($instance, $page) { } function get_required_javascript() { - $this->_initialise_dock(); if ($this->instance_can_be_docked() && !$this->hide_header()) { $this->page->requires->js_init_call('M.core_dock.init_genericblock', array($this->instance->id)); user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); @@ -735,14 +732,6 @@ public function instance_can_be_docked() { return (!empty($CFG->allowblockstodock) && $this->page->theme->enable_dock); } - public function _initialise_dock() { - global $CFG; - if (!self::$dockinitialised) { - $this->page->requires->strings_for_js(array('addtodock','undockitem','undockall'), 'block'); - self::$dockinitialised = true; - } - } - /** @callback callback functions for comments api */ public static function comment_template($options) { $ret = <<config->enabledock) || $this->config->enabledock=='yes')); + } + function get_required_javascript() { global $CFG; - $this->_initialise_dock(); $this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'))); - $this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'))); + $this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'))); user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); } @@ -102,50 +105,29 @@ function get_content() { // Navcount is used to allow us to have multiple trees although I dont' know why // you would want to trees the same - block_global_navigation_tree::$navcount++; + block_navigation::$navcount++; // Check if this block has been docked if ($this->docked === null) { - $this->docked = get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0); + $this->docked = get_user_preferences('nav_in_tab_panel_globalnav'.block_navigation::$navcount, 0); } // Check if there is a param to change the docked state if ($this->docked && optional_param('undock', null, PARAM_INT)==$this->instance->id) { - unset_user_preference('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount); + unset_user_preference('nav_in_tab_panel_globalnav'.block_navigation::$navcount); $url = $this->page->url; $url->remove_params(array('undock')); redirect($url); } else if (!$this->docked && optional_param('dock', null, PARAM_INT)==$this->instance->id) { - set_user_preferences(array('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount=>1)); + set_user_preferences(array('nav_in_tab_panel_globalnav'.block_navigation::$navcount=>1)); $url = $this->page->url; $url->remove_params(array('dock')); redirect($url); } - // Set the expansionlimit if one has been set in block config - if (!empty($this->config->expansionlimit) && $this->config->expansionlimit!='0') { - $this->page->navigation->expansionlimit = $this->config->expansionlimit; - } - // Initialise (only actually happens if it hasn't already been done yet $this->page->navigation->initialise(); - // Remove empty branches if the user has selected to - - if (empty($this->config->showemptybranches) || $this->config->showemptybranches=='no') { - $this->remove_empty_section_branches(); - } - - // Load the my courses branch if the user has selected to - if (isset($CFG->navshowcategories) && empty($CFG->navshowcategories)) { - $this->page->navigation->collapse_course_categories(); - } - - // Load the my courses branch if the user has selected to - if (!empty($this->config->showmycourses) && $this->config->showmycourses=='yes') { - $this->showmycourses(); - } - if (!empty($this->config->showmyhistory) && $this->config->showmyhistory=='yes') { $this->showmyhistory(); } @@ -155,16 +137,17 @@ function get_content() { $this->page->navigation->find_expandable($expandable); // Initialise the JS tree object - $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')); + $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')); $arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked())); $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module); // Grab the items to display - $this->content->items = array($this->page->navigation); + $renderer = $this->page->get_renderer('block_navigation'); + $this->content->text = $renderer->navigation_tree($this->page->navigation); $reloadlink = new moodle_url($this->page->url, array('regenerate'=>'navigation')); - $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand')); + $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand reloadnavigation')); // Set content generated to true so that we know it has been done $this->contentgenerated = true; @@ -294,9 +277,11 @@ protected function showmyhistory() { // If we have `more than nothing` in the history display it :D if ($historycount > 0) { // Add a branch to hold the users history - $mymoodle = $PAGE->navigation->get('mymoodle', navigation_node::TYPE_CUSTOM); + $mymoodle = $PAGE->navigation->get('profile', navigation_node::TYPE_USER); $myhistorybranch = $mymoodle->add(get_string('showmyhistorytitle', $this->blockname), null, navigation_node::TYPE_CUSTOM, null, 'myhistory'); - $mymoodle->get($myhistorybranch)->children = array_reverse($history); + foreach (array_reverse($history) as $node) { + $myhistorybranch->children->add($node); + } } // Cache the history (or update the cached history as it is) @@ -304,69 +289,4 @@ protected function showmyhistory() { return true; } - - /** - * This function loads the users my courses array into the navigation - * - * @return bool - */ - protected function showmycourses() { - global $USER, $PAGE; - - // Create a navigation cache to point at the same node as the main navigation - // cache - $cache = new navigation_cache('navigation'); - - // If the user isn't logged in or is a guest we don't want to display anything - if (!isloggedin() || isguestuser()) { - return false; - } - - // Check the cache to see if we have loaded my courses already - // there is a very good chance that we have - if (!$cache->cached('mycourses')) { - $cache->mycourses = get_my_courses($USER->id); - } - $courses = $cache->mycourses; - - // If no courses to display here, return before adding anything - if (!is_array($courses) || count($courses)==0) { - return false; - } - - // Add a branch labelled something like My Courses - $mymoodle = $PAGE->navigation->get('mymoodle', navigation_node::TYPE_CUSTOM); - $mycoursesbranch = $mymoodle->add(get_string('mycourses'), null,navigation_node::TYPE_CATEGORY, null, 'mycourses'); - $PAGE->navigation->add_courses($courses, 'mycourses'); - $mymoodle->get($mycoursesbranch)->type = navigation_node::TYPE_CUSTOM; - return true; - } - - /** - * This function searches all branches and removes any empty section branches - * for the global navigation structure. This is usually called by the global - * navigation block based on a block setting - */ - protected function remove_empty_section_branches() { - global $PAGE; - $cache = new navigation_cache('navigation'); - $course = &$PAGE->navigation->find_active_node(navigation_node::TYPE_COURSE); - if ($course===false || !$cache->cached('modinfo'.$course->key) || !$cache->cached('coursesections'.$course->key)) { - return 0; - } - $sectionstoremove = array(); - $coursesections = $cache->{'coursesections'.$course->key}; - $modinfosections = $cache->{'modinfo'.$course->key}->sections; - foreach ($coursesections as $id=>$section) { - if (!array_key_exists($id, $modinfosections)) { - $sectionstoremove[] = $section->id; - } - } - - foreach ($course->children as $key=>$node) { - if ($node->type == navigation_node::TYPE_SECTION && in_array($node->key, $sectionstoremove)) { - $course->remove_child($key); - } - } - } } diff --git a/blocks/global_navigation_tree/db/upgrade.php b/blocks/navigation/db/upgrade.php similarity index 89% rename from blocks/global_navigation_tree/db/upgrade.php rename to blocks/navigation/db/upgrade.php index d99a7232aa53f..4cb45eea8be21 100644 --- a/blocks/global_navigation_tree/db/upgrade.php +++ b/blocks/navigation/db/upgrade.php @@ -16,7 +16,7 @@ // along with Moodle. If not, see . /** - * This file keeps track of upgrades to the global_navigation_tree block + * This file keeps track of upgrades to the navigation block * * Sometimes, changes between versions involve alterations to database structures * and other major things that may break installations. @@ -41,7 +41,7 @@ /** * As of the implementation of this block and the general navigation code * in Moodle 2.0 the body of immediate upgrade work for this block and - * settings_navigation_tree is done in core upgrade {@see lib/db/upgrade.php} + * settings is done in core upgrade {@see lib/db/upgrade.php} * * There were several reasons that they were put there and not here, both becuase * the process for the two blocks was very similar and because the upgrade process @@ -51,7 +51,7 @@ * @param int $oldversion * @param object $block */ -function xmldb_block_global_navigation_tree_upgrade($oldversion, $block) { +function xmldb_block_navigation_upgrade($oldversion, $block) { // Implemented at 2009082800 return true; } \ No newline at end of file diff --git a/blocks/global_navigation_tree/edit_form.php b/blocks/navigation/edit_form.php similarity index 63% rename from blocks/global_navigation_tree/edit_form.php rename to blocks/navigation/edit_form.php index fcb3bcf4d5b31..d03ec0530932b 100644 --- a/blocks/global_navigation_tree/edit_form.php +++ b/blocks/navigation/edit_form.php @@ -31,25 +31,12 @@ * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class block_global_navigation_tree_edit_form extends block_edit_form { +class block_navigation_edit_form extends block_edit_form { protected function specific_definition($mform) { global $CFG; $mform->addElement('header', 'configheader', get_string('blocksettings', 'block')); - $options = array(); - $options['0'] = get_string('everything', $this->block->blockname); - $options[global_navigation::TYPE_COURSE] = get_string('courses', $this->block->blockname); - $options[global_navigation::TYPE_SECTION] = get_string('coursestructures', $this->block->blockname); - $options[global_navigation::TYPE_ACTIVITY] = get_string('courseactivities', $this->block->blockname); - $mform->addElement('select', 'config_expansionlimit', get_string('expansionlimit', $this->block->blockname), $options); - if (isset($this->block->config->expansionlimit) && $this->block->config->expansionlimit!='0') { - $mform->getElement('config_expansionlimit')->setSelected($this->block->config->expansionlimit); - } else { - $mform->getElement('config_expansionlimit')->setSelected('0'); - } - $mform->setType('expansionlimit', PARAM_MULTILANG); - - $mods = array('showemptybranches'=>'no','enablesidebarpopout'=>'yes', 'enablehoverexpansion'=>'no', 'showmycourses'=>'yes', 'showmyhistory'=>'no'); + $mods = array('enabledock'=>'yes', 'enablehoverexpansion'=>'no', 'showmyhistory'=>'no'); $yesnooptions = array('yes'=>get_string('yes'), 'no'=>get_string('no')); foreach ($mods as $modname=>$default) { $mform->addElement('select', 'config_'.$modname, get_string($modname.'desc', $this->block->blockname), $yesnooptions); diff --git a/blocks/global_navigation_tree/lang/en/block_global_navigation_tree.php b/blocks/navigation/lang/en/block_navigation.php similarity index 74% rename from blocks/global_navigation_tree/lang/en/block_global_navigation_tree.php rename to blocks/navigation/lang/en/block_navigation.php index bfe1eb4649fb6..9e61a4ba96278 100644 --- a/blocks/global_navigation_tree/lang/en/block_global_navigation_tree.php +++ b/blocks/navigation/lang/en/block_navigation.php @@ -16,22 +16,20 @@ // along with Moodle. If not, see . /** - * Strings for component 'block_global_navigation_tree', language 'en', branch 'MOODLE_20_STABLE' + * This file contains language strings used in the global navigation block * - * @package block_global_navigation_tree + * @since 2.0 + * @package blocks * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -$string['courseactivities'] = 'Categories, courses, and course Activities'; +$string['everything'] = 'Everything'; $string['courses'] = 'Categories and courses'; $string['coursestructures'] = 'Categories, courses, and course structures'; +$string['courseactivities'] = 'Categories, courses, and course Activities'; $string['enablehoverexpansiondesc'] = 'Enable mouseover expansion of this block'; -$string['enablesidebarpopoutdesc'] = 'Allow the user to switch the block to a sidbar popout'; -$string['everything'] = 'Everything'; -$string['expansionlimit'] = 'Generate navigation for the following'; +$string['enabledockdesc'] = 'Allow the user to dock this block'; $string['pluginname'] = 'Navigation'; -$string['showemptybranchesdesc'] = 'Show empty course section branches'; -$string['showmycoursesdesc'] = 'Show my courses in the navigation'; $string['showmyhistorydesc'] = 'Show my history as a branch in the navigation'; $string['showmyhistorytitle'] = 'My history'; diff --git a/blocks/global_navigation_tree/navigation.js b/blocks/navigation/navigation.js similarity index 94% rename from blocks/global_navigation_tree/navigation.js rename to blocks/navigation/navigation.js index 5744b42b9bbf9..b8c53c0b0dda6 100644 --- a/blocks/global_navigation_tree/navigation.js +++ b/blocks/navigation/navigation.js @@ -62,7 +62,7 @@ M.block_navigation = M.block_navigation || { /** * @class tree * @constructor - * @base M.core_dock.abstractblock + * @base M.core_dock.genericblock * @param {YUI} Y A yui instance to use with the navigation * @param {string} id The name of the tree * @param {object} properties Object containing tree properties @@ -97,6 +97,12 @@ M.block_navigation.classes.tree = function(Y, id, properties) { if (node === null) { return; } + + var reloadicon = node.one('.footer .reloadnavigation'); + if (reloadicon) { + reloadicon.remove(); + } + // Attach event to toggle expansion node.all('.tree_item.branch').on('click', this.toggleexpansion , this); @@ -115,6 +121,10 @@ M.block_navigation.classes.tree = function(Y, id, properties) { if (this.candock) { this.init(Y, node); } + + if (reloadicon) { + node.one('.header .block_action').insert(reloadicon, 0); + } } /** @@ -207,6 +217,13 @@ M.block_navigation.classes.tree.prototype.add_branch = function(branchobj, targe M.block_navigation.classes.tree.prototype.toggleexpansion = function(e) { // First check if they managed to click on the li iteslf, then find the closest // LI ancestor and use that + + if (e.target.get('nodeName').toUpperCase() == 'A') { + // A link has been clicked don't fire any more events just do the default. + e.stopPropagation(); + return; + } + if (e.target.get('nodeName').toUpperCase() == 'LI') { e.target.toggleClass('collapsed'); } else if (e.target.ancestor('LI')) { @@ -244,12 +261,18 @@ M.block_navigation.classes.branch = function(tree, obj) { this.construct_from_json(obj); } } +/** + * Populates this branch from a JSON object + * @param {object} obj + */ M.block_navigation.classes.branch.prototype.construct_from_json = function(obj) { for (var i in obj) { this[i] = obj[i]; } - if (this.children) { + if (this.children && this.children.length > 0) { this.haschildren = true; + } else { + this.children = []; } if (this.id && this.id.match(/^expandable_branch_\d+$/)) { // Assign a new unique id for this new expandable branch diff --git a/blocks/navigation/renderer.php b/blocks/navigation/renderer.php new file mode 100644 index 0000000000000..47b5fd3d1fe30 --- /dev/null +++ b/blocks/navigation/renderer.php @@ -0,0 +1,101 @@ +navigation_node(array($navigation), array('class'=>'block_tree list')); + if (isset($navigation->id) && !is_numeric($navigation->id) && !empty($content)) { + $content = $this->output->box($content, 'block_tree_box', $navigation->id); + } + return $content; + } + + protected function navigation_node($items, $attrs=array()) { + + // exit if empty, we don't want an empty ul element + if (count($items)==0) { + return ''; + } + + // array of nested li elements + $lis = array(); + foreach ($items as $item) { + if (!$item->display) { + continue; + } + $content = $item->get_content(); + $title = $item->get_title(); + if ($item->icon instanceof renderable) { + $icon = $this->output->render($item->icon); + $content = $icon.' '.$content; // use CSS for spacing of icons + } + if ($item->helpbutton !== null) { + $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton')); + } + + if ($content === '') { + continue; + } + + if ($item->action instanceof action_link) { + //TODO: to be replaced with something else + $link = $item->action; + if ($item->hidden) { + $link->add_class('dimmed'); + } + $content = $this->output->render($link); + } else if ($item->action instanceof moodle_url) { + $attributes = array(); + if ($title !== '') { + $attributes['title'] = $title; + } + if ($item->hidden) { + $attributes['class'] = 'dimmed_text'; + } + $content = html_writer::link($item->action, $content, $attributes); + + } else if (is_string($item->action) || empty($item->action)) { + $attributes = array(); + if ($title !== '') { + $attributes['title'] = $title; + } + if ($item->hidden) { + $attributes['class'] = 'dimmed_text'; + } + $content = html_writer::tag('span', $content, $attributes); + } + + // this applies to the li item which contains all child lists too + $liclasses = array($item->get_css_type()); + if ($item->has_children() && (!$item->forceopen || $item->collapse)) { + $liclasses[] = 'collapsed'; + } + if ($item->isactive === true) { + $liclasses[] = 'current_branch'; + } + $liattr = array('class'=>join(' ',$liclasses)); + // class attribute on the div item which only contains the item content + $divclasses = array('tree_item'); + if ($item->children->count() > 0 || ($item->nodetype == navigation_node::NODETYPE_BRANCH && $item->children->count()==0 && isloggedin())) { + $divclasses[] = 'branch'; + } else { + $divclasses[] = 'leaf'; + } + if (!empty($item->classes) && count($item->classes)>0) { + $divclasses[] = join(' ', $item->classes); + } + $divattr = array('class'=>join(' ', $divclasses)); + if (!empty($item->id)) { + $divattr['id'] = $item->id; + } + $content = html_writer::tag('p', $content, $divattr) . $this->navigation_node($item->children); + if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) { + $content = html_writer::empty_tag('hr') . $content; + } + $content = html_writer::tag('li', $content, $liattr); + $lis[] = $content; + } + return html_writer::tag('ul', implode("\n", $lis), $attrs); + } + +} \ No newline at end of file diff --git a/blocks/navigation/styles.css b/blocks/navigation/styles.css new file mode 100644 index 0000000000000..bd3e9dc935372 --- /dev/null +++ b/blocks/navigation/styles.css @@ -0,0 +1,20 @@ +/** JavaScript state rules **/ +.jsenabled .block_navigation.dock_on_load, +.block_navigation .block_tree_box .requiresjs {display:none;} +.jsenabled .block_navigation .block_tree_box .requiresjs {display:inline;} + +/** General display rules **/ +.block_navigation .block_tree {margin:5px;padding-left:0px;overflow:visible;} +.block_navigation .block_tree li {margin:0;list-style: none;} +.block_navigation .block_tree li ul {padding-left:16px;margin:0;} +.block_navigation .block_tree .tree_item {padding-left: 16px;margin:3px 0px;text-align:left;} +.block_navigation .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: center left;background-repeat: no-repeat;} +.block_navigation .block_tree .root_node.leaf {padding-left:0px;} +.block_navigation .block_tree .current_branch {font-weight:bold;} +.jsenabled .block_navigation .block_tree .tree_item.branch {cursor:pointer;} +.jsenabled .block_navigation .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: center left;background-repeat: no-repeat;} +.jsenabled .block_navigation .block_tree .collapsed ul {display: none;} +.jsenabled .block_navigation .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);} + +/** Internet explorer specific rules **/ +.ie6 .block_navigation .block_tree .tree_item {width:100%;} \ No newline at end of file diff --git a/blocks/settings_navigation_tree/block_settings_navigation_tree.php b/blocks/settings/block_settings.php similarity index 73% rename from blocks/settings_navigation_tree/block_settings_navigation_tree.php rename to blocks/settings/block_settings.php index 1b8b2a5ea6fde..9a896c8ef0659 100644 --- a/blocks/settings_navigation_tree/block_settings_navigation_tree.php +++ b/blocks/settings/block_settings.php @@ -34,7 +34,7 @@ * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class block_settings_navigation_tree extends block_tree { +class block_settings extends block_base { /** @var string */ public static $navcount; @@ -77,11 +77,14 @@ function instance_allow_config() { return true; } + function instance_can_be_docked() { + return (parent::instance_can_be_docked() && (empty($this->config->enabledock) || $this->config->enabledock=='yes')); + } + function get_required_javascript() { global $CFG; - $this->_initialise_dock(); $this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'))); - $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')); + $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')); $arguments = array($this->instance->id, array('instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked())); $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module); user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); @@ -99,55 +102,42 @@ function get_content() { $this->page->requires->yui2_lib('dom'); // JS for navigation moved to the standard theme, the code will probably have to depend on the actual page structure // $this->page->requires->js('/lib/javascript-navigation.js'); - block_settings_navigation_tree::$navcount++; + block_settings::$navcount++; // Check if this block has been docked if ($this->docked === null) { - $this->docked = get_user_preferences('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, 0); + $this->docked = get_user_preferences('nav_in_tab_panel_settingsnav'.block_settings::$navcount, 0); } // Check if there is a param to change the docked state if ($this->docked && optional_param('undock', null, PARAM_INT)==$this->instance->id) { - unset_user_preference('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, 0); + unset_user_preference('nav_in_tab_panel_settingsnav'.block_settings::$navcount, 0); $url = $this->page->url; $url->remove_params(array('undock')); redirect($url); } else if (!$this->docked && optional_param('dock', null, PARAM_INT)==$this->instance->id) { - set_user_preferences(array('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount=>1)); + set_user_preferences(array('nav_in_tab_panel_settingsnav'.block_settings::$navcount=>1)); $url = $this->page->url; $url->remove_params(array('dock')); redirect($url); } - if (!$this->page->settingsnav->contains_active_node() && !$this->page->navigation->contains_active_node()) { - if (!$this->page->settingsnav->reiterate_active_nodes()) { - $this->page->settingsnav->reiterate_active_nodes(URL_MATCH_BASE); - } - } + $renderer = $this->page->get_renderer('block_settings'); + $this->content->text = $renderer->settings_tree($this->page->settingsnav); - // Grab the children from settings nav, we have more than one root node - // and we dont want to show the site node - $this->content->items = $this->page->settingsnav->children; // only do search if you have moodle/site:config - if (count($this->content->items)>0) { + if (!empty($this->content->text)) { if (has_capability('moodle/site:config',get_context_instance(CONTEXT_SYSTEM)) ) { - $value = optional_param('query', '', PARAM_RAW); - $target = new moodle_url("$CFG->wwwroot/$CFG->admin/search.php"); - $form = '
'; - $form .= '
'; - $form .= ''; - $form .= ''; - $form .= '
'; - $this->content->footer = $form; + $this->content->footer = $renderer->search_form(new moodle_url("$CFG->wwwroot/$CFG->admin/search.php"), optional_param('query', '', PARAM_RAW)); } else { $this->content->footer = ''; } $reloadlink = new moodle_url($this->page->url, array('regenerate'=>'navigation')); - $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand')); + $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand reloadnavigation')); - if (!empty($this->config->enablesidebarpopout) && $this->config->enablesidebarpopout == 'yes') { - user_preference_allow_ajax_update('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, PARAM_INT); + if (!empty($this->config->enabledock) && $this->config->enabledock == 'yes') { + user_preference_allow_ajax_update('nav_in_tab_panel_settingsnav'.block_settings::$navcount, PARAM_INT); } } diff --git a/blocks/settings_navigation_tree/edit_form.php b/blocks/settings/edit_form.php similarity index 78% rename from blocks/settings_navigation_tree/edit_form.php rename to blocks/settings/edit_form.php index 78777a36413d0..efe5d0d56a299 100644 --- a/blocks/settings_navigation_tree/edit_form.php +++ b/blocks/settings/edit_form.php @@ -31,7 +31,7 @@ * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class block_settings_navigation_tree_edit_form extends block_edit_form { +class block_settings_edit_form extends block_edit_form { protected function specific_definition($mform) { $mform->addElement('header', 'configheader', get_string('blocksettings', 'block')); @@ -44,11 +44,11 @@ protected function specific_definition($mform) { $mform->getElement('config_enablehoverexpansion')->setSelected('yes'); } - $mform->addElement('select', 'config_enablesidebarpopout', get_string('enablesidebarpopout', $this->block->blockname), $yesnooptions); - if (empty($this->block->config->enablesidebarpopout) || $this->block->config->enablesidebarpopout=='no') { - $mform->getElement('config_enablesidebarpopout')->setSelected('no'); + $mform->addElement('select', 'config_enabledock', get_string('enabledock', $this->block->blockname), $yesnooptions); + if (empty($this->block->config->enabledock) || $this->block->config->enabledock=='yes') { + $mform->getElement('config_enabledock')->setSelected('yes'); } else { - $mform->getElement('config_enablesidebarpopout')->setSelected('yes'); + $mform->getElement('config_enabledock')->setSelected('no'); } } } \ No newline at end of file diff --git a/blocks/settings_navigation_tree/lang/en/block_settings_navigation_tree.php b/blocks/settings/lang/en/block_settings.php similarity index 79% rename from blocks/settings_navigation_tree/lang/en/block_settings_navigation_tree.php rename to blocks/settings/lang/en/block_settings.php index 0ff92adcf8d80..12f532242d443 100644 --- a/blocks/settings_navigation_tree/lang/en/block_settings_navigation_tree.php +++ b/blocks/settings/lang/en/block_settings.php @@ -16,13 +16,14 @@ // along with Moodle. If not, see . /** - * Strings for component 'block_settings_navigation_tree', language 'en', branch 'MOODLE_20_STABLE' + * This file contains language strings used in the settings navigation block * - * @package block_settings_navigation_tree + * @since 2.0 + * @package blocks * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['enablehoverexpansion'] = 'Enable mouseover expansion of this block'; -$string['enablesidebarpopout'] = 'Allow the user to switch the block to a sidbar popout'; +$string['enabledock'] = 'Allow the user to dock this block'; $string['pluginname'] = 'Settings'; diff --git a/blocks/settings/renderer.php b/blocks/settings/renderer.php new file mode 100644 index 0000000000000..bde5c53f224d5 --- /dev/null +++ b/blocks/settings/renderer.php @@ -0,0 +1,79 @@ +children as &$child) { + $child->preceedwithhr = ($count!==0); + $count++; + } + $content = $this->navigation_node($navigation, array('class'=>'block_tree list')); + if (isset($navigation->id) && !is_numeric($navigation->id) && !empty($content)) { + $content = $this->output->box($content, 'block_tree_box', $navigation->id); + } + return $content; + } + + protected function navigation_node(navigation_node $node, $attrs=array()) { + $items = $node->children; + + // exit if empty, we don't want an empty ul element + if ($items->count()==0) { + return ''; + } + + // array of nested li elements + $lis = array(); + foreach ($items as $item) { + if (!$item->display) { + continue; + } + + $content = $this->output->render($item); + + // this applies to the li item which contains all child lists too + $liclasses = array($item->get_css_type()); + if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count()==0 && $item->nodetype==navigation_node::NODETYPE_BRANCH)) { + $liclasses[] = 'collapsed'; + } + if ($item->isactive === true) { + $liclasses[] = 'current_branch'; + } + $liattr = array('class'=>join(' ',$liclasses)); + // class attribute on the div item which only contains the item content + $divclasses = array('tree_item'); + if ($item->children->count()>0 || $item->nodetype==navigation_node::NODETYPE_BRANCH) { + $divclasses[] = 'branch'; + } else { + $divclasses[] = 'leaf'; + } + if (!empty($item->classes) && count($item->classes)>0) { + $divclasses[] = join(' ', $item->classes); + } + $divattr = array('class'=>join(' ', $divclasses)); + if (!empty($item->id)) { + $divattr['id'] = $item->id; + } + $content = html_writer::tag('p', $content, $divattr) . $this->navigation_node($item); + if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) { + $content = html_writer::empty_tag('hr') . $content; + } + $content = html_writer::tag('li', $content, $liattr); + $lis[] = $content; + } + return html_writer::tag('ul', implode("\n", $lis), $attrs); + } + + public function search_form(moodle_url $formtarget, $searchvalue) { + $content = html_writer::start_tag('form', array('class'=>'adminsearchform', 'method'=>'get', 'action'=>$formtarget)); + $content .= html_writer::start_tag('div'); + $content .= html_writer::tag('label', s(get_string('searchinsettings', 'admin')), array('for'=>'adminsearchquery', 'class'=>'accesshide')); + $content .= html_writer::empty_tag('input', array('id'=>'adminsearchquery', 'type'=>'text', 'name'=>'query', 'value'=>s($searchvalue))); + $content .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>s(get_string('search')))); + $content .= html_writer::end_tag('div'); + $content .= html_writer::end_tag('form'); + return $content; + } + +} \ No newline at end of file diff --git a/blocks/settings/styles.css b/blocks/settings/styles.css new file mode 100644 index 0000000000000..3853f1fabfd6a --- /dev/null +++ b/blocks/settings/styles.css @@ -0,0 +1,20 @@ +/** JavaScript state rules **/ +.jsenabled .block_settings.dock_on_load, +.block_settings .block_tree_box .requiresjs {display:none;} +.jsenabled .block_settings .block_tree_box .requiresjs {display:inline;} + +/** General display rules **/ +.block_settings .block_tree {margin:5px;padding-left:0px;overflow:visible;} +.block_settings .block_tree li {margin:0;list-style: none;} +.block_settings .block_tree li ul {padding-left:16px;margin:0;} +.block_settings .block_tree .tree_item {padding-left: 16px;margin:3px 0px;text-align:left;} +.block_settings .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: center left;background-repeat: no-repeat;} +.block_settings .block_tree .root_node.leaf {padding-left:0px;} +.block_settings .block_tree .current_branch {font-weight:bold;} +.jsenabled .block_settings .block_tree .tree_item.branch {cursor:pointer;} +.jsenabled .block_settings .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: center left;background-repeat: no-repeat;} +.jsenabled .block_settings .block_tree .collapsed ul {display: none;} +.jsenabled .block_settings .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);} + +/** Internet explorer specific rules **/ +.ie6 .block_settings .block_tree .tree_item {width:100%;} \ No newline at end of file diff --git a/blocks/settings_navigation_tree/styles.css b/blocks/settings_navigation_tree/styles.css deleted file mode 100644 index 2545fcd63a765..0000000000000 --- a/blocks/settings_navigation_tree/styles.css +++ /dev/null @@ -1,20 +0,0 @@ -/** JavaScript state rules **/ -.jsenabled .block_settings_navigation_tree.dock_on_load, -.block_settings_navigation_tree .block_tree_box .requiresjs {display:none;} -.jsenabled .block_settings_navigation_tree .block_tree_box .requiresjs {display:inline;} - -/** General display rules **/ -.block_settings_navigation_tree .block_tree {margin:5px;padding-left:0px;overflow:visible;} -.block_settings_navigation_tree .block_tree li {margin:0;list-style: none;} -.block_settings_navigation_tree .block_tree li ul {padding-left:16px;margin:0;} -.block_settings_navigation_tree .block_tree .tree_item {padding-left: 16px;margin:3px 0px;text-align:left;} -.block_settings_navigation_tree .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: center left;background-repeat: no-repeat;} -.block_settings_navigation_tree .block_tree .root_node.leaf {padding-left:0px;} -.block_settings_navigation_tree .block_tree .current_branch {font-weight:bold;} -.jsenabled .block_settings_navigation_tree .block_tree .tree_item.branch {cursor:pointer;} -.jsenabled .block_settings_navigation_tree .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: center left;background-repeat: no-repeat;} -.jsenabled .block_settings_navigation_tree .block_tree .collapsed ul {display: none;} -.jsenabled .block_settings_navigation_tree .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);} - -/** Internet explorer specific rules **/ -.ie6 .block_settings_navigation_tree .block_tree .tree_item {width:100%;} \ No newline at end of file diff --git a/blog/lib.php b/blog/lib.php index 596e0d14ea440..4852792748d97 100755 --- a/blog/lib.php +++ b/blog/lib.php @@ -597,8 +597,7 @@ function blog_get_headers() { */ function blog_extend_settings_navigation($settingsnav) { global $USER, $PAGE, $FULLME, $CFG, $DB, $OUTPUT; - $blogkey = $settingsnav->add(get_string('blogadministration', 'blog')); - $blog = $settingsnav->get($blogkey); + $blog = $settingsnav->add(get_string('blogadministration', 'blog')); $blog->forceopen = true; $blog->add(get_string('preferences', 'blog'), new moodle_url('preferences.php'), navigation_node::TYPE_SETTING); @@ -607,7 +606,7 @@ function blog_extend_settings_navigation($settingsnav) { $blog->add(get_string('externalblogs', 'blog'), new moodle_url('external_blogs.php'), navigation_node::TYPE_SETTING); } - return $blogkey; + return $blog; } /** diff --git a/config-dist.php b/config-dist.php index 2c757b82ad3a3..ceb835eb9319e 100644 --- a/config-dist.php +++ b/config-dist.php @@ -217,7 +217,7 @@ // The blocks in this list will be protected from deletion, and this is primarily // used to protect the navigation and settings blocks which can be very hard to // get back if accidentally delete. -// $CFG->undeletableblocktypes = 'global_navigation_tree,settings_navigation_tree'; +// $CFG->undeletableblocktypes = 'navigation,settings'; // // You can specify a different class to be created for the $PAGE global, and to // compute which blocks appear on each page. However, I cannot think of any good diff --git a/course/format/topics/lib.php b/course/format/topics/lib.php index 228e21ee132dc..0279b22b86e99 100644 --- a/course/format/topics/lib.php +++ b/course/format/topics/lib.php @@ -33,8 +33,8 @@ * @param stdClass $modinfo The mod info object for the current course * @return bool Returns true */ -function callback_topics_load_content(&$navigation, $keys, $course) { - $navigation->add_course_section_generic($keys, $course, get_string('topic'), 'topic'); +function callback_topics_load_content(&$navigation, $course, $coursenode) { + return $navigation->load_generic_course_sections($course, $coursenode, get_string('topic'), 'topic'); } /** diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php index ebad925bbb457..2e845171fb1dd 100644 --- a/course/format/weeks/lib.php +++ b/course/format/weeks/lib.php @@ -33,8 +33,8 @@ * @param array $path An array of keys to the course node * @param stdClass $course The course we are loading the section for */ -function callback_weeks_load_content(&$navigation, $keys, $course) { - $navigation->add_course_section_generic($keys, $course, get_string('week'), 'week'); +function callback_weeks_load_content(&$navigation, $course, $coursenode) { + return $navigation->load_generic_course_sections($course, $coursenode, get_string('week'), 'week'); } /** diff --git a/course/loginas.php b/course/loginas.php index 7714fe085a70d..dff72e64e218b 100644 --- a/course/loginas.php +++ b/course/loginas.php @@ -1,83 +1,73 @@ wwwroot); - } + if ($return and isset($_SERVER["HTTP_REFERER"])) { // That's all we wanted to do, so let's go back + redirect($_SERVER["HTTP_REFERER"]); + } else { + redirect($CFG->wwwroot); } +} ///------------------------------------- /// We are trying to log in as this user in the first place - $id = optional_param('id', SITEID, PARAM_INT); // course id - $userid = required_param('user', PARAM_INT); // login as this user - - $url = new moodle_url('/course/loginas.php', array('user'=>$userid, 'sesskey'=>sesskey())); - if ($id !== SITEID) { - $url->param('id', $id); - } - $PAGE->set_url($url); +$id = optional_param('id', SITEID, PARAM_INT); // course id +$userid = required_param('user', PARAM_INT); // login as this user - if (!confirm_sesskey()) { - print_error('confirmsesskeybad'); - } +$url = new moodle_url('/course/loginas.php', array('user'=>$userid, 'sesskey'=>sesskey())); +if ($id !== SITEID) { + $url->param('id', $id); +} +$PAGE->set_url($url); - if (!$course = $DB->get_record('course', array('id'=>$id))) { - print_error("invalidcourseid"); - } +require_sesskey(); +$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); /// User must be logged in - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); +$systemcontext = get_context_instance(CONTEXT_SYSTEM); +$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - require_login(); +require_login(); - if (has_capability('moodle/user:loginas', $systemcontext)) { - if (is_siteadmin($userid)) { - print_error('nologinas'); - } - $context = $systemcontext; - } else { - require_login($course); - require_capability('moodle/user:loginas', $coursecontext); - if (is_siteadmin($userid)) { - print_error('nologinas'); - } - if (!is_enrolled($coursecontext, $userid)) { - print_error('usernotincourse'); - } - $context = $coursecontext; +if (has_capability('moodle/user:loginas', $systemcontext)) { + if (is_siteadmin($userid)) { + print_error('nologinas'); + } + $context = $systemcontext; + $PAGE->set_context($context); +} else { + require_login($course); + require_capability('moodle/user:loginas', $coursecontext); + if (is_siteadmin($userid)) { + print_error('nologinas'); + } + if (!is_enrolled($coursecontext, $userid)) { + print_error('usernotincourse'); } + $context = $coursecontext; +} /// Login as this user and return to course home page. - $oldfullname = fullname($USER, true); - session_loginas($userid, $context); - $newfullname = fullname($USER, true); - - add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&user=$userid", "$oldfullname -> $newfullname"); - - $strloginas = get_string('loginas'); - $strloggedinas = get_string('loggedinas', '', $newfullname); - - $PAGE->set_title($strloggedinas); - $PAGE->navbar->add($strloggedinas); - notice($strloggedinas, "$CFG->wwwroot/course/view.php?id=$course->id"); +$oldfullname = fullname($USER, true); +session_loginas($userid, $context); +$newfullname = fullname($USER, true); +add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&user=$userid", "$oldfullname -> $newfullname"); +$strloginas = get_string('loginas'); +$strloggedinas = get_string('loggedinas', '', $newfullname); +$PAGE->set_title($strloggedinas); +$PAGE->navbar->add($strloggedinas); +notice($strloggedinas, "$CFG->wwwroot/course/view.php?id=$course->id"); \ No newline at end of file diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 55a952ad355ac..1fae728834044 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1094,6 +1094,7 @@ $string['mustconfirm'] = 'You need to confirm your login'; $string['mustchangepassword'] = 'The new password must be different than the current one'; $string['mycourses'] = 'My courses'; +$string['myhome'] = 'My home'; $string['mymoodledashboard'] = 'My Moodle dashboard'; $string['myprofile'] = 'My profile'; $string['name'] = 'Name'; diff --git a/lib/adminlib.php b/lib/adminlib.php index dd00bf3d6042d..b0d79ec51fae9 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -5853,7 +5853,7 @@ function print_plugin_tables() { 'comments', 'course_list', 'course_summary', - 'global_navigation_tree', + 'navigation', 'glossary_random', 'html', 'loancalc', @@ -5871,7 +5871,7 @@ function print_plugin_tables() { 'search', 'search_forums', 'section_links', - 'settings_navigation_tree', + 'settings', 'site_main_menu', 'social_activities', 'tag_flickr', diff --git a/lib/ajax/getnavbranch.php b/lib/ajax/getnavbranch.php index d00b96deee0b1..661ab1469c19c 100644 --- a/lib/ajax/getnavbranch.php +++ b/lib/ajax/getnavbranch.php @@ -41,22 +41,17 @@ $instanceid = optional_param('instance', null, PARAM_INT); // Create a global nav object - $navigation = new global_navigation_for_ajax(); + $navigation = new global_navigation_for_ajax($PAGE); // If set to true then we need to call toggle display $toggledisplay = false; if ($instanceid!==null) { // Get the db record for the block instance - $blockrecords = $DB->get_record('block_instances', array('id'=>$instanceid,'blockname'=>'global_navigation_tree')); + $blockrecords = $DB->get_record('block_instances', array('id'=>$instanceid,'blockname'=>'navigation')); if ($blockrecords!=false) { // Instantiate a block_instance object so we can access congif - $block = block_instance('global_navigation_tree', $blockrecords); + $block = block_instance('navigation', $blockrecords); // Check if the expansion limit config option has been set and isn't the default [everything] - if (!empty($block->config->expansionlimit) && $block->config->expansionlimit > '0') { - // Set the expansion limit - $navigation->expansionlimit = $block->config->expansionlimit; - $toggledisplay = true; - } if (empty($block->config->showemptybranches) || $block->config->showemptybranches=='no') { $navigation->showemptybranches = false; } @@ -66,14 +61,9 @@ // Create a navigation object to use, we can't guarantee PAGE will be complete $expandable = $navigation->initialise($branchtype, $branchid); $converter = new navigation_json(); - - if ($toggledisplay) { - // Toggle display of item types we dont' want to display - $navigation->toggle_type_display($navigation->expansionlimit); - $converter->set_expansionceiling($navigation->expansionlimit); - } + // Find the actuall branch we are looking for - $branch = $navigation->find_child($branchid, $branchtype); + $branch = $navigation->find($branchid, $branchtype); // Stop buffering errors at this point $html = ob_get_contents(); diff --git a/lib/blocklib.php b/lib/blocklib.php index 41a1498dda9a9..e08dd34e8a2ef 100644 --- a/lib/blocklib.php +++ b/lib/blocklib.php @@ -874,7 +874,7 @@ public function edit_controls($block) { global $CFG; if (!isset($CFG->undeletableblocktypes) || (!is_array($CFG->undeletableblocktypes) && !is_string($CFG->undeletableblocktypes))) { - $CFG->undeletableblocktypes = array('global_navigation_tree','settings_navigation_tree'); + $CFG->undeletableblocktypes = array('navigation','settings'); } else if (is_string($CFG->undeletableblocktypes)) { $CFG->undeletableblocktypes = explode(',', $CFG->undeletableblocktypes); } @@ -1775,6 +1775,6 @@ function blocks_add_default_course_blocks($course) { function blocks_add_default_system_blocks() { $page = new moodle_page(); $page->set_context(get_context_instance(CONTEXT_SYSTEM)); - $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('global_navigation_tree', 'settings_navigation_tree')), '*', null, true); + $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('navigation', 'settings')), '*', null, true); $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_bookmarks')), 'admin-*', null, null, 2); } diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index ace8794dfd464..3598fcf0e351d 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -19,6 +19,15 @@ // Please do not forget to use upgrade_set_timeout() // before any action that may take longer time to finish. +/** + * + * @global stdClass $CFG + * @global stdClass $USER + * @global moodle_database $DB + * @global core_renderer $OUTPUT + * @param int $oldversion + * @return bool + */ function xmldb_main_upgrade($oldversion) { global $CFG, $USER, $DB, $OUTPUT; @@ -3497,6 +3506,17 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint($result, 2010041300); } + if ($result && $oldversion < 2010041301) { + $sql = "UPDATE {block} SET name=? WHERE name=?"; + $DB->execute($sql, array('navigation', 'global_navigation_tree')); + $DB->execute($sql, array('settings', 'settings_navigation_tree')); + + $sql = "UPDATE {block_instances} SET blockname=? WHERE blockname=?"; + $DB->execute($sql, array('navigation', 'global_navigation_tree')); + $DB->execute($sql, array('settings', 'settings_navigation_tree')); + upgrade_main_savepoint($result, 2010041301); + } + return $result; } diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 5358c45d7ec78..c0c7a28cac186 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -50,31 +50,35 @@ * @copyright 2009 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class navigation_node { - /** Used to identify this node a leaf (default) */ +class navigation_node implements renderable { + /** @var int Used to identify this node a leaf (default) 0 */ const NODETYPE_LEAF = 0; - /** Used to identify this node a branch, happens with children */ + /** @var int Used to identify this node a branch, happens with children 1 */ const NODETYPE_BRANCH = 1; - /** Unknown node type */ + /** @var null Unknown node type null */ const TYPE_UNKNOWN = null; - /** System node type */ - const TYPE_SYSTEM = 0; - /** Category node type */ + /** @var int System node type 0 */ + const TYPE_ROOTNODE = 0; + /** @var int System node type 1 */ + const TYPE_SYSTEM = 1; + /** @var int Category node type 10 */ const TYPE_CATEGORY = 10; - /** Course node type */ + /** @var int Course node type 20 */ const TYPE_COURSE = 20; - /** Course Structure node type */ + /** @var int Course Structure node type 30 */ const TYPE_SECTION = 30; - /** Activity node type, e.g. Forum, Quiz */ + /** @var int Activity node type, e.g. Forum, Quiz 40 */ const TYPE_ACTIVITY = 40; - /** Resource node type, e.g. Link to a file, or label */ + /** @var int Resource node type, e.g. Link to a file, or label 50 */ const TYPE_RESOURCE = 50; - /** A custom node type, default when adding without specifing type */ + /** @var int A custom node type, default when adding without specifing type 60 */ const TYPE_CUSTOM = 60; - /** Setting node type, used only within settings nav */ + /** @var int Setting node type, used only within settings nav 70 */ const TYPE_SETTING = 70; - /** Setting node type, used only within settings nav */ + /** @var int Setting node type, used only within settings nav 80 */ const TYPE_USER = 80; + /** @var int Setting node type, used for containers of no importance 90 */ + const TYPE_CONTAINER = 90; /** @var int Parameter to aid the coder in tracking [optional] */ public $id = null; @@ -88,7 +92,7 @@ class navigation_node { public $title = null; /** @var string A string that can be used to build a help button */ public $helpbutton = null; - /** @var moodle_url|string|null An action for the node (link) */ + /** @var moodle_url|action_link|null An action for the node (link) */ public $action = null; /** @var pix_icon The path to an icon to use for this node */ public $icon = null; @@ -100,13 +104,13 @@ class navigation_node { public $collapse = false; /** @var bool If set to true the node will be expanded by default */ public $forceopen = false; - /** @var string An array of CSS classes for the node */ + /** @var array An array of CSS classes for the node */ public $classes = array(); - /** @var array An array of child nodes */ + /** @var navigation_node_collection An array of child nodes */ public $children = array(); /** @var bool If set to true the node will be recognised as active */ public $isactive = false; - /** @var string If set to true the node will be dimmed */ + /** @var bool If set to true the node will be dimmed */ public $hidden = false; /** @var bool If set to false the node will not be displayed */ public $display = true; @@ -116,6 +120,8 @@ class navigation_node { public $mainnavonly = false; /** @var bool If set to true a title will be added to the action no matter what */ public $forcetitle = false; + /** @var navigation_node A reference to the node parent */ + public $parent = null; /** @var array */ protected $namedtypes = array(0=>'system',10=>'category',20=>'course',30=>'structure',40=>'activity',50=>'resource',60=>'custom',70=>'setting', 80=>'user'); /** @var moodle_url */ @@ -124,54 +130,27 @@ class navigation_node { public static $autofindactive = true; /** - * Establish the node, with either text string or array or properites + * Constructs a new navigation_node * - * Called when first creating the node, requires one argument which can be either - * a string containing the text for the node or an array or properties one of - * which must be text. - * - * - * $PAGE->navigation->newitem = 'This is a new nav item'; - * // or - * $properties = array() - * $properties['text'] = 'This is a new nav item'; - * $properties['short'] = 'This is a new nav item'; - * $properties['action'] = moodle_url($CFG->wwwroot.'/course/category.php'); - * $properties['icon'] = new pix_icon('i/course', ''); - * $properties['type'] = navigation_node::TYPE_COURSE; - * $properties['key'] = 'newitem'; - * $PAGE->navigation->newitem = $properties; - * - * - * The following are properties that must/can be set in the properties array - *
    - *
  • text: You must set text, if this is not set a coding exception is thrown.
  • - *
  • short optional: A short description used for navbar optional.
  • - *
  • action optional: This can be either a {@link moodle_url} for a link, or string that can be directly output in instead of the text.
  • - *
  • icon optional: The path to an icon to display with the node.
  • - *
  • type optional: This type of the node, defaults to TYPE_CUSTOM.
  • - *
  • key optional: This can be set to allow you to easily retrieve a node you have created.
  • - *
- * - * @param string|array $properties + * @param array|string $properties Either an array of properties or a string to use + * as the text for the node */ public function __construct($properties) { if (is_array($properties)) { + // Check the array for each property that we allow to set at construction. + // text - The main content for the node + // shorttext - A short text if required for the node + // icon - The icon to display for the node + // type - The type of the node + // key - The key to use to identify the node + // parent - A reference to the nodes parent + // action - The action to attribute to this node, usually a URL to link to if (array_key_exists('text', $properties)) { $this->text = $properties['text']; } if (array_key_exists('shorttext', $properties)) { $this->shorttext = $properties['shorttext']; } - if (array_key_exists('action', $properties)) { - $this->action = $properties['action']; - if (is_string($this->action)) { - $this->action = new moodle_url($this->action); - } - if (self::$autofindactive) { - $this->check_if_active(); - } - } if (array_key_exists('icon', $properties)) { $this->icon = $properties['icon']; if ($this->icon instanceof pix_icon) { @@ -190,147 +169,224 @@ public function __construct($properties) { if (array_key_exists('key', $properties)) { $this->key = $properties['key']; } + if (array_key_exists('parent', $properties)) { + $this->parent = $properties['parent']; + } + // This needs to happen last because of the check_if_active call that occurs + if (array_key_exists('action', $properties)) { + $this->action = $properties['action']; + if (is_string($this->action)) { + $this->action = new moodle_url($this->action); + } + if (self::$autofindactive) { + $this->check_if_active(); + } + } } else if (is_string($properties)) { $this->text = $properties; } if ($this->text === null) { throw new coding_exception('You must set the text for the node when you create it.'); } + // Default the title to the text $this->title = $this->text; $textlib = textlib_get_instance(); if (strlen($this->text)>50) { + // Truncate the text to 50 characters $this->text = $textlib->substr($this->text, 0, 50).'...'; } if (is_string($this->shorttext) && strlen($this->shorttext)>25) { + // Truncate the shorttext $this->shorttext = $textlib->substr($this->shorttext, 0, 25).'...'; } + // Instantiate a new navigation node collection for this nodes children + $this->children = new navigation_node_collection(); } /** - * This function overrides the active URL that is used to compare new nodes - * to find out if they are active. - * - * If null is passed then $fullmeurl will be regenerated when the next node - * is created/added - */ - public static function override_active_url(moodle_url $url=null) { - self::$fullmeurl = $url; - } - - /** - * This function checks if the node is the active child by comparing its action - * to the current page URL obtained via $PAGE->url - * - * This function compares the nodes url to the static var {@link navigation_node::fullmeurl} - * and if they match (based on $strenght) then the node is considered active. + * Checks if this node is the active node. * - * Note: This function is recursive, when you call it it will check itself and all - * children recursivily. + * This is determined by comparing the action for the node against the + * defined URL for the page. A match will see this node marked as active. * - * @staticvar moodle_url $fullmeurl - * @param int $strength When using the moodle_url compare function how strictly - * to check for a match. Defaults to URL_MATCH_EXACT - * Can be URL_MATCH_EXACT or URL_MATCH_BASE - * @return bool True is active, false otherwise + * @global string $FULLME + * @global moodle_page $PAGE + * @param int $strength One of URL_MATCH_EXACT, URL_MATCH_PARAMS, or URL_MATCH_BASE + * @return bool */ public function check_if_active($strength=URL_MATCH_EXACT) { global $FULLME, $PAGE; + // Set fullmeurl if it hasn't already been set if (self::$fullmeurl == null) { if ($PAGE->has_set_url()) { - $this->override_active_url(new moodle_url($PAGE->url)); + self::override_active_url(new moodle_url($PAGE->url)); } else { - $this->override_active_url(new moodle_url($FULLME)); + self::override_active_url(new moodle_url($FULLME)); } } + // Compare the action of this node against the fullmeurl if ($this->action instanceof moodle_url && $this->action->compare(self::$fullmeurl, $strength)) { $this->make_active(); return true; - } else if (is_string($this->action) && $this->action==self::$fullmeurl->out()) { - $this->make_active(); - return true; } return false; } + /** - * This function allows the user to add a child node to this node. + * Overrides the fullmeurl variable providing * - * @param string $text The text to display in the node - * @param string $action Either a moodle_url or a bit of html to use instead of the text optional - * @param int $type The type of node should be one of the const types of navigation_node optional - * @param string $shorttext The short text to use for this node - * @param string|int $key Sets the key that can be used to retrieve this node optional - * @param string $icon The path to an icon to use for this node optional - * @return string The key that was used for this node + * @param moodle_url $url The url to use for the fullmeurl. + */ + public static function override_active_url(moodle_url $url) { + self::$fullmeurl = $url; + } + + /** + * Adds a navigation node as a child of this node. + * + * @param string $text + * @param moodle_url|action_link $action + * @param int $type + * @param string $shorttext + * @param string|int $key + * @param pix_icon $icon + * @return navigation_node */ - public function add($text, $action=null, $type=null, $shorttext=null, $key=null, pix_icon $icon=null) { + public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) { + // First convert the nodetype for this node to a branch as it will now have children if ($this->nodetype !== self::NODETYPE_BRANCH) { $this->nodetype = self::NODETYPE_BRANCH; } - $itemarray = array('text'=>$text); - if ($type!==null) { - $itemarray['type'] = $type; - } else { - $type = self::TYPE_CUSTOM; - } + // Properties array used when creating the new navigation node + $itemarray = array( + 'text' => $text, + 'type' => $type + ); + // Set the action if one was provided if ($action!==null) { $itemarray['action'] = $action; } - + // Set the shorttext if one was provided if ($shorttext!==null) { $itemarray['shorttext'] = $shorttext; } + // Set the icon if one was provided if ($icon!==null) { $itemarray['icon'] = $icon; } - if ($key===null) { - $key = count($this->children); + // Default the key to the number of children if not provided + if ($key === null) { + $key = $this->children->count(); } - - $key = $key.':'.$type; - + // Set the key $itemarray['key'] = $key; - $this->children[$key] = new navigation_node($itemarray); + // Set the parent to this node + $itemarray['parent'] = $this; + // Add the child using the navigation_node_collections add method + $node = $this->children->add(new navigation_node($itemarray)); + // If the node is a category node or the user is logged in and its a course + // then mark this node as a branch (makes it expandable by AJAX) if (($type==self::TYPE_CATEGORY) || (isloggedin() && $type==self::TYPE_COURSE)) { - $this->children[$key]->nodetype = self::NODETYPE_BRANCH; + $node->nodetype = self::NODETYPE_BRANCH; } + // If this node is hidden mark it's children as hidden also if ($this->hidden) { - $this->children[$key]->hidden = true; + $node->hidden = true; } - return $key; + // Return the node (reference returned by $this->children->add() + return $node; } /** - * Adds a new node to a particular point by recursing through an array of node keys + * Searches for a node of the given type with the given key. * - * @param array $patharray An array of keys to recurse to find the correct node - * @param string $text The text to display in the node - * @param string|int $key Sets the key that can be used to retrieve this node optional - * @param int $type The type of node should be one of the const types of navigation_node optional - * @param string $action Either a moodle_url or a bit of html to use instead of the text optional - * @param string $icon The path to an icon to use for this node optional - * @return mixed Either the key used for the node once added or false for failure + * This searches this node plus all of its children, and their children.... + * If you know the node you are looking for is a child of this node then please + * use the get method instead. + * + * @param int|string $key The key of the node we are looking for + * @param int $type One of navigation_node::TYPE_* + * @return navigation_node|false */ - public function add_to_path($patharray, $key=null, $text=null, $shorttext=null, $type=null, $action=null, pix_icon $icon=null) { - if (count($patharray)==0) { - $key = $this->add($text, $action, $type, $shorttext, $key, $icon); - return $key; - } else { - $pathkey = array_shift($patharray); - $child = $this->get($pathkey); - if ($child!==false) { - return $child->add_to_path($patharray, $key, $text, $shorttext, $type, $action, $icon); - } else { - return false; - } + public function find($key, $type) { + return $this->children->find($key, $type); + } + + /** + * Get ths child of this node that has the given key + (optional) type. + * + * If you are looking for a node and want to search all children + thier children + * then please use the find method instead. + * + * @param int|string $key The key of the node we are looking for + * @param int $type One of navigation_node::TYPE_* + * @return navigation_node|false + */ + public function get($key, $type=null) { + return $this->children->get($key, $type); + } + + /** + * Removes this node. + * + * @return bool + */ + public function remove() { + return $this->parent->children->remove($this->key, $this->type); + } + + /** + * Checks if this node has or could have any children + * + * @return bool Returns true if it has children or could have (by AJAX expansion) + */ + public function has_children() { + return ($this->nodetype === navigation_node::NODETYPE_BRANCH || $this->children->count()>0); + } + + /** + * Marks this node as active and forces it open. + */ + public function make_active() { + $this->isactive = true; + $this->add_class('active_tree_node'); + $this->force_open(); + if ($this->parent !== null) { + $this->parent->make_inactive(); + } + } + + /** + * Marks a node as inactive and recusised back to the base of the tree + * doing the same to all parents. + */ + public function make_inactive() { + $this->isactive = false; + $this->remove_class('active_tree_node'); + if ($this->parent !== null) { + $this->parent->make_inactive(); } } /** - * Add a css class to this particular node + * Forces this node to be open and at the same time forces open all + * parents until the root node. * - * @param string $class The css class to add - * @return bool Returns true + * Recursive. + */ + public function force_open() { + $this->forceopen = true; + if ($this->parent !== null) { + $this->parent->force_open(); + } + } + + /** + * Adds a CSS class to this node. + * + * @param string $class + * @return bool */ public function add_class($class) { if (!in_array($class, $this->classes)) { @@ -340,10 +396,10 @@ public function add_class($class) { } /** - * Removes a given class from this node if it exists + * Removes a CSS class from this node. * * @param string $class - * @return bool + * @return bool True if the class was successfully removed. */ public function remove_class($class) { if (in_array($class, $this->classes)) { @@ -357,115 +413,97 @@ public function remove_class($class) { } /** - * Recurse down child nodes and collapse everything once a given - * depth of recursion has been reached. - * - * This function is used internally during the initialisation of the nav object - * after the tree has been generated to collapse it to a suitable depth. + * Sets the title for this node and forces Moodle to utilise it. + * @param string $title + */ + public function title($title) { + $this->title = $title; + $this->forcetitle = true; + } + + /** + * Resets the page specific information on this node if it is being unserialised. + */ + public function __wakeup(){ + $this->forceopen = false; + $this->isactive = false; + $this->remove_class('active_tree_node'); + } + + /** + * Checks if this node or any of its children contain the active node. + * + * Recursive. * - * @param int $depth defualts to 2 - * @return bool Returns true + * @return bool */ - protected function collapse_at_depth($depth=2) { - if ($depth>0 && $this->nodetype===self::NODETYPE_BRANCH) { - foreach (array_keys($this->children) as $key) { - $this->children[$key]->collapse_at_depth($depth-1); - } + public function contains_active_node() { + if ($this->isactive) { return true; } else { - $this->collapse_children(); - return true; + foreach ($this->children as $child) { + if ($child->isactive || $child->contains_active_node()) { + return true; + } + } } + return false; } /** - * Collapses all of the child nodes recursion optional + * Finds the active node. + * + * Searches this nodes children plus all of the children for the active node + * and returns it if found. * - * @param bool $recurse If set to true child nodes are closed recursively - * @return bool Returns true + * Recursive. + * + * @return navigation_node|false */ - protected function collapse_children($recurse=true) { - if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) { + public function find_active_node() { + if ($this->isactive) { + return $this; + } else { foreach ($this->children as &$child) { - if (!$this->forceopen) { - $child->collapse = true; - } - if ($recurse && $child instanceof navigation_node) { - $child->collapse_children($recurse); + $outcome = $child->find_active_node(); + if ($outcome !== false) { + return $outcome; } } - unset($child); } - return true; + return false; } /** - * Produce the actual HTML content for the node including any action or icon + * Gets the content for this node. * - * @param bool $shorttext If true then short text is used rather than text if it has been set - * @return string The HTML content + * @param bool $shorttext If true shorttext is used rather than the normal text + * @return string */ - public function content($shorttext=false) { - global $OUTPUT; - if (!$this->display) { - return ''; - } + public function get_content($shorttext=false) { if ($shorttext && $this->shorttext!==null) { - $content = format_string($this->shorttext); + return format_string($this->shorttext); } else { - $content = format_string($this->text); - } - $title = ''; - if ($this->forcetitle || ($this->shorttext!==null && $this->title !== $this->shorttext) || $this->title !== $this->text) { - $title = $this->title; - } - - if ($this->icon instanceof renderable) { - $icon = $OUTPUT->render($this->icon); - $content = $icon.' '.$content; // use CSS for spacing of icons - } else if ($this->helpbutton !== null) { - $content = trim($this->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton')); + return format_string($this->text); } + } - if ($content === '') { + /** + * Gets the title to use for this node. + * + * @return string + */ + public function get_title() { + if ($this->forcetitle || ($this->shorttext!==null && $this->title !== $this->shorttext) || $this->title !== $this->text) { + return $this->title; + } else { return ''; } - - if ($this->action instanceof action_link) { - //TODO: to be replaced with something else - $link = $this->action; - if ($this->hidden) { - $link->add_class('dimmed'); - } - $content = $OUTPUT->render($link); - - } else if ($this->action instanceof moodle_url) { - $attributes = array(); - if ($title !== '') { - $attributes['title'] = $title; - } - if ($this->hidden) { - $attributes['class'] = 'dimmed_text'; - } - $content = html_writer::link($this->action, $content, $attributes); - - } else if (is_string($this->action) || empty($this->action)) { - $attributes = array(); - if ($title !== '') { - $attributes['title'] = $title; - } - if ($this->hidden) { - $attributes['class'] = 'dimmed_text'; - } - $content = html_writer::tag('span', $content, $attributes); - } - - return $content; } /** - * Get the CSS type for this node - * + * Gets the CSS class to add to this node to describe its type + * * @return string */ public function get_css_type() { @@ -476,149 +514,145 @@ public function get_css_type() { } /** - * Find and return a child node if it exists (returns a reference to the child) + * Finds all nodes that are expandable by AJAX * - * This function is used to search for and return a reference to a child node when provided - * with the child nodes key and type. - * If the child is found a reference to it is returned otherwise the default is returned. - * - * @param string|int $key The key of the child node you are searching for. - * @param int $type The type of the node you are searching for. Defaults to TYPE_CATEGORY - * @param mixed $default The value to return if the child cannot be found - * @return mixed The child node or what ever default contains (usually false) + * @param array $expandable An array by reference to populate with expandable nodes. */ - public function find_child($key, $type=self::TYPE_CATEGORY, $default = false) { - list($key, $type) = $this->split_key_type($key, $type); - if (array_key_exists($key.":".$type, $this->children)) { - return $this->children[$key.":".$type]; - } else if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0 && $this->type<=$type) { - foreach ($this->children as &$child) { - $outcome = $child->find_child($key, $type); - if ($outcome !== false) { - return $outcome; - } + public function find_expandable(array &$expandable) { + if (!isloggedin()) { + return; + } + foreach ($this->children as &$child) { + if ($child->nodetype == self::NODETYPE_BRANCH && $child->children->count()==0) { + $child->id = 'expandable_branch_'.(count($expandable)+1); + $this->add_class('canexpand'); + $expandable[] = array('id'=>$child->id,'branchid'=>$child->key,'type'=>$child->type); } + $child->find_expandable($expandable); } - return $default; } +} +/** + * Navigation node collection + * + * This class is responsible for managing a collection of navigation nodes. + * It is required because a node's unique identifier is a combination of both its + * key and its type. + * + * Originally an array was used with a string key that was a combination of the two + * however it was decided that a better solution would be to use a class that + * implements the standard IteratorAggregate interface. + * + * @package moodlecore + * @subpackage navigation + * @copyright 2010 Sam Hemelryk + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class navigation_node_collection implements IteratorAggregate { /** - * Find the active child - * - * @param null|int $type - * @return navigation_node|bool + * A multidimensional array to where the first key is the type and the second + * key is the nodes key. + * @var array */ - public function find_active_node($type=null) { - if ($this->contains_active_node()) { - if ($type!==null && $this->type===$type) { - return $this; - } - if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) { - foreach ($this->children as $child) { - if ($child->isactive && !$child->contains_active_node()) { - return $child; - } else { - $outcome = $child->find_active_node($type); - if ($outcome!==false) { - return $outcome; - } - } - } - } - } - return false; - } - + protected $collection = array(); /** - * Returns the depth of a child - * - * @param string|int $key The key for the child we are looking for - * @param int $type The type of the child we are looking for - * @return int The depth of the child once found + * An array that contains references to nodes in the same order they were added. + * This is maintained as a progressive array. + * @var array */ - public function find_child_depth($key, $type=self::TYPE_CATEGORY) { - $depth = 0; - list($key, $type) = $this->split_key_type($key, $type); - if (array_key_exists($key.':'.$type, $this->children)) { - $depth = 1; - } else if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0 && $this->type<=$type) { - foreach ($this->children as $child) { - $depth += $child->find_child_depth($key, $type); - } - } - return $depth; - } - + protected $orderedcollection = array(); + /** + * A reference to the last node that was added to the collection + * @var navigation_node + */ + protected $last = null; + /** + * The total number of items added to this array. + * @var int + */ + protected $count = 0; /** - * Finds all nodes that have the specified type + * Adds a navigation node to the collection * - * @param int $type One of navigation_node::TYPE_* - * @return array An array of navigation_node references for nodes of type $type + * @global stdClass $CFG + * @param navigation_node $node + * @return navigation_node */ - public function get_children_by_type($type) { - $nodes = array(); - if (count($this->children)>0) { - foreach ($this->children as &$child) { - if ($child->type === $type) { - $nodes[] = $child; - } - } + public function add(navigation_node $node) { + global $CFG; + $key = $node->key; + $type = $node->type; + // First check we have a 2nd dimension for this type + if (!array_key_exists($type, $this->orderedcollection)) { + $this->orderedcollection[$type] = array(); } - return $nodes; + // Check for a collision and report if debugging is turned on + if ($CFG->debug && array_key_exists($key, $this->orderedcollection[$type])) { + debugging('Navigation node intersect: Adding a node that already exists '.$key, DEBUG_DEVELOPER); + } + // Add the node to the appropriate place in the ordered structure. + $this->orderedcollection[$type][$key] = $node; + // Add a reference to the node to the progressive collection. + $this->collection[$this->count] = &$this->orderedcollection[$type][$key]; + // Update the last property to a reference to this new node. + $this->last = &$this->orderedcollection[$type][$key]; + $this->count++; + // Return the reference to the now added node + return $this->last; } /** - * Finds all nodes (recursivily) that have the specified type, regardless of - * assumed order or position. + * Fetches a node from this collection. * - * @param int $type One of navigation_node::TYPE_* - * @return array An array of navigation_node references for nodes of type $type + * @param string|int $key The key of the node we want to find. + * @param int $type One of navigation_node::TYPE_*. + * @return navigation_node|null */ - public function find_children_by_type($type) { - $nodes = array(); - if (count($this->children)>0) { - foreach ($this->children as &$child) { - if ($child->type === $type) { - $nodes[] = $child; - } - if (count($child->children)>0) { - $nodes = array_merge($nodes, $child->find_children_by_type($type)); + public function get($key, $type=null) { + if ($type !== null) { + // If the type is known then we can simply check and fetch + if (!empty($this->orderedcollection[$type][$key])) { + return $this->orderedcollection[$type][$key]; + } + } else { + // Because we don't know the type we look in the progressive array + foreach ($this->collection as $node) { + if ($node->key === $key) { + return $node; } } } - return $nodes; + return false; } - /** - * Toogles display of nodes and child nodes based on type + * Searches for a node with matching key and type. * - * If the type of a node if more than the type specified it's display property is set to false - * and it is not shown + * This function searches both the nodes in this collection and all of + * the nodes in each collection belonging to the nodes in this collection. * - * @param int $type - * @param bool $display - */ - public function toggle_type_display($type=self::TYPE_COURSE, $display=false) { - if ((int)$this->type > $type) { - $this->display = $display; - } - if (count($this->children)>0) { - foreach ($this->children as $child) { - $child->toggle_type_display($type, $display); - } - } - } - - /** - * Find out if a child (or subchild) of this node contains an active node + * Recursive. * - * @return bool True if it does fales otherwise + * @param string|int $key The key of the node we want to find. + * @param int $type One of navigation_node::TYPE_*. + * @return navigation_node|null */ - public function contains_active_node() { - if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) { - foreach ($this->children as $child) { - if ($child->isactive || $child->contains_active_node()) { - return true; + public function find($key, $type=null) { + if ($type !== null && array_key_exists($type, $this->orderedcollection) && array_key_exists($key, $this->orderedcollection[$type])) { + return $this->orderedcollection[$type][$key]; + } else { + $nodes = $this->getIterator(); + // Search immediate children first + foreach ($nodes as &$node) { + if ($node->key == $key && ($type == null || $type === $node->type)) { + return $node; + } + } + // Now search each childs children + foreach ($nodes as &$node) { + $result = $node->children->find($key, $type); + if ($result !== false) { + return $result; } } } @@ -626,218 +660,62 @@ public function contains_active_node() { } /** - * Find all nodes that are expandable for this node and its given children. - * - * This function recursively finds all nodes that are expandable by AJAX within - * [and including] this child. - * - * @param array $expandable An array to fill with the HTML id's of all branches - * that can be expanded by AJAX. This is a forced reference. - * @param int $expansionlimit Optional/used internally can be one of navigation_node::TYPE_* + * Fetches the last node that was added to this collection + * + * @return navigation_node */ - public function find_expandable(&$expandable, $expansionlimit = null) { - static $branchcount; - if ($branchcount==null) { - $branchcount=1; - } - if ($this->nodetype == self::NODETYPE_BRANCH && count($this->children)==0 && ($expansionlimit === null || $this->type < $expansionlimit)) { - $this->id = 'expandable_branch_'.$branchcount; - $this->add_class('canexpand'); - $branchcount++; - $expandable[] = array('id'=>$this->id,'branchid'=>$this->key,'type'=>$this->type); - } else if ($this->nodetype==self::NODETYPE_BRANCH && ($expansionlimit === null || $this->type <= $expansionlimit)) { - foreach ($this->children as $child) { - $child->find_expandable($expandable, $expansionlimit); - } + public function last() { + return $this->last; + } + /** + * Fetches all nodes of a given type from this collection + */ + public function type($type) { + if (!array_key_exists($type, $this->orderedcollection)) { + $this->orderedcollection[$type] = array(); } + return $this->orderedcollection[$type]; } - /** - * Used to return a child node with a given key + * Removes the node with the given key and type from the collection * - * This function searchs for a child node with the provided key and returns the - * child. If the child doesn't exist then this function returns false. - * - * @param int|string $key The key to search for - * @param int $type Optional one of TYPE_* constants - * @param navigation_node|bool The child if it exists or false + * @param string|int $key + * @param int $type + * @return bool */ - public function get($key, $type=null) { - if ($key===false) { - return false; - } - list($key, $type) = $this->split_key_type($key); - if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) { - if ($type!==null) { - if (array_key_exists($key.':'.$type, $this->children)) { - return $this->children[$key.':'.$type]; - } - } else { - foreach (array_keys($this->children) as $childkey) { - if (strpos($childkey, $key.':')===0) { - return $this->children[$childkey]; - } + public function remove($key, $type=null) { + $child = $this->get($key, $type); + if ($child !== false) { + foreach ($this->collection as $colkey => $node) { + if ($node->key == $key && $node->type == $type) { + unset($this->collection[$colkey]); + break; } } + unset($this->orderedcollection[$child->type][$child->key]); + $this->count--; + return true; } return false; } /** - * This function is used to split a key into its key and value parts if the - * key is a combination of the two. - * - * Was introduced to help resolve MDL-20543 - * - * @param string $key - * @param int|null $type - * @return array + * Gets the number of nodes in this collection + * @return int */ - protected function split_key_type($key, $type=null) { - /** - * If the key is a combination it will be of the form `key:type` where key - * could be anything and type will be an int value - */ - if (preg_match('#^(.*)\:(\d{1,3})$#', $key, $match)) { - /** - * If type is null then we want to collect and return the type otherwise - * we will use the provided type. This ensures that if a type was specified - * it is not lost - */ - if ($type===null) { - $type = $match[2]; - } - $key = $match[1]; - } - return array($key, $type); + public function count() { + return count($this->collection); } - /** - * Fetch a node given a set of keys that describe its path + * Gets an array iterator for the collection. + * + * This is required by the IteratorAggregator interface and is used by routines + * such as the foreach loop. * - * @param array $keys An array of keys - * @return navigation_node|bool The node or false + * @return ArrayIterator */ - public function get_by_path($keys) { - if (count($keys)==1) { - $key = array_shift($keys); - return $this->get($key); - } else { - $key = array_shift($keys); - $child = $this->get($key); - - if ($child !== false) { - return $child->get_by_path($keys); - } - return false; - } - } - - /** - * Returns the child marked as active if there is one, false otherwise. - * - * @return navigation_node|bool The active node or false - */ - public function get_active_node() { - foreach ($this->children as $child) { - if ($child->isactive) { - return $child; - } - } - return false; - } - - /** - * Mark this node as active - * - * This function marks the node as active my forcing the node to be open, - * setting isactive to true, and adding the class active_tree_node - */ - public function make_active() { - $this->forceopen = true; - $this->isactive = true; - $this->add_class('active_tree_node'); - } - - /** - * This intense little function looks for branches that are forced open - * and checks to ensure that all parent nodes are also forced open. - */ - public function respect_forced_open() { - foreach ($this->children as $child) { - $child->respect_forced_open(); - if ($child->forceopen) { - $this->forceopen = true; - } - } - } - - /** - * This function simply removes a given child node - * - * @param string|int $key The key that identifies a child node - * @return bool - */ - public function remove_child($key, $type=null) { - if ($key instanceof navigation_node) { - $key = $key->key; - } - $child = $this->get($key, $type); - if ($child) { - unset($this->children[$child->key]); - return true; - } - return false; - } - - /** - * Iterate all children and check if any of them are active - * - * This function iterates all children recursively until it sucecssfully marks - * a node as active, or gets to the end of the tree. - * This can be used on a cached branch to mark the active child. - * - * @param int $strength When using the moodle_url compare function how strictly - * to check for a match. Defaults to URL_MATCH_EXACTLY - * @return bool True is a node was marked active false otherwise - */ - public function reiterate_active_nodes($strength=URL_MATCH_EXACT) { - if ($this->nodetype !== self::NODETYPE_BRANCH) { - return false; - } - foreach ($this->children as $child) { - $outcome = $child->check_if_active($strength); - if (!$outcome && $child->nodetype === self::NODETYPE_BRANCH) { - $outcome = $child->reiterate_active_nodes($strength); - } - if ($outcome) { - $this->forceopen = true; - return true; - } - } - } - - /** - * This function sets the title for the node and at the same time sets - * forcetitle to true to ensure that it is used if possible - * - * @param string $title - */ - public function title($title) { - $this->title = $title; - $this->forcetitle = true; - } - - /** - * Magic Method: When we unserialise an object make it `unactive` - * - * This is to ensure that when we take a branch out of the cache it is not marked - * active anymore, as we can't be sure it still is (infact it most likely isnt) - */ - public function __wakeup(){ - $this->forceopen = false; - $this->isactive = false; - $this->remove_class('active_tree_node'); + public function getIterator() { + return new ArrayIterator($this->collection); } } @@ -859,143 +737,437 @@ public function __wakeup(){ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class global_navigation extends navigation_node { - /** @var int */ - protected $depthforward = 1; - /** @var cache */ - protected $cache = null; + /** + * The Moodle page this navigation object belongs to. + * @var moodle_page + */ + protected $page; /** @var bool */ protected $initialised = false; - - /** @var null|int */ - public $expansionlimit = null; - /** @var stdClass */ - public $context = null; - /** @var mixed */ - public $expandable = null; + /** @var array */ + protected $mycourses = array(); + /** @var array */ + protected $rootnodes = array(); /** @var bool */ - public $showemptybranches = true; - /** @var bool */ - protected $isloggedin = false; - /** @var array Array of user objects to extend the navigation with */ + protected $showemptysections = false; + /** @var array */ protected $extendforuser = array(); - /** @var bool Gets set to true if all categories have been loaded */ - protected $allcategoriesloaded = false; - + /** @var navigation_cache */ + protected $cache; + /** @var array */ + protected $addedcourses = array(); /** - * Sets up the object with basic settings and preparse it for use + * Constructs a new global navigation + * + * @global stdClass $SITE + * @global stdClass $USER + * @param moodle_page $page The page this navigation object belongs to */ - public function __construct() { - global $CFG; + public function __construct(moodle_page $page) { + global $SITE, $USER; + if (during_initial_install()) { - return false; + return; } - $this->key = 0; - $this->type = self::TYPE_SYSTEM; - $this->isloggedin = isloggedin(); - $this->text = get_string('home'); + + // Use the parents consturctor.... good good reuse + $properties = array( + 'key' => 'home', + 'type' => navigation_node::TYPE_SYSTEM, + 'text' => get_string('myhome'), + 'action' => new moodle_url('/my/') + ); + parent::__construct($properties); + + // Initalise and set defaults + $this->page = $page; $this->forceopen = true; - $this->action = new moodle_url($CFG->wwwroot); $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); + + // Check if we need to clear the cache $regenerate = optional_param('regenerate', null, PARAM_TEXT); - if ($regenerate==='navigation') { + if ($regenerate === 'navigation') { $this->cache->clear(); } } /** - * Override: This function generated the content of the navigation + * Initialises the navigation object. * - * If an expansion limit has been set then we hide everything to after that - * set limit type + * This causes the navigation object to look at the current state of the page + * that it is associated with and then load the appropriate content. * - * @return string - */ - public function content() { - if ($this->expansionlimit!==null) { - $this->toggle_type_display($this->expansionlimit); - } - return parent::content(); - } - - /** - * Initialise the navigation object, calling it to auto generate - * - * This function starts the navigation object automatically retrieving what it - * needs from Moodle objects. + * This should only occur the first time that the navigation structure is utilised + * which will normally be either when the navbar is called to be displayed or + * when a block makes use of it. * - * It also passed Javascript args and function calls as required - * - * @return bool Returns true + * @global stdClass $SITE + * @global stdClass $USER + * @return bool */ - public function initialise($jsargs = null) { - global $PAGE, $SITE, $USER; + public function initialise() { + global $SITE, $USER; + // Check if it has alread been initialised if ($this->initialised || during_initial_install()) { return true; } - $start = microtime(false); - $this->depthforward = 1; - $this->context = $PAGE->context; - $contextlevel = $this->context->contextlevel; - if ($contextlevel == CONTEXT_COURSE && $PAGE->course->id==$SITE->id) { - $contextlevel = 10; - } - $depth = 0; - - /** - * We always want to load the front page activities into the tree, these - * will appear at the bottom of the opening (site) node. - */ - $sitekeys = array(); - $this->load_course_activities($sitekeys, $SITE); - $this->load_section_activities($sitekeys, false, $SITE); - switch ($contextlevel) { - case CONTEXT_SYSTEM: - $this->cache->volatile(); - $depth = $this->load_for_category(false); - break; - case CONTEXT_COURSECAT: - $depth = $this->load_for_category(); + + // Set up the five base root nodes. These are nodes where we will put our + // content and are as follows: + // site: Navigation for the front page. + // myprofile: User profile information goes here. + // mycourses: The users courses get added here. + // courses: Additional courses are added here. + // users: Other users information loaded here. + $this->rootnodes = array(); + $this->rootnodes['site'] = $this->add_course($SITE); + $this->rootnodes['myprofile'] = $this->add(get_string('myprofile'), null, self::TYPE_USER, null, 'myprofile'); + $this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), null, self::TYPE_ROOTNODE, null, 'mycourses'); + $this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses'); + $this->rootnodes['users'] = $this->add(get_string('users'), null, self::TYPE_ROOTNODE, null, 'users'); + + // Fetch all of the users courses. + $this->mycourses = get_my_courses($USER->id); + // Check if any courses were returned. + if (count($this->mycourses) > 0) { + // Add all of the users courses to the navigation + foreach ($this->mycourses as &$course) { + $course->coursenode = $this->add_course($course); + } + } else { + // The user had no specific courses! they could be no logged in, guest + // or admin so load all courses instead. + $this->load_all_courses(); + } + + // Next load context specific content into the navigation + switch ($this->page->context->contextlevel) { + case CONTEXT_SYSTEM : + case CONTEXT_COURSECAT : + // Load the front page course navigation + $this->load_course($SITE); break; - case CONTEXT_BLOCK: - case CONTEXT_COURSE: - $depth = $this->load_for_course(); + case CONTEXT_BLOCK : + case CONTEXT_COURSE : + // Load the course associated with the page into the navigation + $course = $this->page->course; + $coursenode = $this->load_course($course); + // Make it active + $coursenode->make_active(); + // Add the essentials such as reports etc... + $this->add_course_essentials($coursenode, $course); + if ($this->format_display_course_content($course->format)) { + // Load the course sections + $sections = $this->load_course_sections($course, $coursenode); + } break; - case CONTEXT_MODULE: - $depth = $this->load_for_activity(); + case CONTEXT_MODULE : + $course = $this->page->course; + $cm = $this->page->cm; + // Load the course associated with the page into the navigation + $coursenode = $this->load_course($course); + $this->add_course_essentials($coursenode, $course); + // Load the course sections into the page + $sections = $this->load_course_sections($course, $coursenode); + if ($course->id !== SITEID) { + // Find the section for the $CM associated with the page and collect + // its section number. + foreach ($sections as $section) { + if ($section->id == $cm->section) { + $cm->sectionnumber = $section->section; + break; + } + } + + // Load all of the section activities for the section the cm belongs to. + $activities = $this->load_section_activities($sections[$cm->sectionnumber]->sectionnode, $cm->sectionnumber, get_fast_modinfo($course)); + // Finally load the cm specific navigaton information + } else { + $activities = array(); + $activities[$cm->id] = $coursenode->get($cm->id, navigation_node::TYPE_ACTIVITY); + } + $this->load_activity($cm, $course, $activities[$cm->id]); + // And make the activity node active. + $activities[$cm->id]->make_active(); break; - case CONTEXT_USER: - // If the PAGE course is not the site then add the content of the - // course - if ($PAGE->course->id !== SITEID) { - $depth = $this->load_for_course(); + case CONTEXT_USER : + $course = $this->page->course; + if ($course->id != SITEID) { + // Load the course associated with the user into the navigation + $coursenode = $this->load_course($course); + $this->add_course_essentials($coursenode, $course); + $sections = $this->load_course_sections($course, $coursenode); } break; } + // Load for the current user + $this->load_for_user(); // Load each extending user into the navigation. foreach ($this->extendforuser as $user) { if ($user->id !== $USER->id) { $this->load_for_user($user); } } - // Load the current user into the the navigation - $this->load_for_user(); - $this->collapse_at_depth($this->depthforward+$depth); - $this->respect_forced_open(); - $this->expandable = array(); - $this->find_expandable($this->expandable); + // Remove any empty root nodes + foreach ($this->rootnodes as $node) { + if (!$node->has_children()) { + $node->remove(); + } + } + + // If the user is not logged in modify the navigation structure as detailed + // in {@link http://docs.moodle.org/en/Development:Navigation_2.0_structure} + if (!isloggedin()) { + $activities = clone($this->rootnodes['site']->children); + $this->rootnodes['site']->remove(); + $children = clone($this->children); + $this->children = new navigation_node_collection(); + foreach ($activities as $child) { + $this->children->add($child); + } + foreach ($children as $child) { + $this->children->add($child); + } + $this->action = new moodle_url('/'); + } + $this->initialised = true; return true; } /** - * This function loads the global navigation structure for a user. + * Checks the course format to see whether it wants the navigation to load + * additional information for the course. + * + * This function utilises a callback that can exist within the course format lib.php file + * The callback should be a function called: + * callback_{formatname}_display_content() + * It doesn't get any arguments and should return true if additional content is + * desired. If the callback doesn't exist we assume additional content is wanted. + * + * @global stdClass $CFG + * @param string $format The course format + * @return bool + */ + protected function format_display_course_content($format) { + global $CFG; + $formatlib = $CFG->dirroot.'/course/format/'.$format.'/lib.php'; + if (file_exists($formatlib)) { + require_once($formatlib); + $displayfunc = 'callback_'.$format.'_display_content'; + if (function_exists($displayfunc) && !$displayfunc()) { + return $displayfunc(); + } + } + return true; + } + + /** + * Loads of the the courses in Moodle into the navigation. + * + * @global moodle_database $DB + * @global stdClass $USER + * @return array An array of navigation_node + */ + protected function load_all_courses() { + global $DB, $USER; + list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); + $sql = "SELECT c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.category,cat.path AS categorypath $ccselect + FROM {course} c + $ccjoin + LEFT JOIN {course_categories} cat ON cat.id=c.category + WHERE c.id != :siteid + ORDER BY c.sortorder ASC"; + $courses = $DB->get_records_sql($sql, array('siteid'=>SITEID)); + $coursenodes = array(); + foreach ($courses as $course) { + context_instance_preload($course); + $coursenodes[$course->id] = $this->add_course($course); + } + return $coursenodes; + } + + /** + * Loads the given course into the navigation * - * This gets called by {@link initialise()} when the context is CONTEXT_USER - * @param object|int|null $user + * @param stdClass $course + * @return navigation_node + */ + protected function load_course(stdClass $course) { + if ($course->id == SITEID) { + $coursenode = $this->rootnodes['site']; + } else if (array_key_exists($course->id, $this->mycourses)) { + if (!isset($this->mycourses[$course->id]->coursenode)) { + $this->mycourses[$course->id]->coursenode = $this->add_course($course); + } + $coursenode = $this->mycourses[$course->id]->coursenode; + } else { + $coursenode = $this->add_course($course); + } + return $coursenode; + } + + /** + * Loads all of the courses section into the navigation. + * + * This function utilisies a callback that can be implemented within the course + * formats lib.php file to customise the navigation that is generated at this + * point for the course. + * + * By default (if not defined) the method {@see load_generic_course_sections} is + * called instead. + * + * @global stdClass $CFG + * @param stdClass $course Database record for the course + * @param navigation_node $coursenode The course node within the navigation + * @return array Array of navigation nodes for the section with key = section id + */ + protected function load_course_sections(stdClass $course, navigation_node $coursenode) { + global $CFG; + $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php'; + $structurefunc = 'callback_'.$course->format.'_load_content'; + if (function_exists($structurefunc)) { + return $structurefunc($this, $course, $coursenode); + } else if (file_exists($structurefile)) { + require_once $structurefile; + if (function_exists($structurefunc)) { + return $structurefunc($this, $course, $coursenode); + } else { + return $this->load_generic_course_sections($course, $coursenode, get_string('topic'), 'topic'); + } + } else { + return $this->load_generic_course_sections($course, $coursenode, get_string('topic'), 'topic'); + } + } + + /** + * Generically loads the course sections into the course's navigation. + * + * @param stdClass $course + * @param navigation_node $coursenode + * @param string $name The string that identifies each section. e.g Topic, or Week + * @param string $activeparam The url used to identify the active section + * @return array An array of course section nodes + */ + public function load_generic_course_sections(stdClass $course, navigation_node $coursenode, $name, $activeparam) { + $modinfo = get_fast_modinfo($course); + $sections = array_slice(get_all_sections($course->id), 0, $course->numsections+1, true); + $viewhiddensections = has_capability('moodle/course:viewhiddensections', $this->page->context); + + $strgeneral = get_string('general'); + foreach ($sections as &$section) { + if ($course->id == SITEID) { + $this->load_section_activities($coursenode, $section->section, $modinfo); + } else { + if ((!$viewhiddensections && !$section->visible) || (!$this->showemptysections && !array_key_exists($section->section, $modinfo->sections))) { + continue; + } + if ($section->section == 0) { + $sectionname = $strgeneral; + } else { + $sectionname = $name.' '.$section->section; + } + $url = new moodle_url('/course/view.php', array('id'=>$course->id, $activeparam=>$section->section)); + $sectionnode = $coursenode->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id); + $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH; + $sectionnode->hidden = (!$section->visible); + if ($sectionnode->isactive) { + $this->load_section_activities($sectionnode, $section->section, $modinfo); + } + $section->sectionnode = $sectionnode; + } + } + return $sections; + } + /** + * Loads all of the activities for a section into the navigation structure. + * + * @param navigation_node $sectionnode + * @param int $sectionnumber + * @param stdClass $modinfo Object returned from {@see get_fast_modinfo()} + * @return array Array of activity nodes + */ + protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, $modinfo) { + if (!array_key_exists($sectionnumber, $modinfo->sections)) { + return true; + } + + $viewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->page->context); + + $activities = array(); + + foreach ($modinfo->sections[$sectionnumber] as $cmid) { + $cm = $modinfo->cms[$cmid]; + if (!$viewhiddenactivities && !$cm->visible) { + continue; + } + if ($cm->icon) { + $icon = new pix_icon($cm->icon, '', $cm->iconcomponent); + } else { + $icon = new pix_icon('icon', '', $cm->modname); + } + $url = new moodle_url('/mod/'.$cm->modname.'/view.php', array('id'=>$cm->id)); + $activitynode = $sectionnode->add($cm->name, $url, navigation_node::TYPE_ACTIVITY, $cm->name, $cm->id, $icon); + $activitynode->title(get_string('modulename', $cm->modname)); + $activitynode->hidden = (!$cm->visible); + if ($this->module_extends_navigation($cm->modname)) { + $child->nodetype = navigation_node::NODETYPE_BRANCH; + } + $activities[$cmid] = $activitynode; + } + + return $activities; + } + /** + * Loads the navigation structure for the given activity into the activities node. + * + * This method utilises a callback within the modules lib.php file to load the + * content specific to activity given. + * + * The callback is a method: {modulename}_extend_navigation() + * Examples: + * * {@see forum_extend_navigation()} + * * {@see workshop_extend_navigation()} + * + * @global stdClass $CFG + * @global moodle_database $DB + * @param stdClass $cm + * @param stdClass $course + * @param navigation_node $activity + * @return bool + */ + protected function load_activity(stdClass $cm, stdClass $course, navigation_node $activity) { + global $CFG, $DB; + + $activity->make_active(); + $file = $CFG->dirroot.'/mod/'.$cm->modname.'/lib.php'; + $function = $cm->modname.'_extend_navigation'; + + if (file_exists($file)) { + require_once($file); + if (function_exists($function)) { + $activtyrecord = $DB->get_record($cm->modname, array('id' => $cm->instance), '*', MUST_EXIST); + $function($activity, $course, $activtyrecord, $cm); + return true; + } + } + $activity->nodetype = navigation_node::NODETYPE_LEAF; + return false; + } + /** + * Loads user specific information into the navigation in the appopriate place. + * + * If no user is provided the current user is assumed. + * + * @global moodle_database $DB + * @global stdClass $CFG + * @global stdClass $USER + * @param stdClass $user + * @return bool */ protected function load_for_user($user=null) { - global $DB, $PAGE, $CFG, $USER; + global $DB, $CFG, $USER; $iscurrentuser = false; if ($user === null) { @@ -1010,55 +1182,48 @@ protected function load_for_user($user=null) { // If the user is not an object then get them from the database $user = $DB->get_record('user', array('id'=>(int)$user), '*', MUST_EXIST); } - $baseargs = array('id'=>$user->id); $usercontext = get_context_instance(CONTEXT_USER, $user->id); // Get the course set against the page, by default this will be the site - $course = $PAGE->course; + $course = $this->page->course; + $baseargs = array('id'=>$user->id); if ($course->id !== SITEID) { - // Load all categories if required. - if (!empty($CFG->navshowallcourses)) { - $this->load_categories(); - } - // Attempt to find the course node within the navigation structure. - $coursetab = $this->find_child($course->id, self::TYPE_COURSE); - if (!$coursetab) { - // Load for the course.... this should never happen but is here to - // ensure if it ever does things don't break. - $this->load_for_course(); - $coursetab = $this->find_child($course->id, self::TYPE_COURSE); - } - // Get the context for the course - $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); + if (array_key_exists($course->id, $this->mycourses)) { + $coursenode = $this->mycourses[$course->id]->coursenode; + } else { + $coursenode = $this->rootnodes['courses']->find($course->id, navigation_node::TYPE_COURSE); + if (!$coursenode) { + $coursenode = $this->load_course($course); + } + } $baseargs['course'] = $course->id; + $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); $issitecourse = false; } else { // Load all categories and get the context for the system - $this->load_categories(); $coursecontext = get_context_instance(CONTEXT_SYSTEM); $issitecourse = true; } // Create a node to add user information under. if ($iscurrentuser) { - // If it's the current user the information will go under the my moodle dashboard node - $usernode = $this->add(get_string('mymoodledashboard'), null, navigation_node::TYPE_CUSTOM, get_string('mymoodledashboard'), 'mymoodle'); - $usernode = $this->get($usernode); + // If it's the current user the information will go under the profile root node + $usernode = $this->rootnodes['myprofile']; } else { if (!$issitecourse) { // Not the current user so add it to the participants node for the current course - $usersnode = $coursetab->find_child('participants', self::TYPE_SETTING); + $usersnode = $coursenode->get('participants', navigation_node::TYPE_CONTAINER); } else { // This is the site so add a users node to the root branch - $usersnode = $this->find_child('users', self::TYPE_CUSTOM); - if (!$usersnode) { - $usersnode = $this->add(get_string('users'), new moodle_url('/user/index.php', array('id'=>SITEID)), self::TYPE_CUSTOM, null, 'users'); - $usersnode = $this->get($usersnode); - } + $usersnode = $this->rootnodes['users']; + $usersnode->action = new moodle_url('/user/index.php', array('id'=>$course->id)); } // Add a branch for the current user $usernode = $usersnode->add(fullname($user, true)); - $usernode = $usersnode->get($usernode); + } + + if ($this->page->context->contextlevel == CONTEXT_USER && $user->id == $this->page->context->instanceid) { + $usernode->force_open(); } // If the user is the current user or has permission to view the details of the requested @@ -1072,7 +1237,6 @@ protected function load_for_user($user=null) { $canviewdiscussions = has_capability('mod/forum:viewdiscussion', $coursecontext); if ($canviewposts || $canviewdiscussions) { $forumtab = $usernode->add(get_string('forumposts', 'forum')); - $forumtab = $usernode->get($forumtab); if ($canviewposts) { $forumtab->add(get_string('posts', 'forum'), new moodle_url('/mod/forum/user.php', $baseargs)); } @@ -1083,12 +1247,15 @@ protected function load_for_user($user=null) { // Add a node to view the users notes if permitted if (!empty($CFG->enablenotes) && has_any_capability(array('moodle/notes:manage', 'moodle/notes:view'), $coursecontext)) { - $usernode->add(get_string('notes', 'notes'), new moodle_url('/notes/index.php',array('user'=>$user->id, 'course'=>$coursecontext->instanceid))); + $url = new moodle_url('/notes/index.php',array('user'=>$user->id)); + if ($coursecontext->instanceid) { + $url->param('course', $coursecontext->instanceid); + } + $usernode->add(get_string('notes', 'notes'), $url); } // Add a reports tab and then add reports the the user has permission to see. $reporttab = $usernode->add(get_string('activityreports')); - $reporttab = $usernode->get($reporttab); $anyreport = has_capability('moodle/user:viewuseractivitiesreport', $usercontext); $viewreports = ($anyreport || ($course->showreports && $iscurrentuser)); $reportargs = array('user'=>$user->id); @@ -1154,204 +1321,9 @@ protected function load_for_user($user=null) { } /** - * Adds the provided user to an array and when the navigation is generated - * it is extended for this user. - * - * @param object $user - */ - public function extend_for_user($user) { - $this->extendforuser[] = $user; - } - - /** - * Called by the initalise methods if the context was system or category - * - * @param bool $lookforid If system context then we dont want ID because - * it could be userid, courseid, or anything else - * @return int The depth to the active(requested) node - */ - protected function load_for_category($lookforid=true) { - global $PAGE, $CFG; - $id = optional_param('id', null, PARAM_INT); - if ($lookforid && $id!==null) { - if (!empty($CFG->navshowallcourses)) { - $this->load_categories(); - } - $this->load_categories($id); - $depth = $this->find_child_depth($id); - } else { - $depth = $this->load_categories(); - } - return $depth; - } - - /** - * Called by the initialise methods if the context was course - * - * @return int The depth to the active(requested) node - */ - protected function load_for_course() { - global $PAGE, $CFG, $USER; - $keys = array(); - if (!empty($CFG->navshowallcourses)) { - $this->load_categories(); - } - $depth = $this->load_course_categories($keys); - $depth += $this->load_course($keys); - if (!$this->format_display_course_content($PAGE->course->format)) { - $child = $this->get_by_path($keys); - if ($child!==false) { - $child->nodetype = self::NODETYPE_LEAF; - } - return $depth; - } - - if (isloggedin() && has_capability('moodle/course:participate', get_context_instance(CONTEXT_COURSE, $PAGE->course->id))) { - $depth += $this->load_course_activities($keys); - $depth += $this->load_course_sections($keys); - } - return $depth; - } - - /** - * Check whether the course format defines a display_course_content function - * that can be used to toggle whether or not to display course content - * - * $default is set to true, which may seem counter productive, however it ensures - * backwards compatibility for course types that havn't yet defined the callback - * - * @param string $format - * @param bool $default - * @return bool - */ - protected function format_display_course_content($format, $default=true) { - global $CFG; - $formatlib = $CFG->dirroot.'/course/format/'.$format.'/lib.php'; - if (file_exists($formatlib)) { - require_once($formatlib); - $displayfunc = 'callback_'.$format.'_display_content'; - if (function_exists($displayfunc) && !$displayfunc()) { - return $displayfunc(); - } - } - return $default; - } - - /** - * Internal method to load course activities into the global navigation structure - * Course activities are activities that are in section 0 - * - * @param array $keys By reference - */ - protected function load_course_activities(&$keys, $course=null) { - global $PAGE, $CFG, $FULLME; - - if ($course === null) { - $course = $PAGE->course; - } - - if (!$this->cache->compare('modinfo'.$course->id, $course->modinfo, false)) { - $this->cache->{'modinfo'.$course->id} = get_fast_modinfo($course); - } - $modinfo = $this->cache->{'modinfo'.$course->id}; - - if (!$this->cache->cached('canviewhiddenactivities')) { - $this->cache->canviewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->context); - } - $viewhiddenactivities = $this->cache->canviewhiddenactivities; - - $labelformatoptions = new object(); - $labelformatoptions->noclean = true; - $labelformatoptions->para = false; - - foreach ($modinfo->cms as $module) { - if ($module->sectionnum!='0' || (!$viewhiddenactivities && !$module->visible)) { - continue; - } - $icon = null; - if ($module->icon) { - $icon = new pix_icon($module->icon, '', $module->iconcomponent); - } else { - $icon = new pix_icon('icon', '', $module->modname); - } - $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); - $this->add_to_path($keys, $module->id, $module->name, $module->name, self::TYPE_ACTIVITY, $url, $icon); - $child = $this->find_child($module->id, self::TYPE_ACTIVITY); - if ($child != false) { - $child->title(get_string('modulename', $module->modname)); - if ($this->module_extends_navigation($module->modname)) { - $child->nodetype = self::NODETYPE_BRANCH; - } - if (!$module->visible) { - $child->hidden = true; - } - } - } - } - /** - * Internal function to load the activities within sections - * - * @param array $keys By reference - */ - protected function load_section_activities(&$keys, $singlesectionid=false, $course=null) { - global $PAGE, $CFG, $FULLME; - - if ($course === null) { - $course = $PAGE->course; - } - - if (!$this->cache->compare('modinfo'.$course->id, $course->modinfo, false)) { - $this->cache->{'modinfo'.$course->id} = get_fast_modinfo($course); - } - $modinfo = $this->cache->{'modinfo'.$course->id}; - - if (!$this->cache->cached('coursesections'.$course->id)) { - $this->cache->{'coursesections'.$course->id} = get_all_sections($course->id); - } - $sections = $this->cache->{'coursesections'.$course->id}; - - if (!$this->cache->cached('canviewhiddenactivities')) { - $this->cache->canviewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->context); - } - $viewhiddenactivities = $this->cache->viewhiddenactivities; - - $labelformatoptions = new object(); - $labelformatoptions->noclean = true; - $labelformatoptions->para = false; - - foreach ($modinfo->cms as $module) { - if ($module->sectionnum=='0' || (!$viewhiddenactivities && !$module->visible) || ($singlesectionid!=false && $module->sectionnum!==$singlesectionid)) { - continue; - } - $icon = null; - if ($module->icon) { - $icon = new pix_icon($module->icon, '', $module->iconcomponent); - } else { - $icon = new pix_icon('icon', '', $module->modname); - } - $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); - - $path = $keys; - if ($course->id !== SITEID) { - $path[] = $sections[$module->sectionnum]->id; - } - $this->add_to_path($path, $module->id, $module->name, $module->name, navigation_node::TYPE_ACTIVITY, $url, $icon); - $child = $this->find_child($module->id, navigation_node::TYPE_ACTIVITY); - if ($child != false) { - $child->title(get_string('modulename', $module->modname)); - if (!$module->visible) { - $child->hidden = true; - } - if ($this->module_extends_navigation($module->modname)) { - $child->nodetype = self::NODETYPE_BRANCH; - } - } - } - } - - /** - * Check if a given module has a method to extend the navigation + * This method simply checks to see if a given module can extend the navigation. * + * @global stdClass $CFG * @param string $modname * @return bool */ @@ -1376,537 +1348,165 @@ protected function module_extends_navigation($modname) { return false; } /** - * Load the global navigation structure for an activity - * - * @return int + * Extends the navigation for the given user. + * + * @param stdClass $user A user from the database */ - protected function load_for_activity() { - global $PAGE, $DB, $CFG; - $keys = array(); - - $sectionnum = false; - if (!empty($PAGE->cm->section)) { - $section = $DB->get_record('course_sections', array('id'=>$PAGE->cm->section)); - if (!empty($section->section)) { - $sectionnum = $section->section; - } - } - - if (!empty($CFG->navshowallcourses)) { - $this->load_categories(); - } - - $depth = $this->load_course_categories($keys); - $depth += $this->load_course($keys); - $depth += $this->load_course_activities($keys); - $depth += $this->load_course_sections($keys); - $depth += $this->load_section_activities($keys,$sectionnum); - $depth += $this->load_activity($keys); - return $depth; + public function extend_for_user($user) { + $this->extendforuser[] = $user; } - /** - * This function loads any navigation items that might exist for an activity - * by looking for and calling a function within the modules lib.php + * Adds the given course to the navigation structure. * - * @param int $instanceid - * @return void + * @param stdClass $course + * @return navigation_node */ - protected function load_activity($keys) { - global $CFG, $PAGE; - - if (!$PAGE->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) { - // This is risky but we have no other choice... we need that module and the module - // itself hasn't set PAGE->cm (usually set by require_login) - // Chances are this is a front page module. - $cm = get_coursemodule_from_id(false, $this->context->instanceid); - $PAGE->set_cm($cm, $PAGE->course); - } - - $node = $this->find_child($PAGE->cm->id, self::TYPE_ACTIVITY); - if ($node) { - $node->make_active(); - $this->context = $PAGE->context; - $file = $CFG->dirroot.'/mod/'.$PAGE->activityname.'/lib.php'; - $function = $PAGE->activityname.'_extend_navigation'; - - if (file_exists($file)) { - require_once($file); - if (function_exists($function)) { - $function($node, $PAGE->course, $PAGE->activityrecord, $PAGE->cm); - } - } + public function add_course(stdClass $course) { + if (array_key_exists($course->id, $this->addedcourses)) { + return $this->addedcourses[$course->id]; } - } - /** - * Recursively adds an array of category objexts to the path provided by $keys - * - * @param array $keys An array of keys representing the path to add to - * @param array $categories An array of [nested] categories to add - * @param int $depth The current depth, this ensures we don't generate more than - * we need to - */ - protected function add_categories(&$keys, $categories, $depth=0) { - if (is_array($categories) && count($categories)>0) { - foreach ($categories as $category) { - $url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey())); - $categorykey = $this->add_to_path($keys, $category->id, $category->name, $category->name, self::TYPE_CATEGORY, $url); - if ($depth < $this->depthforward) { - $this->add_categories(array_merge($keys, array($categorykey)), $category->id, $depth+1); - } - } + $canviewhidden = has_capability('moodle/course:viewhiddencourses', $this->page->context); + if ($course->id !== SITEID && !$canviewhidden && (!$course->visible || !course_parent_visible($course))) { + return false; } - } - /** - * This function adds a category to the nav tree based on the categories path - * - * @param stdClass $category - */ - protected function add_category_by_path($category) { - $url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey())); - $keys = explode('/',trim($category->path,'/ ')); - // Check this category hadsn't already been added - if (!$this->get_by_path($keys)) { - $currentcategory = array_pop($keys); - $categorykey = $this->add_to_path($keys, $category->id, $category->name, $category->name, self::TYPE_CATEGORY, $url); + if ($course->id == SITEID) { + $parent = $this; + $url = new moodle_url('/'); + } else if (array_key_exists($course->id, $this->mycourses)) { + $parent = $this->rootnodes['mycourses']; + $url = new moodle_url('/course/view.php', array('id'=>$course->id)); } else { - $currentcategory = array_pop($keys); - $categorykey = $currentcategory.':'.self::TYPE_CATEGORY; - } - return $categorykey; - } - - /** - * Adds an array of courses to thier correct categories if the categories exist - * - * @param array $courses An array of course objects - * @param int $categoryid An override to add the courses to - * @return bool - */ - public function add_courses($courses, $categoryid=null) { - global $SITE; - if (is_array($courses) && count($courses)>0) { - // Work out if the user can view hidden courses, just incase - if (!$this->cache->cached('canviewhiddencourses')) { - $this->cache->canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $this->context); - } - $canviewhidden = $this->cache->canviewhiddencourses; - $expandcourse = $this->can_display_type(self::TYPE_SECTION); - foreach ($courses as $course) { - // Check if the user can't view hidden courses and if the course is hidden, if so skip and continue - if ($course->id!=$SITE->id && !$canviewhidden && (!$course->visible || !course_parent_visible($course))) { - continue; - } - // Process this course into the nav structure - $url = new moodle_url('/course/view.php', array('id'=>$course->id)); - if ($categoryid===null) { - $category = $this->find_child($course->category, self::TYPE_CATEGORY); - } else if ($categoryid === false) { - $category = $this; - } else { - $category = $this->find_child($categoryid); - } - if ($category!==false) { - // Check that this course hasn't already been added. - // This function only adds a skeleton and we don't want to overwrite - // a fully built course object - if (!$category->get($course->id, self::TYPE_COURSE)) { - $coursekey = $category->add($course->fullname, $url, self::TYPE_COURSE, $course->shortname, $course->id, new pix_icon('i/course', '')); - if (!$course->visible) { - $category->get($course->id)->hidden = true; - } - if ($expandcourse!==true) { - $category->get($course->id)->nodetype = self::NODETYPE_LEAF; - } - } - } - } - } - return true; - } - - /** - * Loads the current course into the navigation structure - * - * Loads the current course held by $PAGE {@link moodle_page()} into the navigation - * structure. - * If the course structure has an appropriate display method then the course structure - * will also be displayed. - * - * @param array $keys The path to add the course to - * @return bool - */ - protected function load_course(&$keys, $course=null) { - global $PAGE, $CFG; - if ($course===null) { - $course = $PAGE->course; - } - if (is_object($course) && $course->id !== SITEID) { - - if (!$this->cache->cached('canviewhiddencourses')) { - $this->cache->canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $this->context); - } - $canviewhidden = $this->cache->canviewhiddencourses; - - if (!$canviewhidden && (!$course->visible || !course_parent_visible($course))) { - return; - } + $parent = $this->rootnodes['courses']; $url = new moodle_url('/course/view.php', array('id'=>$course->id)); - $keys[] = $this->add_to_path($keys, $course->id, $course->fullname, $course->shortname, self::TYPE_COURSE, $url, new pix_icon('i/course', '')); - $currentcourse = $this->find_child($course->id, self::TYPE_COURSE); - if ($currentcourse!==false){ - $currentcourse->make_active(); - if (!$course->visible) { - $currentcourse->hidden = true; - } - - //Participants - if (has_capability('moodle/course:viewparticipants', $this->context)) { - $participantskey = $currentcourse->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_SETTING, get_string('participants'), 'participants'); - $participants = $currentcourse->get($participantskey); - if ($participants) { - - require_once($CFG->dirroot.'/blog/lib.php'); - - $currentgroup = groups_get_course_group($course, true); - if ($course->id == SITEID) { - $filterselect = ''; - } else if ($course->id && !$currentgroup) { - $filterselect = $course->id; - } else { - $filterselect = $currentgroup; - } - $filterselect = clean_param($filterselect, PARAM_INT); - - if ($CFG->bloglevel >= 3) { - $blogsurls = new moodle_url('/blog/index.php', array('courseid' => $filterselect)); - $participants->add(get_string('blogs','blog'), $blogsurls->out()); - } - - if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->context) || has_capability('moodle/notes:view', $this->context))) { - $participants->add(get_string('notes','notes'), new moodle_url('/notes/index.php', array('filtertype'=>'course', 'filterselect'=>$filterselect))); - } - } - } - - // View course reports - if (has_capability('moodle/site:viewreports', $this->context)) { // basic capability for listing of reports - $reportkey = $currentcourse->add(get_string('reports'), new moodle_url('/course/report.php', array('id'=>$course->id)), self::TYPE_SETTING, null, null, new pix_icon('i/stats', '')); - $reportnav = $currentcourse->get($reportkey); - if ($reportnav) { - $coursereports = get_plugin_list('coursereport'); - foreach ($coursereports as $report=>$dir) { - $libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php'; - if (file_exists($libfile)) { - require_once($libfile); - $reportfunction = $report.'_report_extend_navigation'; - if (function_exists($report.'_report_extend_navigation')) { - $reportfunction($reportnav, $course, $this->context); - } - } - } - } - } - } - - if (!$this->can_display_type(self::TYPE_SECTION)) { - if ($currentcourse!==false) { - $currentcourse->nodetype = self::NODETYPE_LEAF; - } - return true; - } } + $coursenode = $parent->add($course->fullname, $url, self::TYPE_COURSE, $course->shortname, $course->id); + $coursenode->nodetype = self::NODETYPE_BRANCH; + $coursenode->hidden = (!$course->visible); + $this->addedcourses[$course->id] = &$coursenode; + return $coursenode; } /** - * Loads the sections for a course + * Adds essential course nodes to the navigation for the given course. * - * @param array $keys By reference - * @param stdClass $course The course that we are loading sections for - */ - protected function load_course_sections(&$keys, $course=null) { - global $PAGE, $CFG; - if ($course === null) { - $course = $PAGE->course; - } - $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php'; - $structurefunc = 'callback_'.$course->format.'_load_content'; - if (function_exists($structurefunc)) { - $structurefunc($this, $keys, $course); - } else if (file_exists($structurefile)) { - require_once $structurefile; - if (function_exists($structurefunc)) { - $structurefunc($this, $keys, $course); - } else { - $this->add_course_section_generic($keys, $course); - } - } else { - $this->add_course_section_generic($keys, $course); - } - } - /** - * This function loads the sections for a course if no given course format - * methods have been defined to do so. Thus generic + * This method adds nodes such as reports, blogs and participants * - * @param array $keys By reference - * @param stdClass $course The course object to load for - * @param string $name String to use to describe the current section to the user - * @param string $activeparam Request variable to look for to determine the current section + * @global stdClass $CFG + * @param navigation_node $coursenode + * @param stdClass $course * @return bool */ - public function add_course_section_generic(&$keys, $course=null, $name=null, $activeparam = null) { - global $PAGE; - - if ($course === null) { - $course = $PAGE->course; - } - - $coursesecstr = 'coursesections'.$course->id; - if (!$this->cache->cached($coursesecstr)) { - $sections = get_all_sections($course->id); - $this->cache->$coursesecstr = $sections; - } else { - $sections = $this->cache->$coursesecstr; - } - - if (!$this->cache->compare('modinfo'.$course->id, $course->modinfo, false)) { - $this->cache->{'modinfo'.$course->id} = get_fast_modinfo($course); - } - $modinfo = $this->cache->{'modinfo'.$course->id}; - - $depthforward = 0; - if (!is_array($modinfo->sections)) { - return $keys; - } - - if ($name === null) { - $name = get_string('topic'); - } - - if ($activeparam === null) { - $activeparam = 'topic'; - } - - $coursenode = $this->find_child($course->id, navigation_node::TYPE_COURSE); - if ($coursenode!==false) { - $coursenode->action->param($activeparam,'0'); - } - - if (!$this->cache->cached('canviewhiddenactivities')) { - $this->cache->canviewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->context); - } - $viewhiddenactivities = $this->cache->canviewhiddenactivities; - - if (!$this->cache->cached('canviewhiddensections')) { - $this->cache->canviewhiddensections = has_capability('moodle/course:viewhiddensections', $this->context); - } - $viewhiddensections = $this->cache->canviewhiddensections; - - // MDL-20242 + MDL-21564 - $sections = array_slice($sections, 0, $course->numsections+1, true); - - foreach ($sections as $section) { - if ((!$viewhiddensections && !$section->visible) || (!$this->showemptybranches && !array_key_exists($section->section, $modinfo->sections))) { - continue; - } - if ($section->section!=0) { - $sectionkeys = $keys; - $url = new moodle_url('/course/view.php', array('id'=>$course->id, $activeparam=>$section->section)); - $this->add_to_path($sectionkeys, $section->id, $name.' '.$section->section, null, navigation_node::TYPE_SECTION, $url); - $sectionchild = $this->find_child($section->id, navigation_node::TYPE_SECTION); - if ($sectionchild !== false) { - $sectionchild->nodetype = self::NODETYPE_BRANCH; - if ($sectionchild->isactive) { - $this->load_section_activities($sectionkeys, $section->section); - } - if (!$section->visible) { - $sectionchild->hidden = true; - } - } - } - } - return true; - } - - /** - * Check if we are permitted to display a given type - * - * @return bool True if we are, False otherwise - */ - protected function can_display_type($type) { - if (!is_null($this->expansionlimit) && $this->expansionlimit < $type) { - return false; - } - return true; - } + public function add_course_essentials(navigation_node $coursenode, stdClass $course) { + global $CFG; - /** - * Loads the categories for the current course into the navigation structure - * - * @param array $keys Forced reference to and array to use for the keys - * @return int The number of categories - */ - protected function load_course_categories(&$keys) { - global $PAGE; - $categories = $PAGE->categories; - if (is_array($categories) && count($categories)>0) { - $categories = array_reverse($categories); - foreach ($categories as $category) { - $key = $category->id.':'.self::TYPE_CATEGORY; - if (!$this->get_by_path(array_merge($keys, array($key)))) { - $url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey())); - $keys[] = $this->add_to_path($keys, $category->id, $category->name, $category->name, self::TYPE_CATEGORY, $url); - } else { - $keys[] = $key; - } - } + if ($course->id === SITEID) { + return $this->add_front_page_course_essentials($coursenode, $course); } - return count($categories); - } - /** - * This is called by load_for_category to load categories into the navigation structure - * - * @param int $categoryid The specific category to load - * @return int The depth of categories that were loaded - */ - protected function load_categories($categoryid=0) { - global $CFG, $DB, $USER; - - if ($categoryid === 0 && $this->allcategoriesloaded) { - return 0; + if ($coursenode == false || $coursenode->get('participants', navigation_node::TYPE_CONTAINER)) { + return true; } - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - - // If the user is logged in (but not as a guest), doesnt have the site config capability, - // and my courses havn't been disabled then we will show the user's courses in the - // global navigation, otherwise we will show up to FRONTPAGECOURSELIMIT available courses - if (isloggedin() && !is_siteadmin() && !isguestuser() && empty($CFG->disablemycourses)) { - if (!$this->cache->cached('mycourses')) { - $this->cache->mycourses = get_my_courses($USER->id); - } - $courses = $this->cache->mycourses; - } else { - // Check whether we have already cached the available courses - if (!$this->cache->cached('availablecourses')) { - // Non-cached - get accessinfo - if (isset($USER->access)) { - $accessinfo = $USER->access; - } else { - $accessinfo = get_user_access_sitewide($USER->id); + //Participants + if (has_capability('moodle/course:viewparticipants', $this->page->context)) { + require_once($CFG->dirroot.'/blog/lib.php'); + $participants = $coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CONTAINER, get_string('participants'), 'participants'); + $currentgroup = groups_get_course_group($course, true); + if ($course->id == SITEID) { + $filterselect = ''; + } else if ($course->id && !$currentgroup) { + $filterselect = $course->id; + } else { + $filterselect = $currentgroup; + } + $filterselect = clean_param($filterselect, PARAM_INT); + if ($CFG->bloglevel >= 3) { + $blogsurls = new moodle_url('/blog/index.php', array('courseid' => $filterselect)); + $participants->add(get_string('blogs','blog'), $blogsurls->out()); + } + if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->page->context) || has_capability('moodle/notes:view', $this->page->context))) { + $participants->add(get_string('notes','notes'), new moodle_url('/notes/index.php', array('filtertype'=>'course', 'filterselect'=>$filterselect))); + } + } else if (count($this->extendforuser) > 0) { + $participants = $coursenode->add(get_string('participants'), null, self::TYPE_CONTAINER, get_string('participants'), 'participants'); + } + + // View course reports + if (has_capability('moodle/site:viewreports', $this->page->context)) { // basic capability for listing of reports + $reportnav = $coursenode->add(get_string('reports'), new moodle_url('/course/report.php', array('id'=>$course->id)), self::TYPE_CONTAINER, null, null, new pix_icon('i/stats', '')); + $coursereports = get_plugin_list('coursereport'); + foreach ($coursereports as $report=>$dir) { + $libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php'; + if (file_exists($libfile)) { + require_once($libfile); + $reportfunction = $report.'_report_extend_navigation'; + if (function_exists($report.'_report_extend_navigation')) { + $reportfunction($reportnav, $course, $this->page->context); + } } - // Get the available courses using get_user_courses_bycap - $this->cache->availablecourses = get_user_courses_bycap($USER->id, 'moodle/course:participate', - $accessinfo, true, 'c.sortorder ASC', - array('fullname','visible', 'category'), - FRONTPAGECOURSELIMIT); } - // Cache the available courses for a refresh - $courses = $this->cache->availablecourses; } + return true; + } - // Iterate through all courses, and explode thier course category paths so that - // we can retrieve all of the individual category id's that are required - // to display the list of courses in the tree - $categoryids = array(); - foreach ($courses as $course) { - // If a category id has been specified and the current course is not within - // that category or one of its children then skip this course - if ($categoryid!==0 && !preg_match('#/('.$categoryid.')(/|$)#', $course->categorypath)) { - continue; - } - $categorypathids = explode('/',trim($course->categorypath,' /')); - // If no category has been specified limit the depth we display immediatly to - // that of the nav var depthforwards - if ($categoryid===0 && count($categorypathids)>($this->depthforward+1) && empty($CFG->navshowallcourses)) { - $categorypathids = array_slice($categorypathids, 0, ($this->depthforward+1)); - } - $categoryids = array_merge($categoryids, $categorypathids); - } - // Remove duplicate categories (and there will be a few) - $categoryids = array_unique($categoryids); + public function add_front_page_course_essentials(navigation_node $coursenode, stdClass $course) { + global $CFG; - // Check whether we have some category ids to display and make sure that either - // no category has been specified ($categoryid===0) or that the category that - // has been specified is in the list. - if (count($categoryids)>0 && ($categoryid===0 || in_array($categoryid, $categoryids))) { - $catcachestr = 'categories'.join($categoryids); - if (!$this->cache->cached($catcachestr)) { - $this->cache->{$catcachestr} = $DB->get_records_select('course_categories', 'id IN ('.join(',', $categoryids).')', array(), 'path ASC, sortorder ASC'); - } - $categories = $this->cache->{$catcachestr}; - // Retrieve the nessecary categories and then proceed to add them to the tree - foreach ($categories as $category) { - $this->add_category_by_path($category); - } - // Add the courses that were retrieved earlier to the - $this->add_courses($courses); - } else if ($categoryid === 0) { - $keys = array(); - $categories = $DB->get_records('course_categories', array('parent' => $categoryid), 'sortorder ASC'); - $this->add_categories($keys, $categories); - $this->add_courses($courses, $categoryid); + if ($coursenode == false || $coursenode->get('participants', navigation_node::TYPE_CUSTOM)) { + return true; } - $this->allcategoriesloaded = true; - return 0; - } - /** - * This function marks the cache as volatile so it is cleared during shutdown - */ - public function clear_cache() { - $this->cache->volatile(); - } + //Participants + if (has_capability('moodle/course:viewparticipants', $this->page->context)) { + require_once($CFG->dirroot.'/blog/lib.php'); + $coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CUSTOM, get_string('participants'), 'participants'); + } + + $currentgroup = groups_get_course_group($course, true); + if ($course->id == SITEID) { + $filterselect = ''; + } else if ($course->id && !$currentgroup) { + $filterselect = $course->id; + } else { + $filterselect = $currentgroup; + } + $filterselect = clean_param($filterselect, PARAM_INT); + if ($CFG->bloglevel >= 3) { + $blogsurls = new moodle_url('/blog/index.php', array('courseid' => $filterselect)); + $coursenode->add(get_string('blogs','blog'), $blogsurls->out()); + } + if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->page->context) || has_capability('moodle/notes:view', $this->page->context))) { + $coursenode->add(get_string('notes','notes'), new moodle_url('/notes/index.php', array('filtertype'=>'course', 'filterselect'=>$filterselect))); + } + if (!empty($CFG->usetags)) { + $coursenode->add(get_string('tags', 'tag'), new moodle_url('/tag/search.php')); + } - /** - * Finds all expandable nodes whilst ensuring that expansion limit is respected - * - * @param array $expandable A reference to an array that will be populated as - * we go. - */ - public function find_expandable(&$expandable) { - parent::find_expandable($expandable, $this->expansionlimit); - } - /** - * Loads categories that contain no courses into the structure. - * - * These categories would normally be skipped, as such this function is purely - * for the benefit of code external to navigationlib - */ - public function load_empty_categories() { - $categories = array(); - $categorynames = array(); - $categoryparents = array(); - make_categories_list($categorynames, $categoryparents, '', 0, $category = NULL); - foreach ($categorynames as $id=>$name) { - if (!$this->find_child($id, self::TYPE_CATEGORY)) { - $category = new stdClass; - $category->id = $id; - if (array_key_exists($id, $categoryparents)) { - $category->path = '/'.join('/',array_merge($categoryparents[$id],array($id))); - $name = explode('/', $name); - $category->name = join('/', array_splice($name, count($categoryparents[$id]))); - } else { - $category->path = '/'.$id; - $category->name = $name; + // View course reports + if (has_capability('moodle/site:viewreports', $this->page->context)) { // basic capability for listing of reports + $reportnav = $coursenode->add(get_string('reports'), new moodle_url('/course/report.php', array('id'=>$course->id)), self::TYPE_CONTAINER, null, null, new pix_icon('i/stats', '')); + $coursereports = get_plugin_list('coursereport'); + foreach ($coursereports as $report=>$dir) { + $libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php'; + if (file_exists($libfile)) { + require_once($libfile); + $reportfunction = $report.'_report_extend_navigation'; + if (function_exists($report.'_report_extend_navigation')) { + $reportfunction($reportnav, $course, $this->page->context); + } } - $this->add_category_by_path($category); } } + return true; } - public function collapse_course_categories() { - $categories = $this->get_children_by_type(self::TYPE_CATEGORY); - while (count($categories) > 0) { - foreach ($categories as $category) { - $this->children = array_merge($this->children, $category->children); - $this->remove_child($category->key, self::TYPE_CATEGORY); - } - $categories = $this->get_children_by_type(self::TYPE_CATEGORY); - } + /** + * Clears the navigation cache + */ + public function clear_cache() { + $this->cache->clear(); } } @@ -1927,20 +1527,33 @@ public function collapse_course_categories() { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class global_navigation_for_ajax extends global_navigation { + + /** @var array */ + protected $expandable = array(); + /** - * Initialise the limited navigation object, calling it to auto generate - * - * This function can be used to initialise the global navigation object more - * flexibly by providing a couple of overrides. - * This is used when the global navigation is being generated without other fully - * initialised Moodle objects + * Constructs the navigation for use in AJAX request + * @global stdClass $SITE + */ + public function __construct() { + global $SITE; + $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); + $this->children = new navigation_node_collection(); + $this->rootnodes = array(); + //$this->rootnodes['site'] = $this->add_course($SITE); + $this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses'); + } + /** + * Initialise the navigation given the type and id for the branch to expand. * - * @param int $branchtype What to load for e.g. TYPE_SYSTEM - * @param int $id The instance id for what ever is being loaded - * @return array An array of nodes that are expandable by AJAX + * @global moodle_database $DB + * @global moodle_page $PAGE + * @param int $branchtype One of navigation_node::TYPE_* + * @param int $id + * @return array The expandable nodes */ public function initialise($branchtype, $id) { - global $DB; + global $DB, $PAGE; if ($this->initialised || during_initial_install()) { return $this->expandable; @@ -1948,261 +1561,53 @@ public function initialise($branchtype, $id) { // Branchtype will be one of navigation_node::TYPE_* switch ($branchtype) { - case self::TYPE_CATEGORY : - require_login(); - $depth = $this->load_category($id); - break; case self::TYPE_COURSE : $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST); require_course_login($course); - $depth = $this->load_course($id); + $this->page = $PAGE; + $coursenode = $this->add_course($course); + $this->add_course_essentials($coursenode, $course); + if ($this->format_display_course_content($course->format)) { + $this->load_course_sections($course, $coursenode); + } break; case self::TYPE_SECTION : - $sql = 'SELECT c.* + $sql = 'SELECT c.*, cs.section AS sectionnumber FROM {course} c LEFT JOIN {course_sections} cs ON cs.course = c.id WHERE cs.id = ?'; $course = $DB->get_record_sql($sql, array($id), MUST_EXIST); require_course_login($course); - $depth = $this->load_section($id); + $this->page = $PAGE; + $coursenode = $this->add_course($course); + $this->add_course_essentials($coursenode, $course); + $sections = $this->load_course_sections($course, $coursenode); + $this->load_section_activities($sections[$course->sectionnumber]->sectionnode, $course->sectionnumber, get_fast_modinfo($course)); break; case self::TYPE_ACTIVITY : $cm = get_coursemodule_from_id(false, $id, 0, false, MUST_EXIST); $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); require_course_login($course, true, $cm); - $depth = $this->load_activity($id); + $this->page = $PAGE; + $coursenode = $this->load_course($course); + $sections = $this->load_course_sections($course, $coursenode); + foreach ($sections as $section) { + if ($section->id == $cm->section) { + $cm->sectionnumber = $section->section; + break; + } + } + $activities = $this->load_section_activities($sections[$cm->sectionnumber]->sectionnode, $cm->sectionnumber, get_fast_modinfo($course)); + $modulenode = $this->load_activity($cm, $course, $activities[$cm->id]); break; default: + throw new Exception('Unknown type'); return $this->expandable; - break; } - $this->collapse_at_depth($this->depthforward+$depth); - $this->respect_forced_open(); - $this->expandable = array(); $this->find_expandable($this->expandable); - $this->initialised = true; return $this->expandable; } - - /** - * Loads the content (sub categories and courses) for a given a category - * - * @param int $instanceid - */ - protected function load_category($instanceid) { - if (!$this->cache->cached('coursecatcontext'.$instanceid)) { - $this->cache->{'coursecatcontext'.$instanceid} = get_context_instance(CONTEXT_COURSECAT, $instanceid); - } - $this->context = $this->cache->{'coursecatcontext'.$instanceid}; - $this->load_categories($instanceid); - } - - /** - * Use the instance id to load a course - * - * {@link global_navigation::load_course()} - * @param int $instanceid - */ - protected function load_course($instanceid) { - global $DB, $PAGE; - - if (!$this->cache->cached('course'.$instanceid)) { - if ($PAGE->course->id == $instanceid) { - $this->cache->{'course'.$instanceid} = $PAGE->course; - } else { - $this->cache->{'course'.$instanceid} = $DB->get_record('course', array('id'=>$instanceid), '*', MUST_EXIST); - } - } - $course = $this->cache->{'course'.$instanceid}; - - if (!$this->format_display_course_content($course->format)) { - return true; - } - - if (!$this->cache->cached('coursecontext'.$course->id)) { - $this->cache->{'coursecontext'.$course->id} = get_context_instance(CONTEXT_COURSE, $course->id); - } - $this->context = $this->cache->{'coursecontext'.$course->id}; - - $keys = array(); - parent::load_course($keys, $course); - - if (isloggedin() && has_capability('moodle/course:participate', $this->context)) { - if (!$this->cache->cached('course'.$course->id.'section0')) { - $this->cache->{'course'.$course->id.'section0'} = get_course_section('0', $course->id); - } - $section = $this->cache->{'course'.$course->id.'section0'}; - $this->load_section_activities($course, $section); - if ($this->depthforward>0) { - $this->load_course_sections($keys, $course); - } - } - } - /** - * Use the instance id to load a specific course section - * - * @param int $instanceid - */ - protected function load_section($instanceid=0) { - global $DB, $PAGE, $CFG; - - $section = $DB->get_record('course_sections', array('id'=>$instanceid), '*', MUST_EXIST); - - if (!$this->cache->cached('course'.$section->course)) { - if ($PAGE->course->id == $section->course) { - $this->cache->{'course'.$section->course} = $PAGE->course; - } else { - $this->cache->{'course'.$section->course} = $DB->get_record('course', array('id'=>$section->course), '*', MUST_EXIST); - } - } - $course = $this->cache->{'course'.$section->course}; - - if (!$this->cache->cached('coursecontext'.$course->id)) { - $this->cache->{'coursecontext'.$course->id} = get_context_instance(CONTEXT_COURSE, $course->id); - } - $this->context = $this->cache->{'coursecontext'.$course->id}; - - // Call the function to generate course section - $keys = array(); - $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/navigation_format.php'; - $structurefunc = 'callback_'.$course->format.'_load_limited_section'; - if (function_exists($structurefunc)) { - $sectionnode = $structurefunc($this, $keys, $course, $section); - } else if (file_exists($structurefile)) { - include $structurefile; - if (function_exists($structurefunc)) { - $sectionnode = $structurefunc($this, $keys, $course, $section); - } else { - $sectionnode = $this->limited_load_section_generic($keys, $course, $section); - } - } else { - $sectionnode = $this->limited_load_section_generic($keys, $course, $section); - } - if ($this->depthforward>0) { - $this->load_section_activities($course, $section); - } - } - /** - * This function is called if there is no specific course format function set - * up to load sections into the global navigation. - * - * Note that if you are writing a course format you can call this function from your - * callback function if you don't want to load anything special but just specify the - * GET argument that identifies the current section as well as the string that - * can be used to describve the section. e.g. weeks or topic - * - * @param array $keys - * @param stdClass $course - * @param stdClass $section - * @param string $name - * @param string $activeparam - * @return navigation_node|bool - */ - public function limited_load_section_generic($keys, $course, $section, $name=null, $activeparam = 'topic') { - global $PAGE, $CFG; - - if ($name === null) { - $name = get_string('topic'); - } - - if (!$this->cache->cached('canviewhiddensections')) { - $this->cache->canviewhiddensections = has_capability('moodle/course:viewhiddensections', $this->context); - } - $viewhiddensections = $this->cache->canviewhiddensections; - - if (!$viewhiddensections && !$section->visible) { - return false; - } - if ($section->section!=0) { - $url = new moodle_url('/course/view.php', array('id'=>$course->id, $activeparam=>$section->id)); - $keys[] = $this->add_to_path($keys, $section->id, $name.' '.$section->section, null, navigation_node::TYPE_SECTION, $url); - $sectionchild = $this->find_child($section->id, navigation_node::TYPE_SECTION); - if ($sectionchild !== false) { - $sectionchild->nodetype = self::NODETYPE_BRANCH; - $sectionchild->make_active(); - if (!$section->visible) { - $sectionchild->hidden = true; - } - return $sectionchild; - } - } - return false; - } - - /** - * This function is used to load a course sections activities - * - * @param stdClass $course - * @param stdClass $section - * @return void - */ - protected function load_section_activities($course, $section) { - global $CFG; - if (!is_object($section)) { - return; - } - if ($section->section=='0') { - $keys = array($section->course); - } else { - $keys = array($section->id); - } - - $modinfo = get_fast_modinfo($course); - - if (!$this->cache->cached('canviewhiddenactivities')) { - $this->cache->canviewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->context); - } - $viewhiddenactivities = $this->cache->canviewhiddenactivities; - - foreach ($modinfo->cms as $module) { - if ((!$viewhiddenactivities && !$module->visible) || $module->sectionnum != $section->section) { - continue; - } - $icon = null; - if ($module->icon) { - $icon = new pix_icon($module->icon, '', $module->iconcomponent); - } else { - $icon = new pix_icon('icon', '', $module->modname); - } - $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); - $this->add_to_path($keys, $module->id, $module->name, $module->name, navigation_node::TYPE_ACTIVITY, $url, $icon); - $child = $this->find_child($module->id, navigation_node::TYPE_ACTIVITY); - if ($child != false) { - $child->title(get_string('modulename', $module->modname)); - if (!$module->visible) { - $child->hidden = true; - } - if ($this->module_extends_navigation($module->modname)) { - $child->nodetype = self::NODETYPE_BRANCH; - } - } - } - } - - /** - * This function loads any navigation items that might exist for an activity - * by looking for and calling a function within the modules lib.php - * - * @param int $instanceid - * @return void - */ - protected function load_activity($instanceid) { - global $CFG, $PAGE; - - $this->context = get_context_instance(CONTEXT_COURSE, $PAGE->course->id); - $key = $this->add($PAGE->activityname, null, self::TYPE_ACTIVITY, null, $instanceid); - - $file = $CFG->dirroot.'/mod/'.$PAGE->activityname.'/lib.php'; - $function = $PAGE->activityname.'_extend_navigation'; - - if (file_exists($file)) { - require_once($file); - if (function_exists($function)) { - $function($this->get($key), $PAGE->course, $PAGE->activityrecord, $PAGE->cm); - } - } - } } /** @@ -2223,7 +1628,7 @@ class navbar extends navigation_node { protected $keys = array(); /** @var null|string */ protected $content = null; - /** @var page object */ + /** @var moodle_page object */ protected $page; /** @var bool */ protected $ignoreactive = false; @@ -2231,11 +1636,16 @@ class navbar extends navigation_node { protected $duringinstall = false; /** @var bool */ protected $hasitems = false; - + /** @var array */ + protected $items; + /** @var array */ + public $children = array(); /** * The almighty constructor + * + * @param moodle_page $page */ - public function __construct(&$page) { + public function __construct(moodle_page $page) { global $CFG; if (during_initial_install()) { $this->duringinstall = true; @@ -2260,153 +1670,106 @@ public function has_items() { } else if ($this->hasitems !== false) { return true; } - $this->page->navigation->initialise(); + $this->page->navigation->initialise($this->page); $activenodefound = ($this->page->navigation->contains_active_node() || - $this->page->settingsnav->contains_active_node() || - $this->page->navigation->reiterate_active_nodes(URL_MATCH_BASE) || - $this->page->settingsnav->reiterate_active_nodes(URL_MATCH_BASE)); + $this->page->settingsnav->contains_active_node()); - $outcome = (count($this->page->navbar->children)>0 || (!$this->ignoreactive && $activenodefound)); + $outcome = (count($this->children)>0 || (!$this->ignoreactive && $activenodefound)); $this->hasitems = $outcome; return $outcome; } + /** + * Turn on/off ignore active + * + * @param bool $setting + */ public function ignore_active($setting=true) { $this->ignoreactive = ($setting); } - + public function get($key, $type = null) { + foreach ($this->children as &$child) { + if ($child->key === $key && ($type == null || $type == $child->type)) { + return $child; + } + } + return false; + } /** - * Generate the XHTML content for the navbar and return it - * - * We are lucky in that we can rely on PAGE->navigation for the structure - * we simply need to look for the `active` path through the tree. We make this - * easier by calling {@link strip_down_to_final_active()}. - * - * This function should in the future be refactored to work with a copy of the - * PAGE->navigation object and strip it down to just this the active nodes using - * a function that could be written again navigation_node called something like - * strip_inactive_nodes(). I wrote this originally but currently the navigation - * object is managed via references. - * - * @return string XHTML navbar content + * Returns an array of navigation_node's that make up the navbar. + * + * @return array */ - public function content() { - if ($this->duringinstall) { - return ''; - } - + public function get_items() { + $items = array(); // Make sure that navigation is initialised if (!$this->has_items()) { - return ''; + return $items; } - - if ($this->content !== null) { - return $this->content; + if ($this->items !== null) { + return $this->items; } - // For screen readers - $output = get_accesshide(get_string('youarehere','access'), 'h2').html_writer::start_tag('ul'); + if (count($this->children) > 0) { + // Add the custom children + $items = array_reverse($this->children); + } - $navigationactive = $this->page->navigation->contains_active_node(); - $settingsactive = $this->page->settingsnav->contains_active_node(); + $navigationactivenode = $this->page->navigation->find_active_node(); + $settingsactivenode = $this->page->settingsnav->find_active_node(); // Check if navigation contains the active node if (!$this->ignoreactive) { - if ($navigationactive && $settingsactive) { + + if ($navigationactivenode && $settingsactivenode) { // Parse a combined navigation tree - $output .= $this->parse_branch_to_html($this->page->navigation->children, true, true); - $activenode = $this->page->settingsnav->find_active_node(); - if ($activenode) { - $output .= $this->parse_branch_to_html(array($activenode), false); + while ($settingsactivenode && $settingsactivenode->parent !== null) { + if (!$settingsactivenode->mainnavonly) { + $items[] = $settingsactivenode; + } + $settingsactivenode = $settingsactivenode->parent; + } + // Removes the first node from the settings (root node) from the list + array_pop($items); + while ($navigationactivenode && $navigationactivenode->parent !== null) { + if (!$navigationactivenode->mainnavonly) { + $items[] = $navigationactivenode; + } + $navigationactivenode = $navigationactivenode->parent; } - } else if ($navigationactive) { + } else if ($navigationactivenode) { // Parse the navigation tree to get the active node - $output .= $this->parse_branch_to_html($this->page->navigation->children, true); - } else if ($settingsactive) { + while ($navigationactivenode && $navigationactivenode->parent !== null) { + if (!$navigationactivenode->mainnavonly) { + $items[] = $navigationactivenode; + } + $navigationactivenode = $navigationactivenode->parent; + } + } else if ($settingsactivenode) { // Parse the settings navigation to get the active node - $output .= $this->parse_branch_to_html($this->page->settingsnav->children, true); + while ($settingsactivenode && $settingsactivenode->parent !== null) { + if (!$settingsactivenode->mainnavonly) { + $items[] = $settingsactivenode; + } + $settingsactivenode = $settingsactivenode->parent; + } } - } else { - $output .= $this->parse_branch_to_html($this, true); } - if (!empty($this->children)) { - // Add the custom children - $output .= $this->parse_branch_to_html($this->children, false, false); - } + $items[] = new navigation_node(array( + 'text'=>$this->page->navigation->text, + 'shorttext'=>$this->page->navigation->shorttext, + 'key'=>$this->page->navigation->key, + 'action'=>$this->page->navigation->action + )); - $output .= html_writer::end_tag('ul'); - $this->content = $output; - return $output; + $this->items = array_reverse($items); + return $this->items; } - /** - * This function converts an array of nodes into XHTML for the navbar - * - * @param array $navarray - * @param bool $firstnode - * @return string HTML - */ - protected function parse_branch_to_html($navarray, $firstnode=true, $morecoming=false) { - global $CFG; - $separator = get_separator(); - $output = ''; - if ($firstnode===true) { - // If this is the first node add the class first and display the - // navbar properties (normally sitename) - $output .= html_writer::tag('li', parent::content(true), array('class'=>'first')); - } - $count = 0; - if (!is_array($navarray)) { - return $output; - } - // Iterate the navarray and display each node - while (count($navarray)>0) { - // Sanity check make sure we don't display WAY too much information - // on the navbar. If we get to 20 items just stop! - $count++; - if ($count>20) { - // Maximum number of nodes in the navigation branch - return $output; - } - $child = false; - // Iterate the nodes in navarray and find the active node - foreach ($navarray as $tempchild) { - if ($tempchild->isactive || $tempchild->contains_active_node()) { - $child = $tempchild; - // We've got the first child we can break out of this foreach - break; - } - } - // Check if we found the child - if ($child===false || $child->mainnavonly) { - // Set navarray to an empty array so that we complete the while - $navarray = array(); - } else { - // We found an/the active node, set navarray to it's children so that - // we come back through this while with the children of the active node - $navarray = $child->children; - // If there are not more arrays being processed after this AND this is the last element - // then we want to set the action to null so that it is not used - if (empty($this->children) && !$morecoming && (!$child->contains_active_node() || ($child->find_active_node()==false || $child->find_active_node()->mainnavonly))) { - $oldaction = $child->action; - $child->action = null; - } - if ($child->type !== navigation_node::TYPE_CATEGORY || !isset($CFG->navshowcategories) || !empty($CFG->navshowcategories)) { - // Now display the node - $output .= html_writer::tag('li', $separator.' '.$child->content(true)); - } - if (isset($oldaction)) { - $child->action = $oldaction; - } - } - } - // XHTML - return $output; - } /** - * Add a new node to the navbar, overrides parent::add + * Add a new navigation_node to the navbar, overrides parent::add * * This function overrides {@link navigation_node::add()} so that we can change * the way nodes get added to allow us to simply call add and have the node added to the @@ -2418,23 +1781,41 @@ protected function parse_branch_to_html($navarray, $firstnode=true, $morecoming= * @param string|int $key * @param string $shorttext * @param string $icon - * @return string|int Identifier for this particular node + * @return navigation_node */ public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) { - // Check if there are any keys in the objects keys array - if (count($this->keys)===0) { - // If there are no keys then we can use the add method - $key = parent::add($text, $action, $type, $shorttext, $key, $icon); - } else { - $key = $this->add_to_path($this->keys, $key, $text, $shorttext, $type, $action, $icon); + if ($this->content !== null) { + debugging('Nav bar items must be printed before $OUTPUT->header() has been called', DEBUG_DEVELOPER); + } + + // Properties array used when creating the new navigation node + $itemarray = array( + 'text' => $text, + 'type' => $type + ); + // Set the action if one was provided + if ($action!==null) { + $itemarray['action'] = $action; + } + // Set the shorttext if one was provided + if ($shorttext!==null) { + $itemarray['shorttext'] = $shorttext; + } + // Set the icon if one was provided + if ($icon!==null) { + $itemarray['icon'] = $icon; } - $this->keys[] = $key; - $child = $this->get_by_path($this->keys); - if ($child!==false) { - // This ensure that the child will be shown - $child->make_active(); + // Default the key to the number of children if not provided + if ($key === null) { + $key = count($this->children); } - return $key; + // Set the key + $itemarray['key'] = $key; + // Set the parent to this node + $itemarray['parent'] = $this; + // Add the child using the navigation_node_collections add method + $this->children[] = new navigation_node($itemarray); + return $this; } } @@ -2452,14 +1833,16 @@ public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=nul class settings_navigation extends navigation_node { /** @var stdClass */ protected $context; - /** @var cache */ + /** @var navigation_cache */ protected $cache; - /** @var page object */ + /** @var moodle_page */ protected $page; - /** @var adminsection string */ + /** @var string */ protected $adminsection; /** * Sets up the object with basic settings and preparse it for use + * + * @param moodle_page $page */ public function __construct(moodle_page &$page) { if (during_initial_install()) { @@ -2471,12 +1854,15 @@ public function __construct(moodle_page &$page) { $this->page->navigation->initialise(); // Initialise the navigation cache $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); + $this->children = new navigation_node_collection(); } /** * Initialise the settings navigation based on the current context * * This function initialises the settings navigation tree for a given context * by calling supporting functions to generate major parts of the tree. + * + * @global moodle_database $DB */ public function initialise() { global $DB; @@ -2520,13 +1906,13 @@ public function initialise() { break; } - $settingskey = $this->load_user_settings($this->page->course->id); - $adminkey = $this->load_administration_settings(); + $settings = $this->load_user_settings($this->page->course->id); + $admin = $this->load_administration_settings(); - if ($context->contextlevel == CONTEXT_SYSTEM) { - $this->get($adminkey)->forceopen = true; - } else if ($context->contextlevel == CONTEXT_USER) { - $this->get($settingskey)->forceopen = true; + if ($context->contextlevel == CONTEXT_SYSTEM && $admin) { + $admin->force_open(); + } else if ($context->contextlevel == CONTEXT_USER && $settings) { + $settings->force_open(); } // Check if the user is currently logged in as another user @@ -2539,11 +1925,12 @@ public function initialise() { } // Make sure the first child doesnt have proceed with hr set to true - reset($this->children); - current($this->children)->preceedwithhr = false; - $this->remove_empty_root_branches(); - $this->respect_forced_open(); + foreach ($this->children as $key=>$node) { + if ($node->nodetype != self::NODETYPE_BRANCH || $node->children->count()===0) { + $node->remove(); + } + } } /** * Override the parent function so that we can add preceeding hr's and set a @@ -2558,13 +1945,12 @@ public function initialise() { * @param string|int $key * @param int $type * @param string $icon - * @return sting|int A key that can be used to reference the newly added node + * @return navigation_node */ public function add($text, $url=null, $type=null, $shorttext=null, $key=null, pix_icon $icon=null) { - $key = parent::add($text, $url, $type, $shorttext, $key, $icon); - $this->get($key)->add_class('root_node'); - $this->get($key)->preceedwithhr = true; - return $key; + $node = parent::add($text, $url, $type, $shorttext, $key, $icon); + $node->add_class('root_node'); + return $node; } /** @@ -2577,28 +1963,25 @@ public function add($text, $url=null, $type=null, $shorttext=null, $key=null, pi * @param string|int $key * @param int $type * @param string $icon - * @return sting|int A key that can be used to reference the newly added node + * @return navigation_node */ public function prepend($text, $url=null, $type=null, $shorttext=null, $key=null, pix_icon $icon=null) { - $key = $this->add($text, $url, $type, $shorttext, $key, $icon); $children = $this->children; - $this->children = array(); - $this->children[$key] = array_pop($children); - foreach ($children as $k=>$child) { - $this->children[$k] = $child; - $this->get($k)->add_class('root_node'); - $this->get($k)->preceedwithhr = true; + $this->children = new get_class($children); + $node = $this->add($text, $url, $type, $shorttext, $key, $icon); + foreach ($children as $child) { + $this->children->add($child); } - return $key; + return $node; } /** * Load the site administration tree * * This function loads the site administration tree by using the lib/adminlib library functions * - * @param null|navigation_node $referencebranch A reference to a branch in the settings + * @param navigation_node $referencebranch A reference to a branch in the settings * navigation tree - * @param null|part_of_admin_tree $adminbranch The branch to add, if null generate the admin + * @param part_of_admin_tree $adminbranch The branch to add, if null generate the admin * tree and start at the beginning * @return mixed A key to access the admin tree by */ @@ -2618,22 +2001,23 @@ protected function load_administration_settings(navigation_node $referencebranch // Disable the navigation from automatically finding the active node navigation_node::$autofindactive = false; - $branchkey = $this->add(get_string('administrationsite'), null, self::TYPE_SETTING, null, 'root'); - $referencebranch = $this->get($branchkey); + $referencebranch = $this->add(get_string('administrationsite'), null, self::TYPE_SETTING, null, 'root'); foreach ($adminroot->children as $adminbranch) { $this->load_administration_settings($referencebranch, $adminbranch); } navigation_node::$autofindactive = true; // Use the admin structure to locate the active page - if ($current = $adminroot->locate($this->adminsection, true)) { - // Get the active node using the path in the active page - if ($child = $this->get_by_path(array_reverse($current->path))) { - // Make the node active! - $child->make_active(); + if (!$this->contains_active_node() && $current = $adminroot->locate($this->adminsection, true)) { + $currentnode = $this; + while (($pathkey = array_pop($current->path))!==null && $currentnode) { + $currentnode = $currentnode->get($pathkey); + } + if ($currentnode) { + $currentnode->make_active(); } } - return $branchkey; + return $referencebranch; } else if ($adminbranch->check_access()) { // We have a reference branch that we can access and is not hidden `hurrah` // Now we need to display it and any children it may have @@ -2646,8 +2030,7 @@ protected function load_administration_settings(navigation_node $referencebranch } // Add the branch - $branchkey = $referencebranch->add($adminbranch->visiblename, $url, self::TYPE_SETTING, null, $adminbranch->name, $icon); - $reference = $referencebranch->get($branchkey); + $reference = $referencebranch->add($adminbranch->visiblename, $url, self::TYPE_SETTING, null, $adminbranch->name, $icon); if ($adminbranch->is_hidden()) { if (($adminbranch instanceof admin_externalpage || $adminbranch instanceof admin_settingpage) && $adminbranch->name == $this->adminsection) { @@ -2673,6 +2056,21 @@ protected function load_administration_settings(navigation_node $referencebranch } } + /** + * Gets a navigation node given an array of keys that represent the path to + * the desired node. + * + * @param array $path + * @return navigation_node|false + */ + protected function get_by_path(array $path) { + $node = $this->get(array_shift($path)); + foreach ($path as $key) { + $node->get($key); + } + return $node; + } + /** * Generate the list of modules for the given course. * @@ -2739,7 +2137,7 @@ protected function get_course_modules($course) { * This function loads the course settings that are available for the user * * @param bool $forceopen If set to true the course node will be forced open - * @return bool|mixed Either false of a key to access the course tree by + * @return navigation_node|false */ protected function load_course_settings($forceopen = false) { global $CFG, $USER, $SESSION, $OUTPUT; @@ -2753,9 +2151,10 @@ protected function load_course_settings($forceopen = false) { return false; } - $coursenodekey = $this->add(get_string('courseadministration'), null, self::TYPE_COURSE, null, 'courseadmin'); - $coursenode = $this->get($coursenodekey); - $coursenode->forceopen = ($forceopen==true); + $coursenode = $this->add(get_string('courseadministration'), null, self::TYPE_COURSE, null, 'courseadmin'); + if ($forceopen) { + $coursenode->force_open(); + } if (has_capability('moodle/course:update', $coursecontext)) { // Add the turn on/off settings @@ -2785,11 +2184,11 @@ protected function load_course_settings($forceopen = false) { } $sections = $this->cache->{'coursesections'.$course->id}; - $addresource = $this->get($this->add(get_string('addresource'))); - $addactivity = $this->get($this->add(get_string('addactivity'))); + $addresource = $this->add(get_string('addresource')); + $addactivity = $this->add(get_string('addactivity')); if ($formatidentifier!==0) { - $addresource->forceopen = true; - $addactivity->forceopen = true; + $addresource->force_open(); + $addactivity->force_open(); } if (!$this->cache->cached('course'.$course->id.'resources')) { @@ -2821,7 +2220,7 @@ protected function load_course_settings($forceopen = false) { } else { $url->param('add', $value); } - $addresource->get($sectionresources)->add($resource, $url, self::TYPE_SETTING); + $sectionresources->add($resource, $url, self::TYPE_SETTING); } $subbranch = false; foreach ($activities as $activityname=>$activity) { @@ -2830,7 +2229,7 @@ protected function load_course_settings($forceopen = false) { continue; } if (strpos($activity, '--')===0) { - $subbranch = $addactivity->get($sectionactivities)->add(trim($activity, '-')); + $subbranch = $sectionresources->add(trim($activity, '-')); continue; } $url = new moodle_url('/course/mod.php', array('id'=>$course->id, 'sesskey'=>sesskey(), 'section'=>$section->section)); @@ -2842,9 +2241,9 @@ protected function load_course_settings($forceopen = false) { $url->param('add', $activityname); } if ($subbranch !== false) { - $addactivity->get($sectionactivities)->get($subbranch)->add($activity, $url, self::TYPE_SETTING); + $subbranch->add($activity, $url, self::TYPE_SETTING); } else { - $addactivity->get($sectionactivities)->add($activity, $url, self::TYPE_SETTING); + $sectionresources->add($activity, $url, self::TYPE_SETTING); } } } @@ -2910,8 +2309,8 @@ protected function load_course_settings($forceopen = false) { $url = new moodle_url('/course/importstudents.php', array('id'=>$course->id)); $coursenode->add(get_string('childcourses'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/course', '')); } else if (has_capability('moodle/role:assign', $coursecontext)) { - $key = $coursenode->add(get_string('childcourses'), null, self::TYPE_SETTING, null, null, new pix_icon('i/course', '')); - $coursenode->get($key)->hidden = true;; + $roleassign = $coursenode->add(get_string('childcourses'), null, self::TYPE_SETTING, null, null, new pix_icon('i/course', '')); + $roleassign->hidden = true; } } @@ -3008,12 +2407,6 @@ protected function load_course_settings($forceopen = false) { } } - // Link to the user own profile (except guests) - if (!isguestuser() and isloggedin()) { - $url = new moodle_url('/user/view.php', array('id'=>$USER->id, 'course'=>$course->id)); - $coursenode->add(get_string('profile'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/user', '')); - } - // Switch roles $roles = array(); $assumedrole = $this->in_alternative_role(); @@ -3032,20 +2425,20 @@ protected function load_course_settings($forceopen = false) { } } if (is_array($roles) && count($roles)>0) { - $switchroleskey = $this->add(get_string('switchroleto')); + $switchroles = $this->add(get_string('switchroleto')); if ((count($roles)==1 && array_key_exists(0, $roles))|| $assumedrole!==false) { - $this->get($switchroleskey)->forceopen = true; + $switchroles->force_open(); } $returnurl = $this->page->url; $returnurl->param('sesskey', sesskey()); $SESSION->returnurl = serialize($returnurl); foreach ($roles as $key=>$name) { $url = new moodle_url('/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>$key, 'returnurl'=>'1')); - $this->get($switchroleskey)->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/roles', '')); + $switchroles->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/roles', '')); } } // Return we are done - return $coursenodekey; + return $coursenode; } /** @@ -3057,7 +2450,7 @@ protected function load_course_settings($forceopen = false) { * * For examples mod/forum/lib.php ::: forum_extend_settings_navigation() * - * @return void|mixed The key to access the module method by + * @return navigation_node|false */ protected function load_module_settings() { global $CFG; @@ -3067,9 +2460,8 @@ protected function load_module_settings() { $this->page->set_cm($cm, $this->page->course); } - $modulekey = $this->add(get_string($this->page->activityname.'administration', $this->page->activityname)); - $modulenode = $this->get($modulekey); - $modulenode->forceopen = true; + $modulenode = $this->add(get_string($this->page->activityname.'administration', $this->page->activityname)); + $modulenode->force_open(); // Settings for the module if (has_capability('moodle/course:manageactivities', $this->page->cm->context)) { @@ -3104,17 +2496,17 @@ protected function load_module_settings() { require_once($file); } if (!function_exists($function)) { - return $modulekey; + return $modulenode; } $function($this, $modulenode); // Remove the module node if there are no children if (empty($modulenode->children)) { - $this->remove_child($modulekey); + $modulenode->remove(); } - return $modulekey; + return $modulenode; } /** @@ -3128,6 +2520,7 @@ protected function load_module_settings() { * any bright ideas please feel free to intervene. * * @param int $courseid The course id of the current course + * @return navigation_node|false */ protected function load_user_settings($courseid=SITEID) { global $USER, $FULLME, $CFG; @@ -3141,11 +2534,10 @@ protected function load_user_settings($courseid=SITEID) { // and the key depends on the current location // Default to look at id $userkey='id'; - if (strpos($FULLME,'/blog/') || strpos($FULLME, $CFG->admin.'/roles/')) { // And blog and roles just do thier own thing using `userid` $userkey = 'userid'; - } else if ($this->context->contextlevel >= CONTEXT_COURSECAT && strpos($FULLME, '/message/')===false && strpos($FULLME, '/mod/forum/user')===false) { + } else if ($this->context->contextlevel >= CONTEXT_COURSECAT && strpos($FULLME, '/message/')===false && strpos($FULLME, '/mod/forum/user')===false && strpos($FULLME, '/user/editadvanced')===false) { // If we have a course context and we are not in message or forum // Message and forum both pick the user up from `id` $userkey = 'user'; @@ -3153,12 +2545,12 @@ protected function load_user_settings($courseid=SITEID) { $userid = optional_param($userkey, $USER->id, PARAM_INT); if ($userid!=$USER->id) { - $userkey = $this->generate_user_settings($courseid, $userid, 'userviewingsettings'); + $usernode = $this->generate_user_settings($courseid, $userid, 'userviewingsettings'); $this->generate_user_settings($courseid, $USER->id); } else { - $userkey = $this->generate_user_settings($courseid, $USER->id); + $usernode = $this->generate_user_settings($courseid, $USER->id); } - return $userkey; + return $usernode; } /** @@ -3168,7 +2560,7 @@ protected function load_user_settings($courseid=SITEID) { * @param int $courseid The current course' id * @param int $userid The user id to load for * @param string $gstitle The string to pass to get_string for the branch title - * @return string|int The key to reference this user's settings + * @return navigation_node|false */ protected function generate_user_settings($courseid, $userid, $gstitle='usercurrentsettings') { global $DB, $CFG, $USER, $SITE; @@ -3215,8 +2607,7 @@ protected function generate_user_settings($courseid, $userid, $gstitle='usercurr $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $this->page->context)); // Add a user setting branch - $usersettingskey = $this->add(get_string($gstitle, 'moodle', $fullname)); - $usersetting = $this->get($usersettingskey); + $usersetting = $this->add(get_string($gstitle, 'moodle', $fullname)); $usersetting->id = 'usersettings'; // Check if the user has been deleted @@ -3271,34 +2662,34 @@ protected function generate_user_settings($courseid, $userid, $gstitle='usercurr // View the roles settings if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:manage'), $usercontext)) { - $roleskey = $usersetting->add(get_string('roles'), null, self::TYPE_SETTING); + $roles = $usersetting->add(get_string('roles'), null, self::TYPE_SETTING); $url = new moodle_url('/admin/roles/usersroles.php', array('userid'=>$user->id, 'courseid'=>$course->id)); - $usersetting->get($roleskey)->add(get_string('thisusersroles', 'role'), $url, self::TYPE_SETTING); + $roles->add(get_string('thisusersroles', 'role'), $url, self::TYPE_SETTING); $assignableroles = get_assignable_roles($usercontext, ROLENAME_BOTH); if (!empty($assignableroles)) { $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); - $usersetting->get($roleskey)->add(get_string('assignrolesrelativetothisuser', 'role'), $url, self::TYPE_SETTING); + $roles->add(get_string('assignrolesrelativetothisuser', 'role'), $url, self::TYPE_SETTING); } if (has_capability('moodle/role:review', $usercontext) || count(get_overridable_roles($usercontext, ROLENAME_BOTH))>0) { $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); - $usersetting->get($roleskey)->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING); + $roles->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING); } $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); - $usersetting->get($roleskey)->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); + $roles->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); } // Portfolio if ($currentuser && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', $systemcontext)) { require_once($CFG->libdir . '/portfoliolib.php'); if (portfolio_instances(true, false)) { - $portfoliokey = $usersetting->add(get_string('portfolios', 'portfolio'), null, self::TYPE_SETTING); - $usersetting->get($portfoliokey)->add(get_string('configure', 'portfolio'), new moodle_url('/user/portfolio.php'), self::TYPE_SETTING); - $usersetting->get($portfoliokey)->add(get_string('logs', 'portfolio'), new moodle_url('/user/portfoliologs.php'), self::TYPE_SETTING); + $portfolio = $usersetting->add(get_string('portfolios', 'portfolio'), null, self::TYPE_SETTING); + $portfolio->add(get_string('configure', 'portfolio'), new moodle_url('/user/portfolio.php'), self::TYPE_SETTING); + $portfolio->add(get_string('logs', 'portfolio'), new moodle_url('/user/portfoliologs.php'), self::TYPE_SETTING); } } @@ -3324,20 +2715,19 @@ protected function generate_user_settings($courseid, $userid, $gstitle='usercurr $usersetting->add(get_string('editmymessage', 'message'), $url, self::TYPE_SETTING); } - return $usersettingskey; + return $usersetting; } /** * Loads block specific settings in the navigation * - * @return string The key that can be used to retrieve the navigation node + * @return navigation_node */ protected function load_block_settings() { global $CFG; - $blocksettingskey = $this->add(print_context_name($this->context)); - $blocknode = $this->get($blocksettingskey); - $blocknode->forceopen = true; + $blocknode = $this->add(print_context_name($this->context)); + $blocknode->force_open(); // Assign local roles $assignurl = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$this->context->id)); @@ -3354,20 +2744,19 @@ protected function load_block_settings() { $blocknode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); } - return $blocksettingskey; + return $blocknode; } /** * Loads category specific settings in the navigation * - * @return string + * @return navigation_node */ protected function load_category_settings() { global $CFG; - $blocksettingskey = $this->add(print_context_name($this->context)); - $blocknode = $this->get($blocksettingskey); - $blocknode->forceopen = true; + $blocknode = $this->add(print_context_name($this->context)); + $blocknode->force_open(); if ($this->page->user_is_editing() && has_capability('moodle/category:manage', $this->context)) { $blocknode->add(get_string('editcategorythis'), new moodle_url('/course/editcategory.php', array('id' => $this->context->instanceid))); @@ -3389,7 +2778,7 @@ protected function load_category_settings() { $blocknode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); } - return $blocksettingskey; + return $blocknode; } /** @@ -3420,6 +2809,7 @@ protected function in_alternative_role() { /** * This function loads all of the front page settings into the settings navigation. * This function is called when the user is on the front page, or $COURSE==$SITE + * @return navigation_node */ protected function load_front_page_settings($forceopen = false) { global $SITE, $CFG; @@ -3427,9 +2817,10 @@ protected function load_front_page_settings($forceopen = false) { $course = clone($SITE); $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Course context - $frontpagekey = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage'); - $frontpage = $this->get($frontpagekey); - $frontpage->forceopen = $forceopen; + $frontpage = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage'); + if ($forceopen) { + $frontpage->force_open(); + } $frontpage->id = 'frontpagesettings'; if (has_capability('moodle/course:update', $coursecontext)) { @@ -3513,17 +2904,7 @@ protected function load_front_page_settings($forceopen = false) { $url = new moodle_url('/files/index.php', array('id'=>$course->id)); $frontpage->add(get_string('files'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/files', '')); } - } - - /** - * This function removes all root branches that have no children - */ - public function remove_empty_root_branches() { - foreach ($this->children as $key=>$node) { - if ($node->nodetype != self::NODETYPE_BRANCH || count($node->children)===0) { - $this->remove_child($key); - } - } + return $frontpage; } /** @@ -3547,8 +2928,6 @@ class navigation_json { protected $nodetype = array('node','branch'); /** @var array */ protected $expandable = array(); - /** @var int */ - protected $expansionceiling = array(); /** * Turns a branch and all of its children into XML * @@ -3562,26 +2941,19 @@ public function convert($branch) { /** * Set the expandable items in the array so that we have enough information * to attach AJAX events + * @param array $expandable */ public function set_expandable($expandable) { foreach ($expandable as $node) { $this->expandable[(string)$node['branchid']] = $node; } } - /** - * Sets the upper limit for expandable nodes. Any nodes that are of the specified - * type or larger will not be expandable - * - * @param int $expansionceiling One of navigation_node::TYPE_* - */ - public function set_expansionceiling($expansionceiling) { - $tihs->expansionceiling = $expansionceiling; - } /** * Recusively converts a child node and its children to XML for output * * @param navigation_node $child The child to convert * @param int $depth Pointlessly used to track the depth of the XML structure + * @return string JSON */ protected function convert_child($child, $depth=1) { global $OUTPUT; @@ -3619,8 +2991,6 @@ protected function convert_child($child, $depth=1) { if (array_key_exists((string)$child->key, $this->expandable)) { $attributes['expandable'] = $child->key; $child->add_class($this->expandable[$child->key]['id']); - } else if ($child->type >= $this->expansionceiling) { - $attributes['expansionceiling'] = $child->key; } if (count($child->classes)>0) { @@ -3632,7 +3002,7 @@ protected function convert_child($child, $depth=1) { $attributes['link'] = $child->action->out(); } $attributes['hidden'] = ($child->hidden); - $attributes['haschildren'] = (count($child->children)>0 || $child->type == navigation_node::TYPE_CATEGORY); + $attributes['haschildren'] = ($child->children->count()>0 || $child->type == navigation_node::TYPE_CATEGORY); if (count($child->children)>0) { $attributes['children'] = array(); diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 6a243b0df7d03..ad4e999c726da 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -1959,7 +1959,7 @@ public function container_end() { return $this->opencontainers->pop('container'); } - /** + /** * Make nested HTML lists out of the items * * The resulting list will look something like this: @@ -1990,7 +1990,7 @@ function tree_block_contents($items, $attrs=array()) { // this applies to the li item which contains all child lists too $content = $item->content($this); $liclasses = array($item->get_css_type()); - if (!$item->forceopen || (!$item->forceopen && $item->collapse) || (count($item->children)==0 && $item->nodetype==navigation_node::NODETYPE_BRANCH)) { + if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count()==0 && $item->nodetype==navigation_node::NODETYPE_BRANCH)) { $liclasses[] = 'collapsed'; } if ($item->isactive === true) { @@ -1999,7 +1999,7 @@ function tree_block_contents($items, $attrs=array()) { $liattr = array('class'=>join(' ',$liclasses)); // class attribute on the div item which only contains the item content $divclasses = array('tree_item'); - if (!empty($item->children) || $item->nodetype==navigation_node::NODETYPE_BRANCH) { + if ($item->children->count()>0 || $item->nodetype==navigation_node::NODETYPE_BRANCH) { $divclasses[] = 'branch'; } else { $divclasses[] = 'leaf'; @@ -2026,7 +2026,63 @@ function tree_block_contents($items, $attrs=array()) { * @return string XHTML navbar */ public function navbar() { - return $this->page->navbar->content(); + //return $this->page->navbar->content(); + + $items = $this->page->navbar->get_items(); + + $count = 0; + + $htmlblocks = array(); + // Iterate the navarray and display each node + foreach ($items as $item) { + $htmlblocks[] = html_writer::tag('li', $this->render($item)); + } + + // XHTML + return join(get_separator(), $htmlblocks); + } + + protected function render_navigation_node(navigation_node $item) { + $content = $item->get_content(); + $title = $item->get_title(); + if ($item->icon instanceof renderable) { + $icon = $this->render($item->icon); + $content = $icon.' '.$content; // use CSS for spacing of icons + } + if ($item->helpbutton !== null) { + $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton')); + } + if ($content === '') { + continue; + } + if ($item->action instanceof action_link) { + //TODO: to be replaced with something else + $link = $item->action; + if ($item->hidden) { + $link->add_class('dimmed'); + } + $content = $this->output->render($link); + } else if ($item->action instanceof moodle_url) { + $attributes = array(); + if ($title !== '') { + $attributes['title'] = $title; + } + if ($item->hidden) { + $attributes['class'] = 'dimmed_text'; + } + $content = html_writer::link($item->action, $content, $attributes); + + } else if (is_string($item->action) || empty($item->action)) { + $attributes = array(); + if ($title !== '') { + $attributes['title'] = $title; + } + if ($item->hidden) { + $attributes['class'] = 'dimmed_text'; + } + $content = html_writer::tag('span', $content, $attributes); + } + return $content; } /** diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index f4e7c60d85b46..776acabb2a30e 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -379,7 +379,8 @@ protected function find_module($component) { case 'core_dock': $module = array('name' => 'core_dock', 'fullpath' => '/blocks/dock.js', - 'requires' => array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container')); + 'requires' => array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'), + 'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('undockall', 'block'))); break; case 'core_calendar': $module = array('name' => 'core_calendar', diff --git a/lib/pagelib.php b/lib/pagelib.php index 333f14db31a90..fb6975a1fecd5 100644 --- a/lib/pagelib.php +++ b/lib/pagelib.php @@ -171,7 +171,7 @@ class moodle_page { protected $_button = ''; protected $_theme = null; - /** @var null|global_navigation Contains the global navigation structure*/ + /** @var global_navigation Contains the global navigation structure*/ protected $_navigation = null; /** @var null|settings_navigation Contains the settings navigation structure*/ protected $_settingsnav = null; @@ -520,7 +520,7 @@ protected function magic_get_opencontainers() { */ protected function magic_get_navigation() { if ($this->_navigation === null) { - $this->_navigation = new global_navigation(); + $this->_navigation = new global_navigation($this); } return $this->_navigation; } diff --git a/lib/simpletest/testnavigationlib.php b/lib/simpletest/testnavigationlib.php index 6c8e1fcaeaa0c..cd753e6faa0a4 100644 --- a/lib/simpletest/testnavigationlib.php +++ b/lib/simpletest/testnavigationlib.php @@ -41,6 +41,11 @@ class navigation_node_test extends UnitTestCase { protected $activeurl = null; protected $inactivenode = null; + /** + * @var navigation_node + */ + public $node; + public function setUp() { global $CFG, $PAGE; parent::setUp(); @@ -53,19 +58,16 @@ public function setUp() { $this->node = new navigation_node('Test Node'); $this->node->type = navigation_node::TYPE_SYSTEM; - $this->node->add('demo1', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', '')); - $this->node->add('demo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', '')); - $this->node->add('demo3', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3',new pix_icon('i/course', '')); - $this->node->get('demo3')->add('demo4', $this->inactiveurl,navigation_node::TYPE_COURSE, null, 'demo4', new pix_icon('i/course', '')); - $this->node->get('demo3')->add('demo5', $this->activeurl, navigation_node::TYPE_COURSE, null, 'demo5',new pix_icon('i/course', '')); - $this->node->get('demo3')->get('demo5')->add('activity1', null, navigation_node::TYPE_ACTIVITY, null, 'activity1'); - $this->node->get('demo3')->get('demo5')->get('activity1')->make_active(); - $this->node->add('hiddendemo1', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'hiddendemo1', new pix_icon('i/course', '')); - $this->node->get('hiddendemo1')->hidden = true; - $this->node->get('hiddendemo1')->add('hiddendemo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'hiddendemo2', new pix_icon('i/course', '')); - $this->node->get('hiddendemo1')->add('hiddendemo3', $this->inactiveurl, navigation_node::TYPE_COURSE,null, 'hiddendemo3', new pix_icon('i/course', '')); - $this->node->get('hiddendemo1')->get('hiddendemo2')->helpbutton = 'Here is a help button'; - $this->node->get('hiddendemo1')->get('hiddendemo3')->display = false; + $demo1 = $this->node->add('demo1', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', '')); + $demo2 = $this->node->add('demo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', '')); + $demo3 = $this->node->add('demo3', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3',new pix_icon('i/course', '')); + $demo4 = $demo3->add('demo4', $this->inactiveurl,navigation_node::TYPE_COURSE, null, 'demo4', new pix_icon('i/course', '')); + $demo5 = $demo3->add('demo5', $this->activeurl, navigation_node::TYPE_COURSE, null, 'demo5',new pix_icon('i/course', '')); + $demo5->add('activity1', null, navigation_node::TYPE_ACTIVITY, null, 'activity1')->make_active(); + $hiddendemo1 = $this->node->add('hiddendemo1', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'hiddendemo1', new pix_icon('i/course', '')); + $hiddendemo1->hidden = true; + $hiddendemo1->add('hiddendemo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'hiddendemo2', new pix_icon('i/course', ''))->helpbutton = 'Here is a help button';; + $hiddendemo1->add('hiddendemo3', $this->inactiveurl, navigation_node::TYPE_COURSE,null, 'hiddendemo3', new pix_icon('i/course', ''))->display = false; } public function test___construct() { @@ -81,16 +83,19 @@ public function test___construct() { public function test_add() { global $CFG; // Add a node with all args set - $key1 = $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','key',new pix_icon('i/course', '')); + $node1 = $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','key',new pix_icon('i/course', '')); // Add a node with the minimum args required - $key2 = $this->node->add('test_add_2',null, navigation_node::TYPE_CUSTOM,'testadd2'); - $key3 = $this->node->add(str_repeat('moodle ', 15),str_repeat('moodle', 15)); - $this->assertEqual('key:'.navigation_node::TYPE_COURSE,$key1); - $this->assertEqual($key2, $this->node->get($key2)->key); - $this->assertEqual($key3, $this->node->get($key3)->key); - $this->assertIsA($this->node->get('key'), 'navigation_node'); - $this->assertIsA($this->node->get($key2), 'navigation_node'); - $this->assertIsA($this->node->get($key3), 'navigation_node'); + $node2 = $this->node->add('test_add_2',null, navigation_node::TYPE_CUSTOM,'testadd2'); + $node3 = $this->node->add(str_repeat('moodle ', 15),str_repeat('moodle', 15)); + + $this->assertIsA($node1, 'navigation_node'); + $this->assertIsA($node2, 'navigation_node'); + $this->assertIsA($node3, 'navigation_node'); + + $this->assertReference($node1, $this->node->get('key')); + $this->assertReference($node2, $this->node->get($node2->key)); + $this->assertReference($node2, $this->node->get($node2->key, $node2->type)); + $this->assertReference($node3, $this->node->get($node3->key, $node3->type)); } public function test_add_class() { @@ -103,30 +108,20 @@ public function test_add_class() { } } - public function test_add_to_path() { - global $CFG; - $path = array('demo3','demo5'); - $key1 = $this->node->add_to_path($path,'testatp1', 'Test add to path 1', 'testatp1', navigation_node::TYPE_COURSE, 'http://www.moodle.org/', new pix_icon('i/course', '')); - $this->assertEqual($key1, 'testatp1:'.navigation_node::TYPE_COURSE); - - // This should generate an exception as we have not provided any text for - // the node - $this->expectException(); - $key3 = $this->node->add_to_path(array('demo3','dud1','dud2'), 'text', 'shorttext'); - $this->assertFalse($key3); - - // This should generate an exception as we have not provided any text for - // the node - $this->expectException(new coding_exception('You must set the text for the node when you create it.')); - $key2 = $this->node->add_to_path($path); - } public function test_check_if_active() { // First test the string urls // demo1 -> action is http://www.moodle.org/, thus should be true - $this->assertTrue($this->node->get('demo3')->get('demo5')->check_if_active()); + $demo5 = $this->node->find('demo5', navigation_node::TYPE_COURSE); + if ($this->assertIsA($demo5, 'navigation_node')) { + $this->assertTrue($demo5->check_if_active()); + } + // demo2 -> action is http://www.moodle.com/, thus should be false - $this->assertFalse($this->node->get('demo2')->check_if_active()); + $demo2 = $this->node->get('demo2'); + if ($this->assertIsA($demo2, 'navigation_node')) { + $this->assertFalse($demo2->check_if_active()); + } } public function test_contains_active_node() { @@ -139,80 +134,34 @@ public function test_contains_active_node() { $this->assertFalse($this->node->get('demo1')->contains_active_node()); // Should be true as demo5 contains activity1 $this->assertTrue($this->node->get('demo3')->get('demo5')->contains_active_node()); - // Should be false activity1 doesnt contain the active node... it is the active node - $this->assertFalse($this->node->get('demo3')->get('demo5')->get('activity1')->contains_active_node()); + // Should be true activity1 is the active node + $this->assertTrue($this->node->get('demo3')->get('demo5')->get('activity1')->contains_active_node()); // Obviously duff $this->assertFalse($this->node->get('demo3')->get('demo4')->contains_active_node()); } - public function test_content() { - $this->node->get('demo3')->get('demo5')->action = null; - $this->node->get('demo3')->get('demo5')->title('This is a title'); - $this->node->get('demo3')->get('demo5')->hidden = true; - $this->node->get('demo3')->get('demo5')->icon = null; - $this->node->get('demo3')->get('demo5')->helpbutton = 'A fake help button'; - $content1 = $this->node->get('demo1')->content(); - $content2 = $this->node->get('demo3')->content(); - $content3 = $this->node->get('demo3')->get('demo5')->content(); - $content4 = $this->node->get('hiddendemo1')->get('hiddendemo2')->content(); - $content5 = $this->node->get('hiddendemo1')->get('hiddendemo3')->content(); - $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('demo1')->action->out()), $content1); - $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('demo3')->action->out()), $content2); - $this->assert(new ContainsTagWithAttribute('span','class','dimmed_text'), $content3); - #$this->assertEqual($content3, 'A fake help buttondemo5'); - $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('hiddendemo1')->get('hiddendemo2')->action->out()), $content4); - $this->assertTrue(empty($content5)); - } - - public function test_get_active_node() { - $node1 = $this->node->get_active_node(); - $node2 = $this->node->get('demo3')->get_active_node(); - $this->assertFalse($node1); - $this->assertIsA($node2, 'navigation_node'); - } - public function test_find_active_node() { $activenode1 = $this->node->find_active_node(); - $activenode2 = $this->node->find_active_node(navigation_node::TYPE_COURSE); - $activenode3 = $this->node->find_active_node(navigation_node::TYPE_CATEGORY); - $activenode4 = $this->node->get('demo1')->find_active_node(navigation_node::TYPE_COURSE); - $this->assertIsA($activenode1, 'navigation_node'); - if ($activenode1 instanceof navigation_node) { - $this->assertEqual($activenode1, $this->node->get('demo3')->get('demo5')->get('activity1')); - } - $this->assertIsA($activenode2, 'navigation_node'); - if ($activenode1 instanceof navigation_node) { - $this->assertEqual($activenode2, $this->node->get('demo3')->get('demo5')); - } - $this->assertIsA($activenode3, 'navigation_node'); - if ($activenode1 instanceof navigation_node) { - $this->assertEqual($activenode3, $this->node->get('demo3')); + $activenode2 = $this->node->get('demo1')->find_active_node(); + + if ($this->assertIsA($activenode1, 'navigation_node')) { + $this->assertReference($activenode1, $this->node->get('demo3')->get('demo5')->get('activity1')); } - $this->assertNotA($activenode4, 'navigation_node'); + + $this->assertNotA($activenode2, 'navigation_node'); } - public function test_find_child() { - $node1 = $this->node->find_child('demo1', navigation_node::TYPE_COURSE); - $node2 = $this->node->find_child('demo5', navigation_node::TYPE_COURSE); - $node3 = $this->node->find_child('demo5', navigation_node::TYPE_CATEGORY); - $node4 = $this->node->find_child('demo0', navigation_node::TYPE_COURSE); + public function test_find() { + $node1 = $this->node->find('demo1', navigation_node::TYPE_COURSE); + $node2 = $this->node->find('demo5', navigation_node::TYPE_COURSE); + $node3 = $this->node->find('demo5', navigation_node::TYPE_CATEGORY); + $node4 = $this->node->find('demo0', navigation_node::TYPE_COURSE); $this->assertIsA($node1, 'navigation_node'); $this->assertIsA($node2, 'navigation_node'); $this->assertNotA($node3, 'navigation_node'); $this->assertNotA($node4, 'navigation_node'); } - public function test_find_child_depth() { - $depth1 = $this->node->find_child_depth('demo1',navigation_node::TYPE_COURSE); - $depth2 = $this->node->find_child_depth('demo5',navigation_node::TYPE_COURSE); - $depth3 = $this->node->find_child_depth('demo5',navigation_node::TYPE_CATEGORY); - $depth4 = $this->node->find_child_depth('demo0',navigation_node::TYPE_COURSE); - $this->assertEqual(1, $depth1); - $this->assertEqual(1, $depth2); - $this->assertFalse($depth3); - $this->assertFalse($depth4); - } - public function test_find_expandable() { $expandable = array(); $this->node->find_expandable($expandable); @@ -223,7 +172,7 @@ public function test_find_expandable() { $name .= $expandable[2]['branchid']; $name .= $expandable[3]['branchid']; $name .= $expandable[4]['branchid']; - $this->assertEqual($name, 'demo1:20demo2:20demo4:20hiddendemo2:20hiddendemo3:20'); + $this->assertEqual($name, 'demo1demo2demo4hiddendemo2hiddendemo3'); } } @@ -238,15 +187,6 @@ public function test_get() { $this->assertFalse($node4); } - public function test_get_by_path() { - $node1 = $this->node->get_by_path(array('demo3', 'demo4')); // This path exists and should return a node - $node2 = $this->node->get_by_path(array('demo1', 'demo2')); // Both elements exist but demo2 is not a child of demo1 - $node3 = $this->node->get_by_path(array('demo0', 'demo6')); // This path is totally bogus - $this->assertIsA($node1, 'navigation_node'); - $this->assertFalse($node2); - $this->assertFalse($node3); - } - public function test_get_css_type() { $csstype1 = $this->node->get('demo3')->get_css_type(); $csstype2 = $this->node->get('demo3')->get('demo5')->get_css_type(); @@ -259,39 +199,38 @@ public function test_get_css_type() { public function test_make_active() { global $CFG; - $key1 = $this->node->add('active node 1', null, navigation_node::TYPE_CUSTOM, null, 'anode1'); - $key2 = $this->node->add('active node 2', new moodle_url($CFG->wwwroot), navigation_node::TYPE_COURSE, null, 'anode2'); - $this->node->get($key1)->make_active(); - $this->node->get($key2)->make_active(); - $this->assertTrue($this->node->get($key1)->isactive); - $this->assertTrue($this->node->get($key2)->isactive); - } - public function test_remove_child() { - $this->node->add('child to remove 1', null, navigation_node::TYPE_CUSTOM, null, 'remove1'); - $this->node->add('child to remove 2', null, navigation_node::TYPE_CUSTOM, null, 'remove2'); - $this->node->get('remove2')->add('child to remove 3', null, navigation_node::TYPE_CUSTOM, null, 'remove3'); + $node1 = $this->node->add('active node 1', null, navigation_node::TYPE_CUSTOM, null, 'anode1'); + $node2 = $this->node->add('active node 2', new moodle_url($CFG->wwwroot), navigation_node::TYPE_COURSE, null, 'anode2'); + $node1->make_active(); + $this->node->get('anode2')->make_active(); + $this->assertTrue($node1->isactive); + $this->assertTrue($this->node->get('anode2')->isactive); + } + public function test_remove() { + $remove1 = $this->node->add('child to remove 1', null, navigation_node::TYPE_CUSTOM, null, 'remove1'); + $remove2 = $this->node->add('child to remove 2', null, navigation_node::TYPE_CUSTOM, null, 'remove2'); + $remove3 = $remove2->add('child to remove 3', null, navigation_node::TYPE_CUSTOM, null, 'remove3'); + + $this->assertIsA($remove1, 'navigation_node'); + $this->assertIsA($remove2, 'navigation_node'); + $this->assertIsA($remove3, 'navigation_node'); + $this->assertIsA($this->node->get('remove1'), 'navigation_node'); - $this->assertTrue($this->node->remove_child('remove1')); - $this->assertFalse($this->node->remove_child('remove3')); - $this->assertFalse($this->node->remove_child('remove0')); - $this->assertTrue($this->node->remove_child('remove2')); + $this->assertIsA($this->node->get('remove2'), 'navigation_node'); + $this->assertIsA($remove2->get('remove3'), 'navigation_node'); + + $this->assertTrue($remove1->remove()); + $this->assertTrue($this->node->get('remove2')->remove()); + $this->assertTrue($remove2->get('remove3')->remove()); + + $this->assertFalse($this->node->get('remove1')); + $this->assertFalse($this->node->get('remove2')); } public function test_remove_class() { $this->node->add_class('testclass'); $this->assertTrue($this->node->remove_class('testclass')); $this->assertFalse(in_array('testclass', $this->node->classes)); } - public function test_respect_forced_open() { - $this->node->respect_forced_open(); - $this->assertTrue($this->node->forceopen); - } - public function test_toggle_type_display() { - $this->node->toggle_type_display(navigation_node::TYPE_CATEGORY); - $this->assertFalse($this->node->get('demo1')->display); - $this->assertFalse($this->node->get('demo3')->get('demo5')->display); - $this->assertTrue($this->node->get('demo3')->display); - $this->node->toggle_type_display(navigation_node::TYPE_CATEGORY, true); - } } /** @@ -301,7 +240,8 @@ public function test_toggle_type_display() { class exposed_global_navigation extends global_navigation { protected $exposedkey = 'exposed_'; function __construct() { - parent::__construct(); + global $PAGE; + parent::__construct($PAGE); $this->cache = new navigation_cache('simpletest_nav'); } function __call($method, $arguments) { @@ -359,24 +299,24 @@ public function setUp() { $this->cache = new navigation_cache('simpletest_nav'); $this->node = new exposed_global_navigation(); // Create an initial tree structure to work with - $this->node->add('category 1', null, navigation_node::TYPE_CATEGORY, null, 'cat1'); - $this->node->add('category 2', null, navigation_node::TYPE_CATEGORY, null, 'cat2'); - $this->node->add('category 3', null, navigation_node::TYPE_CATEGORY, null, 'cat3'); - $this->node->get('cat2')->add('sub category 1', null, navigation_node::TYPE_CATEGORY, null, 'sub1'); - $this->node->get('cat2')->add('sub category 2', null, navigation_node::TYPE_CATEGORY, null, 'sub2'); - $this->node->get('cat2')->add('sub category 3', null, navigation_node::TYPE_CATEGORY, null, 'sub3'); - $this->node->get('cat2')->get('sub2')->add('course 1', null, navigation_node::TYPE_COURSE, null, 'course1'); - $this->node->get('cat2')->get('sub2')->add('course 2', null, navigation_node::TYPE_COURSE, null, 'course2'); - $this->node->get('cat2')->get('sub2')->add('course 3', null, navigation_node::TYPE_COURSE, null, 'course3'); - $this->node->get('cat2')->get('sub2')->get('course2')->add('section 1', null, navigation_node::TYPE_COURSE, null, 'sec1'); - $this->node->get('cat2')->get('sub2')->get('course2')->add('section 2', null, navigation_node::TYPE_COURSE, null, 'sec2'); - $this->node->get('cat2')->get('sub2')->get('course2')->add('section 3', null, navigation_node::TYPE_COURSE, null, 'sec3'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 1', null, navigation_node::TYPE_ACTIVITY, null, 'act1'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 2', null, navigation_node::TYPE_ACTIVITY, null, 'act2'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 3', null, navigation_node::TYPE_ACTIVITY, null, 'act3'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 1', null, navigation_node::TYPE_RESOURCE, null, 'res1'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 2', null, navigation_node::TYPE_RESOURCE, null, 'res2'); - $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 3', null, navigation_node::TYPE_RESOURCE, null, 'res3'); + $cat1 = $this->node->add('category 1', null, navigation_node::TYPE_CATEGORY, null, 'cat1'); + $cat2 = $this->node->add('category 2', null, navigation_node::TYPE_CATEGORY, null, 'cat2'); + $cat3 = $this->node->add('category 3', null, navigation_node::TYPE_CATEGORY, null, 'cat3'); + $sub1 = $cat2->add('sub category 1', null, navigation_node::TYPE_CATEGORY, null, 'sub1'); + $sub2 = $cat2->add('sub category 2', null, navigation_node::TYPE_CATEGORY, null, 'sub2'); + $sub3 = $cat2->add('sub category 3', null, navigation_node::TYPE_CATEGORY, null, 'sub3'); + $course1 = $sub2->add('course 1', null, navigation_node::TYPE_COURSE, null, 'course1'); + $course2 = $sub2->add('course 2', null, navigation_node::TYPE_COURSE, null, 'course2'); + $course3 = $sub2->add('course 3', null, navigation_node::TYPE_COURSE, null, 'course3'); + $section1 = $course2->add('section 1', null, navigation_node::TYPE_COURSE, null, 'sec1'); + $section2 = $course2->add('section 2', null, navigation_node::TYPE_COURSE, null, 'sec2'); + $section3 = $course2->add('section 3', null, navigation_node::TYPE_COURSE, null, 'sec3'); + $act1 = $section2->add('activity 1', null, navigation_node::TYPE_ACTIVITY, null, 'act1'); + $act2 = $section2->add('activity 2', null, navigation_node::TYPE_ACTIVITY, null, 'act2'); + $act3 = $section2->add('activity 3', null, navigation_node::TYPE_ACTIVITY, null, 'act3'); + $res1 = $section2->add('resource 1', null, navigation_node::TYPE_RESOURCE, null, 'res1'); + $res2 = $section2->add('resource 2', null, navigation_node::TYPE_RESOURCE, null, 'res2'); + $res3 = $section2->add('resource 3', null, navigation_node::TYPE_RESOURCE, null, 'res3'); $this->cache->clear(); $this->cache->modinfo5 = unserialize($this->modinfo5); @@ -384,218 +324,28 @@ public function setUp() { $this->cache->canviewhiddenactivities = true; $this->cache->canviewhiddensections = true; $this->cache->canviewhiddencourses = true; - $this->node->get('cat2')->get('sub2')->add('Test Course 5', new moodle_url('http://moodle.org'),navigation_node::TYPE_COURSE,null,'5'); - } - public function test_add_categories() { - $categories = array(); - for ($i=0;$i<3;$i++) { - $categories[$i] = new stdClass; - $categories[$i]->id = 'sub4_'.$i; - $categories[$i]->name = 'add_categories '.$i; - } - $this->node->exposed_add_categories(array('cat3'), $categories); - $this->assertEqual(count($this->node->get('cat3')->children), 3); - $this->assertIsA($this->node->get('cat3')->get('sub4_1'), 'navigation_node'); - $this->node->get('cat3')->children = array(); + $sub2->add('Test Course 5', new moodle_url('http://moodle.org'),navigation_node::TYPE_COURSE,null,'5'); } - public function test_add_course_section_generic() { - $keys = array('cat2', 'sub2', '5'); + public function test_load_generic_course_sections() { + $coursenode = $this->node->find('5', navigation_node::TYPE_COURSE); $course = new stdClass; $course->id = '5'; $course->numsections = 10; $course->modinfo = $this->modinfo5; - $this->node->add_course_section_generic($keys, $course, 'topic', 'topic'); - $this->assertEqual(count($this->node->get_by_path($keys)->children),4); - } - public function test_add_category_by_path() { - $category = new stdClass; - $category->id = 'sub3'; - $category->name = 'Sub category 3'; - $category->path = '/cat2/sub3'; - $this->node->exposed_add_category_by_path($category); - $this->assertIsA($this->node->get('cat2')->get('sub3'), 'navigation_node'); - } - - public function test_load_course_categories() { - global $PAGE; - $originalcategories = $PAGE->categories; - $PAGE->categories = array(); - $PAGE->categories[0] = new stdClass; - $PAGE->categories[0]->id = 130; - $PAGE->categories[0]->name = 'category1'; - $PAGE->categories[1] = new stdClass; - $PAGE->categories[1]->id = 131; - $PAGE->categories[1]->name = 'category0'; - $test = new exposed_global_navigation(); - $keys = array(); - $keys[] = $test->add('base level', null, null, null, 'base'); - $catcount = $test->exposed_load_course_categories($keys); - $this->assertIsA($test->get('base'), 'navigation_node'); - $this->assertIsA($test->get('base')->get(131), 'navigation_node'); - $this->assertIsA($test->get('base')->get(131)->get(130), 'navigation_node'); - $PAGE->categories = $originalcategories; - } - - public function test_init() { - global $PAGE, $SITE; - $originalcontext = $PAGE->context; - $test = new mock_initialise_global_navigation(); - // System - $PAGE->context->contextlevel = CONTEXT_SYSTEM; - $node1 = clone($test); - $node1->initialise(); - $this->assertIsA($node1->get('initcall1'), 'navigation_node'); - if ($node1->get('initcall1')) { - $this->assertEqual($node1->get('initcall1')->text, 'load_for_category'); - } - // Course category - $PAGE->context->contextlevel = CONTEXT_COURSECAT; - $node2 = clone($test); - $node2->initialise(); - $this->assertIsA($node2->get('initcall3'), 'navigation_node'); - if ($node2->get('initcall3')) { - $this->assertEqual($node2->get('initcall3')->text, 'load_for_category'); - } - $PAGE->context->contextlevel = CONTEXT_COURSE; - // For course (we need to adjust the site id so we look like a normal course - $SITE->id++; - $node3 = clone($test); - $node3->initialise(); - $this->assertIsA($node3->get('initcall5'), 'navigation_node'); - if ($node3->get('initcall5')) { - $this->assertEqual($node3->get('initcall5')->text, 'load_for_course'); - } - $SITE->id--; - // Course is site - $node4 = clone($test); - $node4->initialise(); - $this->assertIsA($node4->get('initcall7'), 'navigation_node'); - if ($node4->get('initcall7')) { - $this->assertEqual($node4->get('initcall7')->text, 'load_for_category'); - } - $PAGE->context->contextlevel = CONTEXT_MODULE; - $node5 = clone($test); - $node5->initialise(); - $this->assertIsA($node5->get('initcall9'), 'navigation_node'); - if ($node5->get('initcall9')) { - $this->assertEqual($node5->get('initcall9')->text, 'load_for_activity'); - } - $PAGE->context->contextlevel = CONTEXT_BLOCK; - $node6 = clone($test); - $node6->initialise(); - $this->assertIsA($node6->get('initcall11'), 'navigation_node'); - if ($node6->get('initcall11')) { - $this->assertEqual($node6->get('initcall11')->text, 'load_for_course'); - } - $PAGE->context->contextlevel = CONTEXT_USER; - $node7 = clone($test); - $node7->initialise(); - $this->assertIsA($node7->get('initcall13'), 'navigation_node'); - if ($node7->get('initcall13')) { - $this->assertEqual($node7->get('initcall13')->text, 'load_for_user'); - } - $PAGE->context = $originalcontext; - } - - public function test_add_courses() { - $courses = array(); - for ($i=0;$i<5;$i++) { - $course = new stdClass; - $course->id = $i; - $course->visible = true; - $course->category = 'cat3'; - $course->fullname = "Test Course $i"; - $course->shortname = "tcourse$i"; - $course->numsections = 10; - $course->modinfo = $this->modinfo5; - $courses[$i] = $course; - } - - $this->node->add_courses($courses); - $this->assertIsA($this->node->get('cat3')->get(0), 'navigation_node'); - $this->assertIsA($this->node->get('cat3')->get(1), 'navigation_node'); - $this->assertIsA($this->node->get('cat3')->get(2), 'navigation_node'); - $this->assertIsA($this->node->get('cat3')->get(3), 'navigation_node'); - $this->assertIsA($this->node->get('cat3')->get(4), 'navigation_node'); - $this->node->get('cat3')->children = array(); - } - public function test_can_display_type() { - $this->node->expansionlimit = navigation_node::TYPE_COURSE; - $this->assertTrue($this->node->exposed_can_display_type(navigation_node::TYPE_CATEGORY)); - $this->assertTrue($this->node->exposed_can_display_type(navigation_node::TYPE_COURSE)); - $this->assertFalse($this->node->exposed_can_display_type(navigation_node::TYPE_SECTION)); - $this->node->expansionlimit = null; - } - public function test_content() { - $html1 = $this->node->content(); - $this->node->expansionlimit = navigation_node::TYPE_CATEGORY; - $html2 = $this->node->content(); - $this->node->expansionlimit = null; - $this->assert(new ContainsTagWithAttribute('a','href',$this->node->action->out()), $html1); - $this->assert(new ContainsTagWithAttribute('a','href',$this->node->action->out()), $html2); + $this->node->load_generic_course_sections($course, $coursenode, 'topic', 'topic'); + $this->assertEqual($coursenode->children->count(),4); } public function test_format_display_course_content() { $this->assertTrue($this->node->exposed_format_display_course_content('topic')); $this->assertFalse($this->node->exposed_format_display_course_content('scorm')); $this->assertTrue($this->node->exposed_format_display_course_content('dummy')); } - public function test_load_course_activities() { - $keys = array('cat2', 'sub2', '5'); - $course = new stdClass; - $course->id = '5'; - $course->numsections = 10; - $modinfo = $this->cache->modinfo5; - $modinfo->cms[290] = clone($modinfo->cms[288]); - $modinfo->cms[290]->id = 290; - $modinfo->cms[290]->modname = 'resource'; - $modinfo->cms[290]->instance = 21; - $modinfo->instances['resource'] = array(); - $modinfo->instances['resource'][21] = clone($modinfo->instances['forum'][19]); - $modinfo->instances['resource'][21]->id = 21; - $this->cache->modinfo5 = $modinfo; - $course->modinfo = serialize($modinfo); - $this->node->exposed_load_course_activities($keys, $course); - - $this->assertIsA($this->node->get_by_path(array_merge($keys, array(288))), 'navigation_node'); - $this->assertEqual($this->node->get_by_path(array_merge($keys, array(288)))->type, navigation_node::TYPE_ACTIVITY); - $this->assertIsA($this->node->get_by_path(array_merge($keys, array(290))), 'navigation_node'); - $this->assertEqual($this->node->get_by_path(array_merge($keys, array(290)))->type, navigation_node::TYPE_ACTIVITY); - } - public function test_load_course_sections() { - $keys = array('cat2', 'sub2', '5'); - $course = new stdClass; - $course->id = '5'; - $course->format = 'topics'; - $course->numsections = 10; - $course->modinfo = $this->modinfo5; - $coursechildren = $this->node->get_by_path($keys)->children; - - $this->node->get_by_path(array('cat2', 'sub2', '5'))->children = array(); - $this->node->exposed_load_course_sections($keys, $course); - - $course->format = 'topics'; - $this->node->get_by_path(array('cat2', 'sub2', '5'))->children = array(); - $this->node->exposed_load_course_sections($keys, $course); - - $course->format = 'scorm'; - $this->node->get_by_path(array('cat2', 'sub2', '5'))->children = array(); - $this->node->exposed_load_course_sections($keys, $course); - - $course->format = 'sillywilly'; - $this->node->get_by_path(array('cat2', 'sub2', '5'))->children = array(); - $this->node->exposed_load_course_sections($keys, $course); - - $this->node->get_by_path($keys)->children = $coursechildren; - } - public function test_load_for_user() { - $this->node->exposed_load_for_user(); - } public function test_load_section_activities() { - $keys = array('cat2', 'sub2', '5'); + $node = $this->node->find('5', navigation_node::TYPE_COURSE); $course = new stdClass; $course->id = '5'; $course->numsections = 10; - $this->node->get_by_path($keys)->add('Test Section 1', null, navigation_node::TYPE_SECTION, null, $this->cache->coursesections5[1]->id); + $section = $node->add('Test Section 1', null, navigation_node::TYPE_SECTION, null, $this->cache->coursesections5[1]->id); $modinfo = $this->cache->modinfo5; $modinfo->sections[1] = array(289, 290); $modinfo->cms[289] = clone($modinfo->cms[288]); @@ -615,12 +365,13 @@ public function test_load_section_activities() { $modinfo->instances['resource'][21]->id = 21; $this->cache->modinfo5 = $modinfo; $course->modinfo = serialize($modinfo); - $this->node->exposed_load_section_activities($keys, 1, $course); - $keys[] = 97; - $this->assertIsA($this->node->get_by_path(array_merge($keys, array(289))),'navigation_node'); - $this->assertEqual($this->node->get_by_path(array_merge($keys, array(289)))->type, navigation_node::TYPE_ACTIVITY); - $this->assertIsA($this->node->get_by_path(array_merge($keys, array(290))),'navigation_node'); - $this->assertEqual($this->node->get_by_path(array_merge($keys, array(290)))->type, navigation_node::TYPE_ACTIVITY); + $activities = $this->node->exposed_load_section_activities($section, 1, $modinfo); + foreach ($activities as $activity) { + if ($this->assertIsA($activity, 'navigation_node')) { + $this->assertEqual($activity->type, navigation_node::TYPE_ACTIVITY); + $this->assertReference($activity, $section->get($activity->key)); + } + } } public function test_module_extends_navigation() { $this->cache->test1_extends_navigation = true; @@ -668,7 +419,7 @@ public function setUp() { $this->node = new exposed_navbar(); $temptree = new global_navigation_test(); $temptree->setUp(); - $temptree->node->get_by_path(array('cat2','sub2', 'course2'))->make_active(); + $temptree->node->find('course2', navigation_node::TYPE_COURSE)->make_active(); $PAGE->navigation = $temptree->node; } public function tearDown() { @@ -680,24 +431,14 @@ public function test_add() { // Add a node with all args set $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','testadd1',new pix_icon('i/course', '')); // Add a node with the minimum args required - $key2 = $this->node->add('test_add_2'); + $this->node->add('test_add_2','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd2','testadd2',new pix_icon('i/course', '')); $this->assertIsA($this->node->get('testadd1'), 'navigation_node'); - $this->assertIsA($this->node->get('testadd1')->get($key2), 'navigation_node'); - } - public function test_content() { - $this->assertTrue( (strpos($this->node->content(), $this->node->action->out()) !== false) ); + $this->assertIsA($this->node->get('testadd2'), 'navigation_node'); } public function test_has_items() { global $PAGE; $this->assertTrue($this->node->has_items()); } - public function test_parse_branch_to_html() { - global $CFG; - $key = $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','testadd1',new pix_icon('i/course', '')); - $this->node->get($key)->make_active(); - $html = $this->node->exposed_parse_branch_to_html($this->node->children, true, true); - $this->assertTrue( (strpos($html, $this->node->action->out()) !== false) ); - } } class navigation_cache_test extends UnitTestCase { @@ -777,27 +518,7 @@ public function test___initialise() { $this->node->initialise(); $this->assertEqual($this->node->id, 'settingsnav'); } - public function test_load_front_page_settings() { - $this->node->exposed_load_front_page_settings(); - $settings = false; - foreach ($this->node->children as $child) { - if ($child->id === 'frontpagesettings') { - $settings = $child; - } - } - $this->assertIsA($settings, 'navigation_node'); - } public function test_in_alternative_role() { $this->assertFalse($this->node->exposed_in_alternative_role()); } - public function test_remove_empty_root_branches() { - $this->node->add('rootbranch1', null, navigation_node::TYPE_SETTING, null, 'rootbranch1'); - $this->node->add('rootbranch2', null, navigation_node::TYPE_SETTING, null, 'rootbranch2'); - $this->node->add('rootbranch3', null, navigation_node::TYPE_SETTING, null, 'rootbranch3'); - $this->node->get('rootbranch2')->add('something', null, navigation_node::TYPE_SETTING); - $this->node->remove_empty_root_branches(); - $this->assertFalse($this->node->get('rootbranch1')); - $this->assertIsA($this->node->get('rootbranch2'), 'navigation_node'); - $this->assertFalse($this->node->get('rootbranch3')); - } } diff --git a/mod/assignment/type/online/assignment.class.php b/mod/assignment/type/online/assignment.class.php index 9b738f443902e..5f2af3ac640ad 100644 --- a/mod/assignment/type/online/assignment.class.php +++ b/mod/assignment/type/online/assignment.class.php @@ -348,13 +348,13 @@ function extend_settings_navigation($node) { $node->add(get_string('viewmysubmission', 'assignment'), $link, navigation_node::TYPE_SETTING); if (!empty($submission->timemodified)) { - $key = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); - $node->get($key)->text = preg_replace('#([^,])\s#', '$1 ', $node->get($key)->text); - $node->get($key)->add_class('note'); + $submittednode = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); + $submittednode->text = preg_replace('#([^,])\s#', '$1 ', $submittednode->text); + $submittednode->add_class('note'); if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) { - $node->get($key)->add_class('early'); + $submittednode->add_class('early'); } else { - $node->get($key)->add_class('late'); + $submittednode->add_class('late'); } } } diff --git a/mod/assignment/type/upload/assignment.class.php b/mod/assignment/type/upload/assignment.class.php index e3bd75b5ea5a0..4e4c0d6732f3b 100644 --- a/mod/assignment/type/upload/assignment.class.php +++ b/mod/assignment/type/upload/assignment.class.php @@ -1084,13 +1084,13 @@ function extend_settings_navigation($node) { $link = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id)); $node->add(get_string('viewmysubmission', 'assignment'), $link, navigation_node::TYPE_SETTING); if (!empty($submission->timemodified)) { - $key = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); - $node->get($key)->text = preg_replace('#([^,])\s#', '$1 ', $node->get($key)->text); - $node->get($key)->add_class('note'); + $submittednode = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); + $submittednode->text = preg_replace('#([^,])\s#', '$1 ', $submittednode->text); + $submittednode->add_class('note'); if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) { - $node->get($key)->add_class('early'); + $submittednode->add_class('early'); } else { - $node->get($key)->add_class('late'); + $submittednode->add_class('late'); } } } @@ -1100,15 +1100,15 @@ function extend_settings_navigation($node) { $fs = get_file_storage(); if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $USER->id, "timemodified", false)) { if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) { - $filekey = $node->add(get_string('submission', 'assignment')); + $filenode = $node->add(get_string('submission', 'assignment')); } else { - $filekey = $node->add(get_string('submissiondraft', 'assignment')); + $filenode = $node->add(get_string('submissiondraft', 'assignment')); } foreach ($files as $file) { $filename = $file->get_filename(); $mimetype = $file->get_mimetype(); $link = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$USER->id.'/'.$filename); - $node->get($filekey)->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_mimetype_icon($mimetype),'')); + $filenode->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_mimetype_icon($mimetype),'')); } } } diff --git a/mod/assignment/type/uploadsingle/assignment.class.php b/mod/assignment/type/uploadsingle/assignment.class.php index 9401c80631a9b..a4c1cbeca603a 100644 --- a/mod/assignment/type/uploadsingle/assignment.class.php +++ b/mod/assignment/type/uploadsingle/assignment.class.php @@ -204,13 +204,13 @@ function extend_settings_navigation($node) { $link = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id)); $node->add(get_string('viewmysubmission', 'assignment'), $link, navigation_node::TYPE_SETTING); if (!empty($submission->timemodified)) { - $key = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); - $node->get($key)->text = preg_replace('#([^,])\s#', '$1 ', $node->get($key)->text); - $node->get($key)->add_class('note'); + $submissionnode = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified)); + $submissionnode->text = preg_replace('#([^,])\s#', '$1 ', $submissionnode->text); + $submissionnode->add_class('note'); if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) { - $node->get($key)->add_class('early'); + $submissionnode->add_class('early'); } else { - $node->get($key)->add_class('late'); + $submissionnode->add_class('late'); } } } @@ -219,12 +219,12 @@ function extend_settings_navigation($node) { if ($submission && has_capability('mod/assignment:submit', $this->context) && $this->count_user_files($USER->id)) { $fs = get_file_storage(); if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $USER->id, "timemodified", false)) { - $filekey = $node->add(get_string('submission', 'assignment')); + $filenode = $node->add(get_string('submission', 'assignment')); foreach ($files as $file) { $filename = $file->get_filename(); $mimetype = $file->get_mimetype(); $link = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$USER->id.'/'.$filename); - $node->get($filekey)->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_mimetype_icon($mimetype), '')); + $filenode->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_mimetype_icon($mimetype), '')); } } } diff --git a/mod/chat/lib.php b/mod/chat/lib.php index 608b62cfc8dcb..6d281f5b1332f 100644 --- a/mod/chat/lib.php +++ b/mod/chat/lib.php @@ -1274,8 +1274,7 @@ function chat_extend_navigation($navigation, $course, $module, $cm) { $chatusers = chat_get_users($cm->instance, $currentgroup, $cm->groupingid); if (is_array($chatusers) && count($chatusers)>0) { - $userskey = $navigation->add(get_string('currentusers', 'chat')); - $users = $navigation->get($userskey); + $users = $navigation->add(get_string('currentusers', 'chat')); foreach ($chatusers as $chatuser) { $userlink = new moodle_url('/user/view.php', array('id'=>$chatuser->id,'course'=>$course->id)); $users->add(fullname($chatuser).' '.format_time(time() - $chatuser->lastmessageping), $userlink, navigation_node::TYPE_USER, null, null, new pix_icon('c/user', '')); @@ -1294,8 +1293,8 @@ function chat_extend_settings_navigation(settings_navigation $settings, navigati $chat = $DB->get_record("chat", array("id" => $PAGE->cm->instance)); if ($chat->chattime && $chat->schedule) { - $key = $chatnode->add(get_string('nextsession', 'chat').': '.userdate($chat->chattime).' ('.usertimezone($USER->timezone)); - $chatnode->get($key)->add_class('note'); + $nextsessionnode = $chatnode->add(get_string('nextsession', 'chat').': '.userdate($chat->chattime).' ('.usertimezone($USER->timezone)); + $nextsessionnode->add_class('note'); } $currentgroup = groups_get_activity_group($PAGE->cm, true); @@ -1310,4 +1309,4 @@ function chat_extend_settings_navigation(settings_navigation $settings, navigati $chatnode->add(get_string('viewreport', 'chat'), new moodle_url('/mod/chat/report.php', array('id'=>$PAGE->cm->id))); } } - } +} \ No newline at end of file diff --git a/mod/choice/lib.php b/mod/choice/lib.php index 509e612755a66..c455898573800 100644 --- a/mod/choice/lib.php +++ b/mod/choice/lib.php @@ -949,4 +949,4 @@ function choice_extend_settings_navigation(settings_navigation $settings, naviga } $choicenode->add(get_string("viewallresponses", "choice", $responsecount), new moodle_url('/mod/choice/report.php', array('id'=>$PAGE->cm->id))); } - } \ No newline at end of file +} \ No newline at end of file diff --git a/mod/data/lib.php b/mod/data/lib.php index 5f25ec590a822..c3b32e0634045 100755 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -2848,8 +2848,8 @@ function data_extend_navigation($navigation, $course, $module, $cm) { /// Check the number of entries required against the number of entries already made (doesn't apply to teachers) if ($data->requiredentries > 0 && $numentries < $data->requiredentries && !has_capability('mod/data:manageentries', get_context_instance(CONTEXT_MODULE, $cm->id))) { $data->entriesleft = $data->requiredentries - $numentries; - $key = $navigation->add(get_string('entrieslefttoadd', 'data', $data)); - $navigation->get($key)->add_class('note'); + $entriesnode = $navigation->add(get_string('entrieslefttoadd', 'data', $data)); + $entriesnode->add_class('note'); } $navigation->add(get_string('list', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance))); @@ -2904,8 +2904,7 @@ function data_extend_settings_navigation(settings_navigation $settings, navigati $defaultemplate = 'singletemplate'; } - $templatekey = $datanode->add(get_string('templates', 'data')); - $templates = $datanode->get($templatekey); + $templates = $datanode->add(get_string('templates', 'data')); $templatelist = array ('listtemplate', 'singletemplate', 'asearchtemplate', 'addtemplate', 'rsstemplate', 'csstemplate', 'jstemplate'); foreach ($templatelist as $template) { @@ -2915,4 +2914,4 @@ function data_extend_settings_navigation(settings_navigation $settings, navigati $datanode->add(get_string('fields', 'data'), new moodle_url('/mod/data/field.php', array('d'=>$data->id))); $datanode->add(get_string('presets', 'data'), new moodle_url('/mod/data/preset.php', array('d'=>$data->id))); } - } +} \ No newline at end of file diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 3f1b0b02ab36d..c3ae55a519b72 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -2274,22 +2274,18 @@ function feedback_extend_settings_navigation(settings_navigation $settings, navi global $PAGE, $DB; if (!$context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->id)) { - print_error('badcontext'); + print_error('badcontext'); } - // $capabilities = feedback_load_capabilities($PAGE->cm->id); - - // if($capabilities->edititems) { - if(has_capability('mod/feedback:edititems', $context)) { - $qkey = $feedbacknode->add(get_string('questions', 'feedback')); - $feedbacknode->get($qkey)->add(get_string('edit_items', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'edit'))); - $feedbacknode->get($qkey)->add(get_string('export_questions', 'feedback'), new moodle_url('/mod/feedback/export.php', array('id'=>$PAGE->cm->id, 'action'=>'exportfile'))); - $feedbacknode->get($qkey)->add(get_string('import_questions', 'feedback'), new moodle_url('/mod/feedback/import.php', array('id'=>$PAGE->cm->id))); - $feedbacknode->get($qkey)->add(get_string('templates', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'templates'))); + if (has_capability('mod/feedback:edititems', $context)) { + $questionnode = $feedbacknode->add(get_string('questions', 'feedback')); + $questionnode->add(get_string('edit_items', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'edit'))); + $questionnode->add(get_string('export_questions', 'feedback'), new moodle_url('/mod/feedback/export.php', array('id'=>$PAGE->cm->id, 'action'=>'exportfile'))); + $questionnode->add(get_string('import_questions', 'feedback'), new moodle_url('/mod/feedback/import.php', array('id'=>$PAGE->cm->id))); + $questionnode->add(get_string('templates', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'templates'))); } - // if($capabilities->viewreports) { - if(has_capability('mod/feedback:viewreports', $context)) { + if (has_capability('mod/feedback:viewreports', $context)) { $feedback = $DB->get_record('feedback', array('id'=>$PAGE->cm->instance)); if($feedback->course == SITEID){ $feedbacknode->add(get_string('analysis', 'feedback'), new moodle_url('/mod/feedback/analysis_course.php', array('id'=>$PAGE->cm->id, 'course'=>$PAGE->course->id,'do_show'=>'analysis'))); diff --git a/mod/forum/lib.php b/mod/forum/lib.php index 3807ba2b38229..fe23da83facf4 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -7965,7 +7965,7 @@ function forum_get_extra_capabilities() { */ function forum_extend_navigation($navref, $course, $module, $cm) { global $CFG, $OUTPUT, $USER; - + $limit = 5; $discussions = forum_get_discussions($cm,"d.timemodified DESC", false, -1, $limit); @@ -7973,13 +7973,13 @@ function forum_extend_navigation($navref, $course, $module, $cm) { if (!is_array($discussions) || count($discussions)==0) { return; } - $discussionkey = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')'); - $navref->get($discussionkey)->mainnavonly = true; + $discussionnode = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')'); + $discussionnode->mainnavonly = true; foreach ($discussions as $discussion) { $icon = new pix_icon('i/feedback', ''); $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$discussion->discussion)); - $navref->get($discussionkey)->add($discussion->subject, $url, navigation_node::TYPE_SETTING, null, null, $icon); + $discussionnode->add($discussion->subject, $url, navigation_node::TYPE_SETTING, null, null, $icon); } if ($discussioncount > count($discussions)) { @@ -7988,7 +7988,7 @@ function forum_extend_navigation($navref, $course, $module, $cm) { } else { $url = new moodle_url('/mod/forum/view.php', array('id'=>$cm->id)); } - $childkey = $navref->get($discussionkey)->add(get_string('viewalldiscussions', 'forum'), $url, navigation_node::TYPE_SETTING, null, null, $icon); + $discussionnode->add(get_string('viewalldiscussions', 'forum'), $url, navigation_node::TYPE_SETTING, null, null, $icon); } $index = 0; @@ -8002,13 +8002,13 @@ function forum_extend_navigation($navref, $course, $module, $cm) { forum_get_recent_mod_activity($recentposts, $index, $lastlogin, $course->id, $cm->id); if (is_array($recentposts) && count($recentposts)>0) { - $recentkey = $navref->add(get_string('recentactivity').' ('.count($recentposts).')'); - $navref->get($recentkey)->mainnavonly = true; + $recentnode = $navref->add(get_string('recentactivity').' ('.count($recentposts).')'); + $recentnode->mainnavonly = true; foreach ($recentposts as $post) { $icon = new pix_icon('i/feedback', ''); $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->content->discussion)); $title = $post->content->subject."\n".userdate($post->timestamp, get_string('strftimerecent', 'langconfig'))."\n".$post->user->firstname.' '.$post->user->lastname; - $navref->get($recentkey)->add($title, $url, navigation_node::TYPE_SETTING, null, null, $icon); + $recentnode->add($title, $url, navigation_node::TYPE_SETTING, null, null, $icon); } } } @@ -8027,10 +8027,10 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi $PAGE->cm->context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->instance); } if (is_enrolled($PAGE->cm->context)) { // means enrolled users only - $notekey = false; + $notenode = false; $helpbutton = false; if (forum_is_forcesubscribed($forumobject)) { - $notekey = $forumnode->add(get_string("forcessubscribe", 'forum')); + $notenode = $forumnode->add(get_string("forcessubscribe", 'forum')); $string = get_string('allowchoice', 'forum'); $helpbutton = $OUTPUT->old_help_icon("subscription", $string, "forum"); if (has_capability('mod/forum:managesubscriptions', $PAGE->cm->context)) { @@ -8041,11 +8041,11 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi } } else if ($forumobject->forcesubscribe == FORUM_DISALLOWSUBSCRIBE) { $string = get_string('disallowsubscribe', 'forum'); - $notekey = $forumnode->add($string); + $notenode = $forumnode->add($string); $helpbutton = $OUTPUT->old_help_icon("subscription", $string, "forum"); } else { $string = get_string("forcesubscribe", "forum"); - $notekey = $forumnode->add(get_string("allowsallsubscribe", 'forum')); + $notenode = $forumnode->add(get_string("allowsallsubscribe", 'forum')); $helpbutton = $OUTPUT->old_help_icon("subscription", $string, "forum"); if (has_capability('mod/forum:managesubscriptions', $PAGE->cm->context)) { @@ -8081,13 +8081,13 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forumobject->id)); $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING); } - if ($notekey!==false) { - $forumnode->get($notekey)->add_class('note'); + if ($notenode!==false) { + $notenode->add_class('note'); if ($helpbutton!==false) { - $forumnode->get($notekey)->helpbutton = $helpbutton; + $notenode->helpbutton = $helpbutton; } } - } + } if (!empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds) && $forumobject->rsstype && $forumobject->rssarticles) { diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index f8eeb016a573c..dd3cafed42527 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -2710,4 +2710,4 @@ function glossary_extend_settings_navigation(settings_navigation $settings, navi if (has_capability('mod/glossary:write', $PAGE->cm->context)) { $glossarynode->add(get_string('addentry', 'glossary'), new moodle_url('/mod/glossary/edit.php', array('cmid'=>$PAGE->cm->id))); } - } +} diff --git a/mod/lesson/lib.php b/mod/lesson/lib.php index a349207b534fd..a7ed35ad05c4c 100644 --- a/mod/lesson/lib.php +++ b/mod/lesson/lib.php @@ -804,19 +804,19 @@ function lesson_extend_settings_navigation($settings, $lessonnode) { $canedit = has_capability('mod/lesson:edit', $PAGE->cm->context); $url = new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id)); - $key = $lessonnode->add(get_string('preview', 'lesson'), $url); + $lessonnode->add(get_string('preview', 'lesson'), $url); if ($canedit) { $url = new moodle_url('/mod/lesson/edit.php', array('id'=>$PAGE->cm->id)); - $key = $lessonnode->add(get_string('edit', 'lesson'), $url); + $lessonnode->add(get_string('edit', 'lesson'), $url); } if (has_capability('mod/lesson:manage', $PAGE->cm->context)) { - $key = $lessonnode->add(get_string('reports', 'lesson')); + $reportsnode = $lessonnode->add(get_string('reports', 'lesson')); $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportoverview')); - $lessonnode->get($key)->add(get_string('overview', 'lesson'), $url); + $reportsnode->add(get_string('overview', 'lesson'), $url); $url = new moodle_url('/mod/lesson/report.php', array('id'=>$PAGE->cm->id, 'action'=>'reportdetail')); - $lessonnode->get($key)->add(get_string('detailedstats', 'lesson'), $url); + $reportsnode->add(get_string('detailedstats', 'lesson'), $url); } if ($canedit) { @@ -828,7 +828,7 @@ function lesson_extend_settings_navigation($settings, $lessonnode) { $url = new moodle_url('/mod/lesson/highscores.php', array('id'=>$PAGE->cm->id)); $lessonnode->add(get_string('highscores', 'lesson'), $url); } - } +} /** * Get list of available import or export formats diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index eaf151b51cfc5..9d938bb9bebfa 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -1625,13 +1625,13 @@ function quiz_extend_settings_navigation($settings, $quiznode) { } if (has_capability('mod/quiz:viewreports', $PAGE->cm->context)) { $url = new moodle_url('/mod/quiz/report.php', array('q'=>$PAGE->cm->instance)); - $reportkey = $quiznode->add(get_string('results', 'quiz'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', '')); + $reportnode = $quiznode->add(get_string('results', 'quiz'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', '')); require_once($CFG->dirroot.'/mod/quiz/report/reportlib.php'); $reportlist = quiz_report_list($PAGE->cm->context); foreach ($reportlist as $report) { $url = new moodle_url('/mod/quiz/report.php', array('q'=>$PAGE->cm->instance, 'mode'=>$report)); - $quiznode->get($reportkey)->add(get_string($report, 'quiz_'.$report), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/item', '')); + $reportnode->add(get_string($report, 'quiz_'.$report), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/item', '')); } } if (has_capability('mod/quiz:preview', $PAGE->cm->context)) { diff --git a/mod/survey/lib.php b/mod/survey/lib.php index b045ce6dab981..c904dd70defdf 100644 --- a/mod/survey/lib.php +++ b/mod/survey/lib.php @@ -825,23 +825,23 @@ function survey_extend_settings_navigation($settings, $surveynode) { global $PAGE; if (has_capability('mod/survey:readresponses', $PAGE->cm->context)) { - $key = $surveynode->add(get_string("responsereports", "survey")); + $responsesnode = $surveynode->add(get_string("responsereports", "survey")); $url = new moodle_url('/mod/survey/report.php', array('id' => $PAGE->cm->id, 'action'=>'summary')); - $surveynode->get($key)->add(get_string("summary", "survey"), $url); + $responsesnode->add(get_string("summary", "survey"), $url); $url = new moodle_url('/mod/survey/report.php', array('id' => $PAGE->cm->id, 'action'=>'scales')); - $surveynode->get($key)->add(get_string("scales", "survey"), $url); + $responsesnode->add(get_string("scales", "survey"), $url); $url = new moodle_url('/mod/survey/report.php', array('id' => $PAGE->cm->id, 'action'=>'questions')); - $surveynode->get($key)->add(get_string("question", "survey"), $url); + $responsesnode->add(get_string("question", "survey"), $url); $url = new moodle_url('/mod/survey/report.php', array('id' => $PAGE->cm->id, 'action'=>'students')); - $surveynode->get($key)->add(get_string('participants'), $url); + $responsesnode->add(get_string('participants'), $url); if (has_capability('mod/survey:download', $PAGE->cm->context)) { $url = new moodle_url('/mod/survey/report.php', array('id' => $PAGE->cm->id, 'action'=>'download')); $surveynode->add(get_string('downloadresults', 'survey'), $url); } } - } \ No newline at end of file +} \ No newline at end of file diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index 8327f1e0b9f86..befff53a077bb 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -676,8 +676,8 @@ function workshop_extend_navigation(navigation_node $navref, stdclass $course, s if (has_capability('mod/workshop:submit', get_context_instance(CONTEXT_MODULE, $cm->id))) { $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id)); - $mysubmissionkey = $navref->add(get_string('mysubmission', 'workshop'), $url); - $navref->get($mysubmissionkey)->mainnavonly = true; + $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url); + $mysubmission->mainnavonly = true; } } diff --git a/my/index.php b/my/index.php index 040687ffef902..f1254624df78c 100644 --- a/my/index.php +++ b/my/index.php @@ -41,7 +41,6 @@ $button = $OUTPUT->single_button($url, $string); $header = $SITE->shortname . ': ' . $strmymoodle; - $PAGE->navbar->add($strmymoodle); $PAGE->set_title($strmymoodle); $PAGE->set_heading($header); diff --git a/notes/index.php b/notes/index.php index 86f8a13541512..2cbb691558837 100644 --- a/notes/index.php +++ b/notes/index.php @@ -22,12 +22,6 @@ if ($userid !== 0) { $url->param('user', $userid); } -if ($filtertype !== '') { - $url->param('filtertype', $filtertype); -} -if ($filterselect !== 0) { - $url->param('filterselect', $filterselect); -} $PAGE->set_url($url); /// tabs compatibility @@ -41,15 +35,11 @@ } /// locate course information -if (!$course = $DB->get_record('course', array('id'=>$courseid))) { - print_error('invalidcourseid'); -} +$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); /// locate user information if ($userid) { - if (!$user = $DB->get_record('user', array('id'=>$userid))) { - print_error('invaliduserid'); - } + $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST); $filtertype = 'user'; $filterselect = $user->id; @@ -82,16 +72,19 @@ $systemcontext = get_context_instance(CONTEXT_SYSTEM); // SYSTEM context $strnotes = get_string('notes', 'notes'); -$link = null; -if (has_capability('moodle/course:viewparticipants', $coursecontext) || has_capability('moodle/site:viewparticipants', $systemcontext)) { - $link = new moodle_url('/user/index.php',array('id'=>$course->id)); -} if ($userid) { + $PAGE->set_context(get_context_instance(CONTEXT_USER, $user->id)); $PAGE->navigation->extend_for_user($user); } else { + $link = null; + if (has_capability('moodle/course:viewparticipants', $coursecontext) || has_capability('moodle/site:viewparticipants', $systemcontext)) { + $link = new moodle_url('/user/index.php',array('id'=>$course->id)); + } $PAGE->navbar->add(get_string('participants'), $link); $PAGE->navbar->add($strnotes); } + +$PAGE->set_pagelayout('course'); $PAGE->set_title($course->shortname . ': ' . $strnotes); $PAGE->set_heading($course->fullname); diff --git a/theme/standard/style/dock.css b/theme/standard/style/dock.css index 8da99f31bd369..421edf1ae4f11 100644 --- a/theme/standard/style/dock.css +++ b/theme/standard/style/dock.css @@ -13,6 +13,10 @@ body.has_dock {margin-left:30px;margin-right:30px;} .dock .controls img {cursor:pointer;} .dock .bd.oversized_content {overflow-y:auto;overflow-x:visible;height:inherit;} .dock .bd.oversized_content .content {margin:6px 6px 6px 0px;padding-bottom:6px;} +.dock .yui-panel .hd {text-align:right;border-width:0 0 1px 0;padding:3px 5px;} +.dock .yui-panel .hd .commands img {margin-right:2px;} + +/**.dock .bd .content .sideblockcommands {text-align:right;border:1px solid #AAA;border-width:0 0 1px 1px;background-image:url([[pix:theme|hgradient]]);background-repeat: repeat-x;}**/ /** YUI overrides **/ .yui-skin-sam .dock .yui-panel {border-width:1px 2px 1px 1px;border-color:#AAA;min-width:150px;} .yui-skin-sam .dock .yui-panel .bd {border-width:0;background-color:#FAFAFA;} diff --git a/user/editadvanced.php b/user/editadvanced.php index 58ba590139726..5653d9580afce 100644 --- a/user/editadvanced.php +++ b/user/editadvanced.php @@ -41,14 +41,14 @@ } $PAGE->set_url($url); -if (!$course = $DB->get_record('course', array('id'=>$course))) { - print_error('invalidcourseid'); -} +$course = $DB->get_record('course', array('id'=>$course), '*', MUST_EXIST); + if (!empty($USER->newadminuser)) { $PAGE->set_course($SITE); $PAGE->set_pagelayout('maintenance'); } else { require_login($course); + $PAGE->set_pagelayout('admin'); } if ($course->id == SITEID) { @@ -60,18 +60,19 @@ if ($id == -1) { // creating new user - require_capability('moodle/user:create', $systemcontext); $user = new object(); $user->id = -1; $user->auth = 'manual'; $user->confirmed = 1; $user->deleted = 0; + require_capability('moodle/user:create', $systemcontext); + admin_externalpage_setup('addnewuser', '', array('id' => -1)); } else { // editing existing user require_capability('moodle/user:update', $systemcontext); - if (!$user = $DB->get_record('user', array('id'=>$id))) { - print_error('invaliduserid'); - } + $user = $DB->get_record('user', array('id'=>$id), '*', MUST_EXIST); + $PAGE->set_context(get_context_instance(CONTEXT_USER, $user->id)); + $PAGE->navigation->extend_for_user($user); } // remote users cannot be edited @@ -94,12 +95,6 @@ die; } -if ($user->id == -1) { - admin_externalpage_setup('addnewuser', '', array('id' => -1)); -} else if ($user->id != $USER->id) { - admin_externalpage_setup('editusers', '', array('id' => $user->id, 'course' => SITEID), $CFG->wwwroot . '/user/editadvanced.php'); -} - //load user preferences useredit_load_preferences($user); @@ -259,15 +254,6 @@ $strnewuser = get_string('newuser'); $userfullname = fullname($user, true); - $link = null; - if (has_capability('moodle/course:viewparticipants', $coursecontext) || has_capability('moodle/site:viewparticipants', $systemcontext)) { - $link = new moodle_url("/user/index.php", array('id'=>$course->id)); - } - $PAGE->navbar->add($strparticipants, $link); - $link = new moodle_url('/user/view.php', array('id'=>$user->id, 'course'=>$course->id)); - $PAGE->navbar->add($userfullname, $link); - $PAGE->navbar->add($streditmyprofile); - $PAGE->set_title("$course->shortname: $streditmyprofile"); $PAGE->set_heading($course->fullname); diff --git a/version.php b/version.php index 64d63bf39dd96..fadd3e8c69e15 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2010041300; // YYYYMMDD = date of the last version bump + $version = 2010041301; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20100419)'; // Human-friendly version name