]> git.mjollnir.org Git - moodle.git/commitdiff
navigation MDL-14632 Very significant navigation commit
authorsamhemelryk <samhemelryk>
Fri, 28 Aug 2009 08:47:31 +0000 (08:47 +0000)
committersamhemelryk <samhemelryk>
Fri, 28 Aug 2009 08:47:31 +0000 (08:47 +0000)
This patch introduces two new blocks global_navigation_tree and settings_navigation_tree
both of which have been designed to make full use of the new navigation objects available through
the $PAGE object.
Bulk of this code is within lib/navigationlib.php

55 files changed:
blocks/global_navigation_tree/block_global_navigation_tree.php [new file with mode: 0644]
blocks/global_navigation_tree/db/upgrade.php [new file with mode: 0644]
blocks/global_navigation_tree/edit_form.php [new file with mode: 0644]
blocks/moodleblock.class.php
blocks/rss_client/editfeed.php
blocks/rss_client/managefeeds.php
blocks/rss_client/viewfeed.php
blocks/settings_navigation_tree/block_settings_navigation_tree.php [new file with mode: 0644]
blocks/settings_navigation_tree/edit_form.php [new file with mode: 0644]
course/category.php
course/format/scorm/config.php
course/format/scorm/lib.php [new file with mode: 0644]
course/format/social/config.php
course/format/social/lib.php [new file with mode: 0644]
course/format/topics/config.php
course/format/topics/lib.php [new file with mode: 0644]
course/format/weeks/config.php
course/format/weeks/lib.php [new file with mode: 0644]
course/search.php
course/switchrole.php [new file with mode: 0644]
index.php
lang/en_utf8/admin.php
lang/en_utf8/block_global_navigation_tree.php [new file with mode: 0644]
lang/en_utf8/block_settings_navigation_tree.php [new file with mode: 0644]
lang/en_utf8/forum.php
lang/en_utf8/moodle.php
lib/adminlib.php
lib/ajax/getnavbranch.php [new file with mode: 0644]
lib/ajax/setuserpref.php
lib/blocklib.php
lib/db/upgrade.php
lib/db/upgradelib.php
lib/deprecatedlib.php
lib/javascript-navigation.js [new file with mode: 0644]
lib/javascript-static.js
lib/navigationlib.php [new file with mode: 0644]
lib/outputrenderers.php
lib/pagelib.php
lib/setup.php
lib/simpletest/testnavigationlib.php [new file with mode: 0644]
lib/weblib.php
mod/forum/lib.php
mod/forum/view.php
pix/t/collapsed_empty.png [new file with mode: 0644]
pix/t/movetoblock.png [new file with mode: 0644]
pix/t/movetosidetab.png [new file with mode: 0644]
pix/t/reload.png [new file with mode: 0644]
theme/standard/gradient_vertical.jpg [new file with mode: 0644]
theme/standard/layout.php
theme/standard/shadow_corners.png [new file with mode: 0644]
theme/standard/shadow_left_right.png [new file with mode: 0644]
theme/standard/shadow_top_bottom.png [new file with mode: 0644]
theme/standard/styles_layout.css
user/tabs.php
version.php

diff --git a/blocks/global_navigation_tree/block_global_navigation_tree.php b/blocks/global_navigation_tree/block_global_navigation_tree.php
new file mode 100644 (file)
index 0000000..4ab412c
--- /dev/null
@@ -0,0 +1,354 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * The global navigation tree block class
+ *
+ * Used to produce the global navigation block new to Moodle 2.0
+ *
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_global_navigation_tree extends block_tree {
+
+    /** @var int */
+    public static $navcount;
+    /** @var string */
+    public $blockname = null;
+    protected $contentgenerated = false;
+
+    /**
+     * Set the initial properties for the block
+     */
+    function init() {
+        global $CFG;
+        $this->blockname = get_class($this);
+        $this->title = get_string('blockname', $this->blockname);
+        $this->version = 2009082800;
+    }
+
+    /**
+     * All multiple instances of this block
+     * @return bool Returns true
+     */
+    function instance_allow_multiple() {
+        return false;
+    }
+
+    /**
+     * Set the applicable formats for this block to all
+     * @return array
+     */
+    function applicable_formats() {
+        return array('all' => true);
+    }
+
+    /**
+     * Allow the user to configure a block instance
+     * @return bool Returns true
+     */
+    function instance_allow_config() {
+        return true;
+    }
+
+    /**
+     * Gets the content for this block by grabbing it from $this->page
+     */
+    function get_content() {
+        global $CFG, $OUTPUT;
+        // First check if we have already generated, don't waste cycles
+        if ($this->contentgenerated === true) {
+            return true;
+        }
+        $this->page->requires->js('lib/javascript-navigation.js');
+        // 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++;
+        
+        // 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 (!empty($this->config->showmycourses) && $this->config->showmycourses=='yes') {
+            $this->showmycourses();
+        }
+
+        if (!empty($this->config->showmyhistory) && $this->config->showmyhistory=='yes') {
+            $this->showmyhistory();
+        }
+
+        $tooglesidetabdisplay = get_string('tooglesidetabdisplay', $this->blockname);
+        $toogleblockdisplay = get_string('toogleblockdisplay', $this->blockname);
+
+
+        // Get the expandable items so we can pass them to JS
+        $expandable = array();
+        $this->page->navigation->find_expandable($expandable);
+        $args = array('expansions'=>$expandable,'instance'=>$this->instance->id);
+        $args['tooglesidetabdisplay'] = $tooglesidetabdisplay;
+        $args['toogleblockdisplay'] = $toogleblockdisplay;
+        // Give JS some information we will use within the JS tree object
+        $this->page->requires->data_for_js('globalnav'.block_global_navigation_tree::$navcount, $args);
+        // Initialise the JS tree object
+        $this->id = 'globalnav'.block_global_navigation_tree::$navcount;
+        $this->page->requires->js_function_call('setup_new_navtree', array($this->id))->on_dom_ready();
+        // Grab the items to display
+        $this->content->items = array($this->page->navigation);
+
+        $url = $this->page->url;
+        $url->param('regenerate','navigation');
+        $reloadstr = get_string('reload');
+        $this->content->footer .= '<a href="'.$url->out().'" class="customcommand"><img src="'.$OUTPUT->old_icon_url('t/reload').'" alt="'.$reloadstr.'" title="'.$reloadstr.'" /></a>';
+        if (empty($this->config->enablesidebarpopout) || $this->config->enablesidebarpopout == 'yes') {
+            user_preference_allow_ajax_update('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, PARAM_INT);
+            if (get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0)) {
+                $icon = $OUTPUT->old_icon_url('t/movetoblock');
+                $string = $toogleblockdisplay;
+            } else {
+                $icon = $OUTPUT->old_icon_url('t/movetosidetab');
+                $string = $tooglesidetabdisplay;
+            }
+            $this->content->footer .= '<a class="moveto customcommand requiresjs"><img src="'.$icon.'" alt="'.$string.'" title="'.$string.'"></a>';
+        }
+
+        // Set content generated to true so that we know it has been done
+        $this->contentgenerated = true;
+        return true;
+    }
+
+    /**
+     * Returns the attributes to set for this block
+     *
+     * This function returns an array of HTML attributes for this block including
+     * the defaults
+     * {@link block_tree->html_attributes()} is used to get the default arguments
+     * and then we check whether the user has enabled hover expansion and add the
+     * appropriate hover class if it has
+     *
+     * @return array An array of HTML attributes
+     */
+    public function html_attributes() {
+        $attributes = parent::html_attributes();
+        if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
+            $attributes['class'] .= ' sideblock_js_expansion';
+        }
+        if (get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0)) {
+            $attributes['class'] .= ' sideblock_js_sidebarpopout';
+        }
+        return $attributes;
+    }
+
+    /**
+     * This function maintains a history of the active pages that a user has visited
+     * and displays it back to the user as part of the navigation structure
+     *
+     * @return bool
+     */
+    protected function showmyhistory() {
+        global $USER, $PAGE, $ME;
+
+        // Create a navigation cache so that we can store the history
+        $cache = new navigation_cache('navigationhistory', 60*60);
+
+        // If the user isn't logged in or is a guest we don't want to display anything
+        if (!isloggedin() || isguest()) {
+            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('history')) {
+            $cache->history = array();
+        }
+        $history = $cache->history;
+        $historycount = count($history);
+
+        // Find the initial active node
+        $child = false;
+        if ($PAGE->navigation->contains_active_node()) {
+            $child = $PAGE->navigation->find_active_node();
+        } else if ($PAGE->settingsnav->contains_active_node()) {
+            $child = $PAGE->settingsnav->find_active_node();
+        }
+        // Check that we found an active child node
+        if ($child!==false) {
+            $properties = array();
+            // Check whether this child contains another active child node
+            // this can happen if we are looking at a module
+            if ($child->contains_active_node()) {
+                $titlebits = array();
+                // Loop while the child contains active nodes and in each iteration
+                // find the next node in the correct direction
+                while ($child!==null && $child->contains_active_node()) {
+                    if (!empty($child->shorttext)) {
+                        $titlebits[] = $child->shorttext;
+                    } else {
+                        $titlebits[] = $child->text;
+                    }
+                    foreach ($child->children as $child) {
+                        if ($child->contains_active_node() || $child->isactive) {
+                            // We have found the active child or one of its parents
+                            // so break the foreach so we can proceed in the while
+                            break;
+                        }
+                    }
+                }
+                if (!empty($child->shorttext)) {
+                    $titlebits[] = $child->shorttext;
+                } else {
+                    $titlebits[] = $child->text;
+                }
+                $properties['text'] = join(' - ', $titlebits);
+                $properties['shorttext'] = join(' - ', $titlebits);
+            } else {
+                $properties['text'] = $child->text;
+                $properties['shorttext'] = $child->shorttext;
+            }
+            $properties['action'] = $child->action;
+            $properties['key'] = $child->key;
+            $properties['type'] = $child->type;
+            $properties['icon'] = $child->icon;
+
+            // Create a new navigation node object free of the main structure
+            // so that it is easily storeable and customised
+            $child = new navigation_node($properties);
+
+            // Check that this page isn't already in the history array. If it is
+            // we will remove it so that it gets added at the top and we dont get
+            // duplicate entries
+            foreach ($history as $key=>$node) {
+                if ($node->key == $child->key && $node->type == $child->type) {
+                    if ($node->action instanceof moodle_url && $child->action instanceof moodle_url && $node->action->compare($child->action)) {
+                        unset($history[$key]);
+                    } else if ($child->action instanceof moodle_url && $child->action->out(true) == $node->action) {
+                        unset($history[$key]);
+                    } else if ($child->action == $node->action) {
+                        unset($history[$key]);
+                    }
+                }
+            }
+            // If there is more than 5 elements in the array remove the first one
+            // We want a fifo array
+            if (count($history) > 5) {
+                array_shift($history);
+            }
+            $child->nodetype = navigation_node::NODETYPE_LEAF;
+            $child->children = array();
+            // Add the child to the history array
+            array_push($history,$child);
+        }
+
+        // If we have `more than nothing` in the history display it :D
+        if ($historycount > 0) {
+            // Add a branch to hold the users history
+            $myhistorybranch = $PAGE->navigation->add(get_string('showmyhistorytitle', $this->blockname), null, 'myhistory',navigation_node::TYPE_CATEGORY);
+            $PAGE->navigation->get($myhistorybranch)->children = array_reverse($history);
+        }
+
+        // Cache the history (or update the cached history as it is)
+        $cache->history = $history;
+
+        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() || isguest()) {
+            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
+        $mycoursesbranch = $PAGE->navigation->add(get_string('mycourses'), null, 'mycourses',navigation_node::TYPE_CATEGORY);
+        $PAGE->navigation->add_courses($courses, $mycoursesbranch);
+
+        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);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/blocks/global_navigation_tree/db/upgrade.php b/blocks/global_navigation_tree/db/upgrade.php
new file mode 100644 (file)
index 0000000..d99a723
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file keeps track of upgrades to the global_navigation_tree block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installtion to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * 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}
+ *
+ * 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
+ * was complex due to us wanting to remvoe the outmoded blocks that this
+ * block was going to replace.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_global_navigation_tree_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/global_navigation_tree/edit_form.php
new file mode 100644 (file)
index 0000000..fcb3bcf
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Form for editing global navigation instances.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Form for editing global navigation instances.
+ *
+ * @package blocks
+ * @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 {
+    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');
+        $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);
+            if (isset($this->block->config->{$modname}) && $this->block->config->{$modname}!=$default) {
+                if ($default=='no') {
+                    $mform->getElement('config_'.$modname)->setSelected('yes');
+                } else {
+                    $mform->getElement('config_'.$modname)->setSelected('no');
+                }
+            } else {
+                $mform->getElement('config_'.$modname)->setSelected($default);
+            }
+        }
+    }
+}
\ No newline at end of file
index c3a928604f2e982e1650dc0cebb326d58c888b70..cb312b6c0b62acdfe9c8194f245355af8f25459f 100644 (file)
@@ -33,6 +33,10 @@ define('BLOCK_TYPE_LIST',    1);
  * Block type of text. Contents of block should be set to standard html text in the content object as items ($this->content->text). Optionally include footer text in $this->content->footer.
  */
 define('BLOCK_TYPE_TEXT',    2);
+/**
+ * Block type of tree. $this->content->items is a list of tree_item objects and $this->content->footer is a string.
+ */
+define('BLOCK_TYPE_TREE',    3);
 
 /**
  * Class for describing a moodle block, all Moodle blocks derive from this class
@@ -440,7 +444,7 @@ class block_base {
             $errors[] = 'title_not_set';
             $correct = false;
         }
-        if (!in_array($this->get_content_type(), array(BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT))) {
+        if (!in_array($this->get_content_type(), array(BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT, BLOCK_TYPE_TREE))) {
             $errors[] = 'invalid_content_type';
             $correct = false;
         }
@@ -520,6 +524,20 @@ class block_base {
     /**
      * Return any HTML attributes that you want added to the outer <div> that
      * of the block when it is output.
+     *
+     * Because of the way certain JS events are wired it is a good idea to ensure
+     * that the default values here still get set.
+     * I found the easiest way to do this and still set anything you want is to
+     * override it within your block in the following way
+     *
+     * <code php>
+     * function html_attributes() {
+     *    $attributes = parent::html_attributes();
+     *    $attributes['class'] .= ' mynewclass';
+     *    return $attributes;
+     * }
+     * </code>
+     *
      * @return array attribute name => value.
      */
     function html_attributes() {
@@ -767,4 +785,55 @@ class block_list extends block_base {
     }
 }
 
-?>
+/**
+ * Specialized class for displaying a tree menu.
+ * 
+ * The {@link get_content()} method involves setting the content of
+ * <code>$this->content->items</code> with an array of {@link tree_item}
+ * objects (these are the top-level nodes). The {@link tree_item::children}
+ * property may contain more tree_item objects, and so on. The tree_item class
+ * itself is abstract and not intended for use, use one of it's subclasses.
+ * 
+ * Unlike {@link block_list}, the icons are specified as part of the items,
+ * not in a separate array.
+ *
+ * @author Alan Trick
+ * @package blocks
+ * @internal this extends block_list so we get is_empty() for free
+ */
+class block_tree extends block_list {
+    
+    /**
+     * @var int specifies the manner in which contents should be added to this
+     * block type. In this case <code>$this->content->items</code> is used with
+     * {@link tree_item}s.
+     */
+    public $content_type = BLOCK_TYPE_TREE;
+    
+    /**
+     * Make the formatted HTML ouput.
+     * 
+     * Also adds the required javascript call to the page output.
+     *
+     * @param moodle_core_renderer $output
+     * @return string HTML
+     */
+    protected function formatted_contents($output) {
+        // based of code in admin_tree
+        global $PAGE; // TODO change this when there is a proper way for blocks to get stuff into head.
+        static $eventattached;
+        if ($eventattached===null) {
+            $eventattached = true;
+        }
+        if (!$this->content) {
+            $this->content = new stdClass;
+            $this->content->items = array();
+        }
+        $this->get_content();
+        $content = $output->tree_block_contents($this->content->items,array('class'=>'block_tree'));
+        if (isset($this->id) && !is_numeric($this->id)) {
+            $content = $output->box($content, 'block_tree_box', $this->id);
+        }
+        return $content;
+    }
+}
\ No newline at end of file
index fe68002473765a87215fbcf1db4375565b6ff141..f35bfe2c02dc0492b50032504be731342965fa83 100644 (file)
@@ -166,20 +166,14 @@ if (!$managesharedfeeds) {
 }
 
 $urlparams = array('rssid' => $rssid);
-$manageparams = array();
 if ($courseid) {
     $urlparams['courseid'] = $courseid;
-    $manageparams[] = 'courseid=' . $courseid;
 }
 if ($returnurl) {
     $urlparams['returnurl'] = $returnurl;
-    $manageparams = 'returnurl=' . $returnurl;
-}
-if ($manageparams) {
-    $manageparams = '?' . implode('&', $manageparams);
-} else {
-    $manageparams = '';
 }
+$managefeeds = new moodle_url($CFG->wwwroot . '/blocks/rss_client/managefeeds.php', $urlparams);
+
 $PAGE->set_url('blocks/rss_client/editfeed.php', $urlparams);
 $PAGE->set_generaltype('form');
 
@@ -195,7 +189,7 @@ $mform = new feed_edit_form($PAGE->url, $isadding, $managesharedfeeds);
 $mform->set_data($rssrecord);
 
 if ($mform->is_cancelled()) {
-    redirect($CFG->wwwroot . '/blocks/rss_client/managefeeds.php' . $manageparams);
+    redirect($managefeeds);
 
 } else if ($data = $mform->get_data()) {
     $data->userid = $USER->id;
@@ -210,7 +204,7 @@ if ($mform->is_cancelled()) {
         $DB->update_record('block_rss_client', $data);
     }
 
-    redirect($CFG->wwwroot . '/blocks/rss_client/managefeeds.php' . $manageparams);
+    redirect($managefeeds);
 
 } else {
     if ($isadding) {
@@ -222,17 +216,13 @@ if ($mform->is_cancelled()) {
     $PAGE->set_title($strtitle);
     $PAGE->set_heading($strtitle);
 
-    $navlinks = array(
-        array('name' => get_string('administration'), 'link' => "$CFG->wwwroot/$CFG->admin/index.php", 'type' => 'misc'),
-        array('name' => get_string('managemodules'), 'link' => null, 'type' => 'misc'),
-        array('name' => get_string('blocks'), 'link' => null, 'type' => 'misc'),
-        array('name' => get_string('feedstitle', 'block_rss_client'), 'link' => "$CFG->wwwroot/$CFG->admin/settings.php?section=blocksettingrss_client", 'type' => 'misc'),
-        array('name' => get_string('managefeeds', 'block_rss_client'), 'link' => $CFG->wwwroot . '/blocks/rss_client/managefeeds.php' . $manageparams, 'type' => 'misc'),
-        array('name' => $strtitle, 'link' => null,  'type' => 'misc'),
-    );
-    $navigation = build_navigation($navlinks);
-
-    echo $OUTPUT->header($navigation);
+    $settingsurl = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=blocksettingrss_client');
+    $PAGE->navbar->add(get_string('blocks'));
+    $PAGE->navbar->add(get_string('feedstitle', 'block_rss_client'), null, null, navbar::TYPE_SETTING, $settingsurl);
+    $PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'));
+    $PAGE->navbar->add($strtitle);
+
+    echo $OUTPUT->header();
     echo $OUTPUT->heading($strtitle, 2);
 
     $mform->display();
index 98a5db96d78db28e4d1f7a33b7dd19f3949edebf..6f77309efbb36110bccaa46c5c669889bb4ca56b 100644 (file)
@@ -82,16 +82,12 @@ $PAGE->set_generaltype('form');
 $PAGE->set_title($strmanage);
 $PAGE->set_heading($strmanage);
 
-$navlinks = array(
-    array('name' => get_string('administration'), 'link' => "$CFG->wwwroot/$CFG->admin/index.php", 'type' => 'misc'),
-    array('name' => get_string('managemodules'), 'link' => null, 'type' => 'misc'),
-    array('name' => get_string('blocks'), 'link' => null, 'type' => 'misc'),
-    array('name' => get_string('feedstitle', 'block_rss_client'), 'link' => "$CFG->wwwroot/$CFG->admin/settings.php?section=blocksettingrss_client", 'type' => 'misc'),
-    array('name' => get_string('managefeeds', 'block_rss_client'), 'link' => null, 'type' => 'misc'),
-);
-$navigation = build_navigation($navlinks);
-
-echo $OUTPUT->header($navigation);
+$settingsurl = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=blocksettingrss_client');
+$managefeeds = new moodle_url($CFG->wwwroot . '/blocks/rss_client/managefeeds.php', $urlparams);
+$PAGE->navbar->add(get_string('blocks'));
+$PAGE->navbar->add(get_string('feedstitle', 'block_rss_client'), null, null, navbar::TYPE_SETTING, $settingsurl);
+$PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'), null, null, navbar::TYPE_SETTING, $managefeeds);
+echo $OUTPUT->header();
 
 $table = new flexible_table('rss-display-feeds');
 
index 51f161c0da331fc67ae39de2f08ca77bbf27dcda..cc1ff4588608fe63bc894e4a6d3b28b817b32f64 100644 (file)
@@ -48,19 +48,11 @@ if ($courseid) {
 }
 
 $urlparams = array('rssid' => $rssid);
-$manageparams = array();
 if ($courseid) {
     $urlparams['courseid'] = $courseid;
-    $manageparams[] = 'courseid=$courseid';
 }
 if ($returnurl) {
     $urlparams['returnurl'] = $returnurl;
-    $manageparams = 'returnurl=' . $returnurl;
-}
-if ($manageparams) {
-    $manageparams = '?' . implode('&', $manageparams);
-} else {
-    $manageparams = '';
 }
 $PAGE->set_url('blocks/rss_client/viewfeed.php', $urlparams);
 $PAGE->set_generaltype('popup');
@@ -79,18 +71,13 @@ $strviewfeed = get_string('viewfeed', 'block_rss_client');
 $PAGE->set_title($strviewfeed);
 $PAGE->set_heading($strviewfeed);
 
-$navlinks = array(
-    array('name' => get_string('administration'), 'link' => "$CFG->wwwroot/$CFG->admin/index.php", 'type' => 'misc'),
-    array('name' => get_string('managemodules'), 'link' => null, 'type' => 'misc'),
-    array('name' => get_string('blocks'), 'link' => null, 'type' => 'misc'),
-    array('name' => get_string('feedstitle', 'block_rss_client'), 'link' => "$CFG->wwwroot/$CFG->admin/settings.php?section=blocksettingrss_client", 'type' => 'misc'),
-    array('name' => get_string('managefeeds', 'block_rss_client'), 'link' => $CFG->wwwroot . '/blocks/rss_client/managefeeds.php' . $manageparams, 'type' => 'misc'),
-    array('name' => $strviewfeed, 'link' => null,  'type' => 'misc'),
-);
-$navigation = build_navigation($navlinks);
-
-echo $OUTPUT->header($navigation);
-
+$settingsurl = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=blocksettingrss_client');
+$managefeeds = new moodle_url($CFG->wwwroot . '/blocks/rss_client/managefeeds.php', $urlparams);
+$PAGE->navbar->add(get_string('blocks'));
+$PAGE->navbar->add(get_string('feedstitle', 'block_rss_client'), null, null, navbar::TYPE_SETTING, $settingsurl);
+$PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'));
+$PAGE->navbar->add($strviewfeed);
+echo $OUTPUT->header();
 
 if (!empty($rssrecord->preferredtitle)) {
     $feedtitle = $rssrecord->preferredtitle;
diff --git a/blocks/settings_navigation_tree/block_settings_navigation_tree.php b/blocks/settings_navigation_tree/block_settings_navigation_tree.php
new file mode 100644 (file)
index 0000000..e574be7
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * The settings navigation tree block class
+ *
+ * Used to produce the settings navigation block new to Moodle 2.0
+ *
+ * @package blocks
+ * @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 {
+
+    /** @var string */
+    public static $navcount;
+    public $blockname = null;
+    protected $contentgenerated = false;
+    public $id = null;
+
+    /**
+     * Set the initial properties for the block
+     */
+    function init() {
+        $this->blockname = get_class($this);
+        $this->title = get_string('blockname', $this->blockname);
+        $this->version = 2009082800;
+    }
+
+    /**
+     * All multiple instances of this block
+     * @return bool Returns true
+     */
+    function instance_allow_multiple() {
+        return false;
+    }
+
+    /**
+     * Set the applicable formats for this block to all
+     * @return array
+     */
+    function applicable_formats() {
+        return array('all' => true);
+    }
+
+    /**
+     * Allow the user to configure a block instance
+     * @return bool Returns true
+     */
+    function instance_allow_config() {
+        return true;
+    }
+
+    /**
+     * Gets the content for this block by grabbing it from $this->page
+     */
+    function get_content() {
+        global $CFG, $OUTPUT;
+        // First check if we have already generated, don't waste cycles
+        if ($this->contentgenerated === true) {
+            return true;
+        }
+        $this->page->requires->js('lib/javascript-navigation.js');
+        block_settings_navigation_tree::$navcount++;
+
+        $tooglesidetabdisplay = get_string('tooglesidetabdisplay', $this->blockname);
+        $toogleblockdisplay = get_string('toogleblockdisplay', $this->blockname);
+        $args = array('instance'=>$this->instance->id);
+        $args['tooglesidetabdisplay'] = $tooglesidetabdisplay;
+        $args['toogleblockdisplay'] = $toogleblockdisplay;
+        // Give JS some information we will use within the JS tree object
+        $this->page->requires->data_for_js('settingsnav'.block_settings_navigation_tree::$navcount, $args);
+
+
+        $this->id = 'settingsnav'.block_settings_navigation_tree::$navcount;
+        $this->page->requires->js_function_call('setup_new_navtree', array($this->id))->on_dom_ready();
+        // 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 (has_capability('moodle/site:config',get_context_instance(CONTEXT_SYSTEM)) ) {
+                $this->content->footer =
+                        '<div class="adminsearchform">'.
+                        '<form action="'.$CFG->wwwroot.'/'.$CFG->admin.'/search.php" method="get"><div>'.
+                        '<label for="query" class="accesshide">'.get_string('searchinsettings', 'admin').'</label>'.
+                        '<input type="text" name="query" id="query" size="8" value="'.s(optional_param('query', '')).'" />'.
+                        '<input type="submit" value="'.get_string('search').'" /></div>'.
+                        '</form></div>';
+            } else {
+                $this->content->footer = '';
+            }
+
+            $url = $this->page->url;
+            $url->param('regenerate','navigation');
+            $reloadstr = get_string('reload');
+            $this->content->footer .= '<a href="'.$url->out().'" class="customcommand"><img src="'.$OUTPUT->old_icon_url('t/reload').'" alt="'.$reloadstr.'" title="'.$reloadstr.'" /></a>';
+            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 (get_user_preferences('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, 0)) {
+                    $icon = $OUTPUT->old_icon_url('t/movetoblock');
+                    $string = $toogleblockdisplay;
+                } else {
+                    $icon = $OUTPUT->old_icon_url('t/movetosidetab');
+                    $string = $tooglesidetabdisplay;
+                }
+                $this->content->footer .= '<a class="moveto customcommand requiresjs"><img src="'.$icon.'" alt="'.$string.'" title="'.$string.'"></a>';
+            }
+        }
+
+        $this->contentgenerated = true;
+        return true;
+    }
+
+    function html_attributes() {
+        $attributes = parent::html_attributes();
+        if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
+            $attributes['class'] .= ' sideblock_js_expansion';
+        }
+        if (get_user_preferences('nav_in_tab_panel_settingsnav'.block_settings_navigation_tree::$navcount, 0)) {
+            $attributes['class'] .= ' sideblock_js_sidebarpopout';
+        }
+        return $attributes;
+    }
+}
\ No newline at end of file
diff --git a/blocks/settings_navigation_tree/edit_form.php b/blocks/settings_navigation_tree/edit_form.php
new file mode 100644 (file)
index 0000000..78777a3
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Form for editing settings navigation instances.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Form for setting navigation instances.
+ *
+ * @package blocks
+ * @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 {
+    protected function specific_definition($mform) {
+        $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
+
+        $yesnooptions = array('yes'=>get_string('yes'), 'no'=>get_string('no'));
+
+        $mform->addElement('select', 'config_enablehoverexpansion', get_string('enablehoverexpansion', $this->block->blockname), $yesnooptions);
+        if (empty($this->block->config->enablehoverexpansion) || $this->block->config->enablehoverexpansion=='no') {
+            $mform->getElement('config_enablehoverexpansion')->setSelected('no');
+        } else {
+            $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');
+        } else {
+            $mform->getElement('config_enablesidebarpopout')->setSelected('yes');
+        }
+    }
+}
\ No newline at end of file
index 79fb74b56ce36aa3807deafb42d80390f1d94097..a738f6a1b201ea319a5d836940d3b7a66936088e 100644 (file)
     $strcategory = get_string('category');
     $strcourses = get_string('courses');
 
-    $navlinks = array();
-    $navlinks[] = array('name' => $strcategories, 'link' => 'index.php', 'type' => 'misc');
-    $navlinks[] = array('name' => format_string($category->name), 'link' => null, 'type' => 'misc');
-    $navigation = build_navigation($navlinks);
-
     if ($editingon && can_edit_in_category()) {
         // Integrate into the admin tree only if the user can edit categories at the top level,
         // otherwise the admin block does not appear to this user, and you get an error.
         admin_externalpage_print_header();
     } else {
         $navbaritem = print_course_search('', true, 'navbar');
-        print_header("$site->shortname: $category->name", "$site->fullname: $strcourses", $navigation, '', '', true, $navbaritem);
+        print_header("$site->shortname: $category->name", "$site->fullname: $strcourses", array(), '', '', true, $navbaritem);
     }
 
 /// Print link to roles
index 289ef677f62051b1b51d03955df8a674a7a9f2a4..ca02d73b12582a927f683598539e7ffd2d08740d 100755 (executable)
@@ -6,7 +6,6 @@
 // social format.
 //
 // The default blocks layout for this course format:
-    $format['defaultblocks'] = 'participants,social_activities,admin,course_list:'.
-                               'news_items,recent_activity,calendar_upcoming';
+    $format['defaultblocks'] = ':news_items,recent_activity,calendar_upcoming';
 
 ?>
diff --git a/course/format/scorm/lib.php b/course/format/scorm/lib.php
new file mode 100644 (file)
index 0000000..01605e5
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains general functions for the course format SCORM
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * The string that is used to describe a section of the course
+ * e.g. Topic, Week...
+ *
+ * @return string
+ */
+function callback_scorm_definition() {
+    return get_string('scorm');
+}
+
+/**
+ * The GET argument variable that is used to identify the section being
+ * viewed by the user (if there is one)
+ *
+ * @return string
+ */
+function callback_scorm_request_keyscorm() {
+    return 'scorm';
+}
+
+/**
+ * Toogle display of course contents (sections, activities)
+ *
+ * @return bool
+ */
+function callback_scorm_display_content() {
+    return false;
+}
+
+?>
index 49720e24c9519796bf554a2f015c593885246642..d2d8a9ca76a5dba701e95a8a3eb1f8100c41738f 100755 (executable)
@@ -6,7 +6,6 @@
 // social format.
 //
 // The default blocks layout for this course format:
-    $format['defaultblocks'] = 'participants,search_forums,calendar_upcoming,'.
-                               'social_activities,recent_activity,admin,course_list';
+    $format['defaultblocks'] = ':search_forums,calendar_upcoming,social_activities,recent_activity,admin,course_list';
 
 ?>
diff --git a/course/format/social/lib.php b/course/format/social/lib.php
new file mode 100644 (file)
index 0000000..5b1bd1e
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains general functions for the course format Social
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Used to display the course structure for a course where format=social
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks.
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @param stdClass $modinfo The mod info object for the current course
+ * @return bool Returns true
+ */
+function load_course_format_social(&$navigation, $keys, $course) {
+    $navigation->add_course_section_generic($keys, $course, get_string('social'), 'social');
+}
+
+/**
+ * Used to display the course structure for a course where format=social
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks and the navigation was requested via AJAX
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @param stdClass $modinfo The mod info object for the current course
+ * @return bool Returns true
+ */
+function limited_load_section_social(&$navigation, $keys, $course, $section) {
+    $navigation->limited_load_section_generic($keys, $course, $section, get_string('social'), 'social');
+}
+
+/**
+ * The string that is used to describe a section of the course
+ * e.g. Topic, Week...
+ *
+ * @return string
+ */
+function callback_social_definition() {
+    return get_string('social');
+}
+
+/**
+ * The GET argument variable that is used to identify the section being
+ * viewed by the user (if there is one)
+ *
+ * @return string
+ */
+function callback_social_request_key() {
+    return 'social';
+}
+
+/**
+ * Toogle display of course contents (sections, activities)
+ *
+ * @return bool
+ */
+function callback_social_display_content() {
+    return false;
+}
+
+?>
index 2d3eaca70736b7da7cd5494965a9552c2a0091cd..cf58f2db927604976e1ef95cf438d945663f0456 100755 (executable)
@@ -6,8 +6,6 @@
 // format.
 //
 // The default blocks layout for this course format:
-    $format['defaultblocks'] = 'participants,activity_modules,search_forums,'.
-                               'admin,course_list:news_items,calendar_upcoming,'.
-                               'recent_activity';
+    $format['defaultblocks'] = ':search_forums,news_items,calendar_upcoming,recent_activity';
 //
 ?>
\ No newline at end of file
diff --git a/course/format/topics/lib.php b/course/format/topics/lib.php
new file mode 100644 (file)
index 0000000..c91a586
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains general functions for the course format Topic
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Used to display the course structure for a course where format=topic
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks.
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @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');
+}
+
+/**
+ * Used to display the course structure for a course where format=topic
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks and the navigation was requested via AJAX
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @param stdClass $modinfo The mod info object for the current course
+ * @return bool Returns true
+ */
+function callback_topics_load_limited_section(&$navigation, $keys, $course, $section) {
+    $navigation->limited_load_section_generic($keys, $course, $section, get_string('topic'), 'topic');
+}
+
+/**
+ * The string that is used to describe a section of the course
+ * e.g. Topic, Week...
+ *
+ * @return string
+ */
+function callback_topics_definition() {
+    return get_string('topic');
+}
+
+/**
+ * The GET argument variable that is used to identify the section being
+ * viewed by the user (if there is one)
+ *
+ * @return string
+ */
+function callback_topics_request_key() {
+    return 'topic';
+}
+?>
index 2d3eaca70736b7da7cd5494965a9552c2a0091cd..cf58f2db927604976e1ef95cf438d945663f0456 100755 (executable)
@@ -6,8 +6,6 @@
 // format.
 //
 // The default blocks layout for this course format:
-    $format['defaultblocks'] = 'participants,activity_modules,search_forums,'.
-                               'admin,course_list:news_items,calendar_upcoming,'.
-                               'recent_activity';
+    $format['defaultblocks'] = ':search_forums,news_items,calendar_upcoming,recent_activity';
 //
 ?>
\ No newline at end of file
diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php
new file mode 100644 (file)
index 0000000..0f40233
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains general functions for the course format Week
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Used to display the course structure for a course where format=weeks
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks.
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @param stdClass $modinfo The mod info object for the current course
+ * @return bool Returns true
+ */
+function callback_weeks_load_content(&$navigation, $keys, $course) {
+    $navigation->add_course_section_generic($keys, $course, get_string('week'), 'week');
+}
+
+/**
+ * Used to display the course structure for a course where format=weeks
+ *
+ * This is called automatically by {@link load_course()} if the current course
+ * format = weeks and the navigation was requested via AJAX
+ *
+ * @param array $path An array of keys to the course node in the navigation
+ * @param stdClass $modinfo The mod info object for the current course
+ * @return bool Returns true
+ */
+function callback_weeks_load_limited_section(&$navigation, $keys, $course, $section) {
+    $navigation->limited_load_section_generic($keys, $course, $section, get_string('week'), 'week');
+}
+
+/**
+ * The string that is used to describe a section of the course
+ * e.g. Topic, Week...
+ *
+ * @return string
+ */
+function callback_weeks_definition() {
+    return get_string('week');
+}
+
+/**
+ * The GET argument variable that is used to identify the section being
+ * viewed by the user (if there is one)
+ *
+ * @return string
+ */
+function callback_weeks_request_key() {
+    return 'week';
+}
+?>
index e9f82b0d9e86868c76a86d6c915ae8062a3b5a86..0cc7ddb1eb9e04ccd6aa86e6b10b8068ca650f80 100644 (file)
     $navlinks = array();
     $navlinks[] = array('name' => $strcourses, 'link' => 'index.php', 'type' => 'misc');
     $navlinks[] = array('name' => $strsearch, 'link' => 'search.php', 'type' => 'misc');
-    $navlinks[] = array('name' => "'".s($search)."'", 'link' => null, 'type' => 'misc');
+    if (!empty($search)) {
+        $navlinks[] = array('name' => "'".s($search)."'", 'link' => null, 'type' => 'misc');
+    }
     $navigation = build_navigation($navlinks);
 
     print_header("$site->fullname : $strsearchresults", $site->fullname, $navigation, "", "", "", $searchform);
diff --git a/course/switchrole.php b/course/switchrole.php
new file mode 100644 (file)
index 0000000..5ed7b53
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The purpose of this file is to allow the user to switch roles and be redirected
+ * back to the page that they were on.
+ *
+ * This functionality is also supported in {@link /course/view.php} in order to comply
+ * with backwards compatibility
+ * The reason that we created this file was so that user didn't get redirected back
+ * to the course view page only to be redirected again.
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @subpackage navigation
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+$id          = required_param('id', PARAM_INT);
+$switchrole  = optional_param('switchrole',-1, PARAM_INT);
+$returnurlkey = optional_param('returnurl', false, PARAM_INT);
+
+if (!confirm_sesskey()) {
+    print_error('confirmsesskeybad', 'error');
+}
+
+if (! ($course = $DB->get_record('course', array('id'=>$id)))) {
+    print_error('invalidcourseid', 'error');
+}
+
+if (!$context = get_context_instance(CONTEXT_COURSE, $course->id)) {
+    print_error('nocontext');
+}
+
+// Remove any switched roles before checking login
+if ($switchrole == 0) {
+    role_switch($switchrole, $context);
+}
+require_login($course);
+
+// Switchrole - sanity check in cost-order...
+if ($switchrole > 0 && has_capability('moodle/role:switchroles', $context)) {
+    // is this role assignable in this context?
+    // inquiring minds want to know...
+    $aroles = get_switchable_roles($context);
+    if (is_array($aroles) && isset($aroles[$switchrole])) {
+        role_switch($switchrole, $context);
+        // Double check that this role is allowed here
+        require_login($course);
+    }
+}
+
+$returnurl = false;
+if ($returnurlkey && !empty($SESSION->returnurl) && strpos($SESSION->returnurl, 'moodle_url')!==false) {
+    $returnurl = @unserialize($SESSION->returnurl);
+    if (!($returnurl instanceof moodle_url)) {
+        $returnurl = false;
+    }
+    unset($SESSION->returnurl);
+}
+
+if ($returnurl===false) {
+    $returnurl = new moodle_url('/course/view.php', array('id'=>$course->id));
+}
+redirect($returnurl);
+
+?>
index 7db0b94fbaeff4ac3e88bf626b8a7f8e493a4c05..113e3d911d7119eadb2098ddfcfbc1561756d7e3 100644 (file)
--- a/index.php
+++ b/index.php
@@ -87,7 +87,7 @@
     $editing = $PAGE->user_is_editing();
     $PAGE->set_title($SITE->fullname);
     $PAGE->set_heading($SITE->fullname);
-    echo $OUTPUT->header('', user_login_string($SITE) . $langmenu);
+    echo $OUTPUT->header(user_login_string($SITE) . $langmenu);
 
 /// Print Section
     if ($SITE->numsections > 0) {
index 73601f1f31e6e8df32c7ea3c01ff2d03b58436bf..33cc7bdf000900620b8db640b7993f20eeba3634 100644 (file)
@@ -614,6 +614,9 @@ $string['mymoodle'] = 'My Moodle';
 $string['mymoodleredirect'] = 'Force users to use My Moodle';
 $string['mysql416bypassed'] = 'However, if your site is using iso-8859-1 (latin) languages ONLY, you may continue using your currently installed MySQL 4.1.12 (or higher).';
 $string['mysql416required'] = 'MySQL 4.1.16 is the minimum version required for Moodle 1.6 in order to guarantee that all data can be converted to UTF-8 in the future.';
+$string['navigationupgrade'] = 'This upgrade introduces two new navigation blocks that will replace these blocks: Administration, Courses, Activities and Participants.  If you had set any special permissions on those blocks you should check to make sure everything
+is behaving as you want it.<br /><br />
+You should also "Shift-Refresh" your browser to load the new styles, otherwise the new blocks will not work correctly.';
 $string['nobookmarksforuser'] = 'You do not have any bookmarks.';
 $string['nochanges'] = 'No changes';
 $string['nodatabase'] = 'No database';
diff --git a/lang/en_utf8/block_global_navigation_tree.php b/lang/en_utf8/block_global_navigation_tree.php
new file mode 100644 (file)
index 0000000..25caf1a
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains language strings used in the global navigation block
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['blockname'] = 'Navigation';
+$string['expansionlimit'] = 'Generate navigation for the following';
+$string['everything'] = 'Everything';
+$string['courses'] = 'Categories and courses';
+$string['coursestructures'] = 'Categories, courses, and course structures';
+$string['courseactivities'] = 'Categories, courses, and course Activities';
+$string['showemptybranchesdesc'] = 'Show empty course section branches';
+$string['showmycoursesdesc'] = 'Show my courses in the navigation';
+$string['enablehoverexpansiondesc'] = 'Enable mouseover expansion of this block';
+$string['enablesidebarpopoutdesc'] = 'Allow the user to switch the block to a sidbar popout';
+$string['showmyhistorydesc'] = 'Show my history as a branch in the navigation';
+$string['showmyhistorytitle'] = 'My history';
+$string['toogleblockdisplay'] = 'Move to block position';
+$string['tooglesidetabdisplay'] = 'Move to side panel tab';
diff --git a/lang/en_utf8/block_settings_navigation_tree.php b/lang/en_utf8/block_settings_navigation_tree.php
new file mode 100644 (file)
index 0000000..27d6790
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains language strings used in the settings navigation block
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['blockname'] = 'Settings';
+$string['enablehoverexpansion'] = 'Enable mouseover expansion of this block';
+$string['enablesidebarpopout'] = 'Allow the user to switch the block to a sidbar popout';
+$string['toogleblockdisplay'] = 'Move to block position';
+$string['tooglesidetabdisplay'] = 'Move to side panel tab';
\ No newline at end of file
index 87848f4f77c7fa613128ad59026f56a3a3a78979..6076401316452cef851a059d77c001f9884a2db6 100644 (file)
@@ -126,6 +126,7 @@ $string['forcessubscribe'] = 'This forum forces everyone to be subscribed';
 $string['forcesubscribe'] = 'Force everyone to be subscribed';
 $string['forcesubscribeq'] = 'Force everyone to be subscribed?';
 $string['forum'] = 'Forum';
+$string['forumadministration'] = 'Forum Administration';
 $string['forum:addnews'] = 'Add news';
 $string['forumauthorhidden'] = 'Author (hidden)';
 $string['forumblockingalmosttoomanyposts'] = 'You are approaching the posting threshold. You have posted $a->numposts times in the last $a->blockperiod and the limit is $a->blockafter posts.';
@@ -350,6 +351,7 @@ $string['unsubscribeallempty'] = 'Sorry, you are not subscribed to any forums. I
 $string['unsubscribed'] = 'Unsubscribed';
 $string['unsubscribeshort'] = 'Unsubscribe';
 $string['usermarksread'] = 'Manual message read marking';
+$string['viewalldiscussions'] = 'View all discussions';
 $string['warnafter'] = 'Post threshold for warning';
 $string['yesforever'] = 'Yes, forever';
 $string['yesinitially'] = 'Yes, initially';
@@ -357,4 +359,4 @@ $string['youratedthis'] = 'You rated this';
 $string['yournewquestion'] = 'Your new question';
 $string['yournewtopic'] = 'Your new discussion topic';
 $string['yourreply'] = 'Your reply';
-?>
+?>
\ No newline at end of file
index e945d3ac5ee75541149757862a408c6d79333092..044a1196ad000c4cc33a23f32c378eebd0e38012 100644 (file)
@@ -275,6 +275,7 @@ $string['costdefault'] = 'Default cost';
 $string['counteditems'] = '$a->count $a->items';
 $string['country'] = 'Country';
 $string['course'] = 'Course';
+$string['courseadministration'] = 'Course Administration';
 $string['courseapprovedemail'] = 'Your requested course, $a->name, has been approved and you have been made a $a->teacher.  To access your new course, go to $a->url';
 $string['courseapprovedemail2'] = 'Your requested course, $a->name, has been approved.  To access your new course, go to $a->url';
 $string['courseapprovedfailed'] = 'Failed to save the course as approved!';
@@ -731,6 +732,7 @@ $string['frontpagedescriptionhelp'] = 'This description of the site will be disp
 $string['frontpageformat'] = 'Front page format';
 $string['frontpageformatloggedin'] = 'Front page format when logged in';
 $string['frontpagenews'] = 'News items';
+$string['frontpagesettings'] = 'Front Page Settings';
 $string['frontpagetopiconly'] = 'Topic section';
 $string['fulllistofcourses'] = 'All courses';
 $string['fullname'] = 'Full name';
@@ -1034,6 +1036,7 @@ $string['moreinformation'] = 'More information about this error';
 $string['moreprofileinfoneeded'] = 'Please tell us more about yourself';
 $string['mostrecently'] = 'most recently';
 $string['move'] = 'Move';
+$string['moveallsidetabstoblock'] = 'Move all tabs back to their block position';
 $string['movecategoryto'] = 'Move category to:';
 $string['movecategorycontentto'] = 'Move into';
 $string['movecontentstoanothercategory'] = 'Move contents to another category';
@@ -1355,6 +1358,7 @@ $string['restoretositeadding'] = 'Warning: You are about to restore to the site
 $string['restoretositedeleting'] = 'Warning: You are about to restore to the site front page, deleting data from it first!';
 $string['restricted'] = 'Restricted';
 $string['restrictmodules'] = 'Restrict activity modules?';
+$string['returntooriginaluser'] = 'Return to $a';
 $string['returningtosite'] = 'Returning to this web site?';
 $string['revert'] = 'Revert';
 $string['role'] = 'Role';
@@ -1657,6 +1661,8 @@ $string['used'] = 'Used';
 $string['usedinnplaces'] = 'Used in $a places';
 $string['usemessageform'] = 'or use the form below to send a message to the selected students';
 $string['user'] = 'User';
+$string['usercurrentsettings'] = 'My profile settings';
+$string['userviewingsettings'] = 'Profile for $a';
 $string['userconfirmed'] = 'Confirmed $a';
 $string['userdata'] = 'User Data';
 $string['userdeleted'] = 'This user account has been deleted';
@@ -1687,6 +1693,7 @@ $string['valuealreadyused'] = 'This value has already been used.';
 $string['version'] = 'Version';
 $string['view'] = 'View';
 $string['viewfileinpopup'] = 'View file in a popup window';
+$string['viewmyprofile'] = 'View my profile';
 $string['views'] = 'Views';
 $string['viewsolution'] = 'view solution';
 $string['virusfound'] = 'Attention administrator! Clam AV has found a virus in a file uploaded by $a->user for the course $a->course. Here is the output of clamscan:';
index 10c9efd9074de4db3c5cf9814aecc0eb90b5420e..769497e97981a19659f3919e473c381cc2fb19ee 100644 (file)
@@ -5082,13 +5082,7 @@ function admin_externalpage_print_header($focus='') {
         $buttons = $OUTPUT->button(html_form::make_button($PAGE->url->out(false), $options, $caption, 'get'));
     }
 
-    $navlinks = array();
-    foreach ($visiblepathtosection as $element) {
-        $navlinks[] = array('name' => $element, 'link' => null, 'type' => 'misc');
-    }
-    $navigation = build_navigation($navlinks);
-
-    print_header("$SITE->shortname: " . implode(": ",$visiblepathtosection), $SITE->fullname, $navigation, $focus, '', true, $buttons, '');
+    print_header("$SITE->shortname: " . implode(": ",$visiblepathtosection), $SITE->fullname, array(), $focus, '', true, $buttons, '');
 }
 
 /**
@@ -5551,6 +5545,7 @@ function print_plugin_tables() {
                                         'comments',
                                         'course_list',
                                         'course_summary',
+                                        'global_navigation_tree',
                                         'glossary_random',
                                         'html',
                                         'loancalc',
@@ -5567,6 +5562,7 @@ function print_plugin_tables() {
                                         'search',
                                         'search_forums',
                                         'section_links',
+                                        'settings_navigation_tree',
                                         'site_main_menu',
                                         'social_activities',
                                         'tag_flickr',
diff --git a/lib/ajax/getnavbranch.php b/lib/ajax/getnavbranch.php
new file mode 100644 (file)
index 0000000..8be709a
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file is used to deliver a branch from the navigation structure
+ * in XML format back to a page from an AJAX call
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/** Include config */
+require_once(dirname(__FILE__) . '/../../config.php');
+/** Include course lib for its functions */
+require_once($CFG->dirroot.'/course/lib.php');
+// Start buffer capture so that we can `remove` any errors
+ob_start();
+
+// Require elementid (this identifies which HTML element it is being expanded
+$elementid = optional_param('elementid', null, PARAM_INT);
+// Require id This is the key for whatever branch we want to get
+$branchid = optional_param('id', null, PARAM_INT);
+// This identifies the type of the branch we want to get
+$branchtype = optional_param('type', null, PARAM_INT);
+// This identifies the block instance requesting AJAX extension
+$instanceid = optional_param('instance', null, PARAM_INT);
+
+// Create a global nav object
+$navigation = new limited_global_navigation();
+// 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'));
+    if ($blockrecords!=false) {
+        // Instantiate a block_instance object so we can access congif
+        $block = block_instance('global_navigation_tree', $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;
+        }
+    }
+}
+
+// Create a navigation object to use, we can't guarantee PAGE will be complete
+$expandable = $navigation->initialise($branchtype, $branchid);
+
+if ($toggledisplay) {
+    // Toggle display of item types we dont' want to display
+    $navigation->toggle_type_display($navigation->expansionlimit);
+}
+// Find the actuall branch we are looking for
+$branch = $navigation->find_child($branchid, $branchtype);
+
+// Stop buffering errors at this point
+$html = ob_get_contents();
+ob_end_clean();
+
+// Check if the buffer contianed anything if it did ERROR!
+if (trim($html)!=='') {
+    die('Errors were encountered while producing the navigation branch'."\n\n\n".$html);
+}
+// Check that branch isn't empty... if it is ERROR!
+if (empty($branch) || $branch->nodetype !== navigation_node::NODETYPE_BRANCH) {
+    die('No further information available for this branch');
+}
+
+// Prepare an XML converter for the branch
+$converter = new navigation_xml();
+$converter->set_expandable($expandable);
+// Set XML headers
+header('Content-type: text/xml');
+echo '<?xml version="1.0" encoding="utf-8"?>';
+// Convert and output the branch as XML
+echo $converter->convert($branch);
\ No newline at end of file
index 0137ee928cdc2c9d05b92bc75c2e1f42b4b4ac2b..fea4bdab9399d42606d0f814e1a23955eddc7745 100644 (file)
@@ -35,9 +35,6 @@
 require_once(dirname(__FILE__) . '/../../config.php');
 
 // Check access.
-if (!isloggedin()) {;
-    print_error('mustbeloggedin');
-}
 if (!confirm_sesskey()) {
     print_error('invalidsesskey');
 }
index 064af2314f769d17416edc0d367022290d2680f8..c6a58d850a364a816fcd4adef3f3e1ae06640e19 100644 (file)
@@ -585,12 +585,12 @@ class block_manager {
      * @param string $pagetypepattern optional. Passed to @see add_block()
      * @param string $subpagepattern optional. Passed to @see add_block()
      */
-    public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL) {
+    public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL, $showinsubcontexts=false, $weight=0) {
         $this->add_regions(array_keys($blocks));
         foreach ($blocks as $region => $regionblocks) {
             $weight = 0;
             foreach ($regionblocks as $blockname) {
-                $this->add_block($blockname, $region, $weight, false, $pagetypepattern, $subpagepattern);
+                $this->add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern, $subpagepattern);
                 $weight += 1;
             }
         }
@@ -1626,10 +1626,16 @@ function blocks_parse_default_blocks_list($blocksstr) {
     $blocks = array();
     $bits = explode(':', $blocksstr);
     if (!empty($bits)) {
-        $blocks[BLOCK_POS_LEFT] = explode(',', array_shift($bits));
+        $leftbits = trim(array_shift($bits));
+        if ($leftbits != '') {
+            $blocks[BLOCK_POS_LEFT] = explode(',', $leftbits);
+        }
     }
     if (!empty($bits)) {
-        $blocks[BLOCK_POS_RIGHT] = explode(',', array_shift($bits));
+        $rightbits =trim(array_shift($bits));
+        if ($rightbits != '') {
+            $blocks[BLOCK_POS_RIGHT] = explode(',', $rightbits);
+        }
     }
     return $blocks;
 }
@@ -1644,7 +1650,7 @@ function blocks_get_default_site_course_blocks() {
         return blocks_parse_default_blocks_list($CFG->defaultblocks_site);
     } else {
         return array(
-            BLOCK_POS_LEFT => array('site_main_menu', 'admin_tree'),
+            BLOCK_POS_LEFT => array('site_main_menu'),
             BLOCK_POS_RIGHT => array('course_summary', 'calendar_month')
         );
     }
@@ -1682,8 +1688,8 @@ function blocks_add_default_course_blocks($course) {
 
             } else {
                 $blocknames = array(
-                    BLOCK_POS_LEFT => array('participants', 'activity_modules', 'search_forums', 'admin', 'course_list'),
-                    BLOCK_POS_RIGHT => array('news_items', 'calendar_upcoming', 'recent_activity')
+                    BLOCK_POS_LEFT => array(),
+                    BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
                 );
             }
         }
@@ -1706,5 +1712,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('admin_tree', 'admin_bookmarks')), 'admin-*');
+    $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('admin_bookmarks')), 'admin-*', null, null, 2);
 }
index 24d91932f5fd0e843ad8a506a1e99f819529c0f8..be20f161e1328838bb34f0d657f3e5eb087af502 100644 (file)
@@ -2370,6 +2370,110 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         upgrade_main_savepoint($result, 2009072400);
     }
 
+    /**
+     * This upgrade is to set up the new navigation blocks that have been developed
+     * as part of Moodle 2.0
+     * Now I [Sam Hemelryk] hit a conundrum while exploring how to go about this
+     * as not only do we want to install the new blocks but we also want to set up
+     * default instances of them, and at the same time remove instances of the blocks
+     * that were/will-be outmoded by the two new navigation blocks.
+     * After talking it through with Tim Hunt {@link http://moodle.org/mod/cvsadmin/view.php?conversationid=3112}
+     * we decided that the best way to go about this was to put the bulk of the
+     * upgrade operation into core upgrade `here` but to let the plugins block
+     * still install the blocks.
+     * This leaves one hairy end in that we will create block_instances within the
+     * DB before the blocks themselves are created within the DB
+     */
+    if ($result && $oldversion < 2009082800) {
+
+        echo $OUTPUT->notification(get_string('navigationupgrade', 'admin'));
+
+        // Get the system context so we can set the block instances to it
+        $syscontext = get_context_instance(CONTEXT_SYSTEM);
+
+        // An array to contain the new block instances we will create
+        $newblockinstances = array('globalnavigation'=>new stdClass,'settingsnavigation'=>new stdClass);
+        // The new global navigation block instance as a stdClass
+        $newblockinstances['globalnavigation']->blockname = 'global_navigation_tree';
+        $newblockinstances['globalnavigation']->parentcontextid = $syscontext->id; // System context
+        $newblockinstances['globalnavigation']->showinsubcontexts = true; // Show absolutly everywhere
+        $newblockinstances['globalnavigation']->pagetypepattern = '*'; // Thats right everywhere
+        $newblockinstances['globalnavigation']->subpagetypepattern = null;
+        $newblockinstances['globalnavigation']->defaultregion = BLOCK_POS_LEFT;
+        $newblockinstances['globalnavigation']->defaultweight = -10; // Try make this first
+        $newblockinstances['globalnavigation']->configdata = '';
+        // The new settings navigation block instance as a stdClass
+        $newblockinstances['settingsnavigation']->blockname = 'settings_navigation_tree';
+        $newblockinstances['settingsnavigation']->parentcontextid = $syscontext->id;
+        $newblockinstances['settingsnavigation']->showinsubcontexts = true;
+        $newblockinstances['settingsnavigation']->pagetypepattern = '*';
+        $newblockinstances['settingsnavigation']->subpagetypepattern = null;
+        $newblockinstances['settingsnavigation']->defaultregion = BLOCK_POS_LEFT;
+        $newblockinstances['settingsnavigation']->defaultweight = -9; // Try make this second
+        $newblockinstances['settingsnavigation']->configdata = '';
+
+        // Blocks that are outmoded and for whom the bells will toll... by which I
+        // mean we will delete all instances of
+        $outmodedblocks = array('participants','admin_tree','activity_modules','admin','course_list');
+        $outmodedblocksstring = '\''.join('\',\'',$outmodedblocks).'\'';
+        unset($outmodedblocks);
+        // Retrieve the block instance id's and parent contexts, so we can join them an GREATLY
+        // cut down the number of delete queries we will need to run
+        $allblockinstances = $DB->get_recordset_select('block_instances', 'blockname IN ('.$outmodedblocksstring.')', array(), '', 'id, parentcontextid');
+        
+        $contextids = array();
+        $instanceids = array();
+        // Iterate through all block instances
+        foreach ($allblockinstances as $blockinstance) {
+            if (!in_array($blockinstance->parentcontextid, $contextids)) {
+                $contextids[] = $blockinstance->parentcontextid;
+
+                // If we have over 1000 contexts clean them up and reset the array
+                // this ensures we don't hit any nasty memory limits or such
+                if (count($contextids) > 1000) {
+                    $result = $result && upgrade_cleanup_unwanted_block_contexts($contextids);
+                    $contextids = array();
+                }
+            }
+            if (!in_array($blockinstance->id, $instanceids)) {
+                $instanceids[] = $blockinstance->id;
+                // If we have more than 1000 block instances now remove all block positions
+                // and empty the array
+                if (count($contextids) > 1000) {
+                    $instanceidstring = join(',',$instanceids);
+                    $result = $result && $DB->delete_records_select('block_positions', 'blockinstanceid IN ('.$instanceidstring.')');
+                    $instanceids = array();
+                }
+            }
+        }
+
+        $result = $result && upgrade_cleanup_unwanted_block_contexts($contextids);
+
+        $instanceidstring = join(',',$instanceids);
+        $outcome1 = $result && $DB->delete_records_select('block_positions', 'blockinstanceid IN ('.$instanceidstring.')');
+        
+        unset($allblockinstances);
+        unset($contextids);
+        unset($instanceids);
+        unset($instanceidstring);
+        
+        // Now remove the actual block instance
+        $result = $result && $DB->delete_records_select('block_instances', 'blockname IN ('.$outmodedblocksstring.')');
+        unset($outmodedblocksstring);
+
+        // Insert the new block instances. Remember they have not been installed yet
+        // however this should not be a problem
+        foreach ($newblockinstances as $blockinstance) {
+            $blockinstance->id= $DB->insert_record('block_instances', $blockinstance);
+            // Ensure the block context is created.
+            get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
+        }
+        unset($newblockinstances);
+        
+        upgrade_main_savepoint($result, 2009082800);
+        // The end of the navigation upgrade
+    }
+
     return $result;
 }
 
index 81ad5a6887a3fba318c5110b3c523e7111ee1648..bf92018c52981a73b02a66b03b8037be39e89001 100644 (file)
@@ -331,4 +331,53 @@ function upgrade_fix_incorrect_mnethostids() {
     }
 }
 
+/**
+ * This function is used as part of the great navigation upgrade of 20090828
+ * It is used to clean up contexts that are unique to a blocks that are about
+ * to be removed.
+ *
+ *
+ * Look at {@link blocklib.php::blocks_delete_instance()} the function from
+ * which I based this code. It is important to mention one very important fact
+ * before doing this I checked that the blocks did not override the
+ * {@link block_base::instance_delete()} method. Should this function ever
+ * be repeated check this again
+ * 
+ * @link lib/db/upgrade.php
+ *
+ * @since navigation upgrade 20090828
+ * @param array $contextidarray An array of block instance context ids
+ * @return bool
+ */
+function upgrade_cleanup_unwanted_block_contexts($contextidarray) {
+    global $DB;
+
+    if (!is_array($contextidarray) || count($contextidarray)===0) {
+        // Ummmm no instances?
+        return true;
+    }
+
+    $contextidstring = join(',', $contextidarray);
+
+    $blockcontexts = $DB->get_recordset_select('context', 'contextlevel = '.CONTEXT_BLOCK.' AND id IN ('.$contextidstring.')', array(), '', 'id, contextlevel');
+    $blockcontextids = array();
+    foreach ($blockcontexts as $blockcontext) {
+        $blockcontextids[] = $blockcontext->id;
+    }
+
+    if (count($blockcontextids)===0) {
+        // None of the instances have unique contexts
+        return true;
+    }
+
+    $blockcontextidsstring = join(',', $blockcontextids);
+
+    $outcome1 = $DB->delete_records_select('role_assignments', 'contextid IN ('.$blockcontextidsstring.')');
+    $outcome2 = $DB->delete_records_select('role_capabilities', 'contextid IN ('.$blockcontextidsstring.')');
+    $outcome3 = $DB->delete_records_select('role_names', 'contextid IN ('.$blockcontextidsstring.')');
+    $outcome4 = $DB->delete_records_select('context', 'id IN ('.$blockcontextidsstring.')');
+
+    return ($outcome1 && $outcome2 && $outcome4 && $outcome4);
+}
+
 ?>
index 9c62a02d3232c26b4b3da3b7f4bed878377f7316..401806092b1dac99b1aa9a23622a18dc7c78744e 100644 (file)
@@ -2133,15 +2133,6 @@ function print_header($title='', $heading='', $navigation='', $focus='',
     }
     $PAGE->set_button($button);
 
-    if ($navigation == 'home') {
-        $navigation = '';
-    }
-    if (gettype($navigation) == 'string' && strlen($navigation) != 0 && $navigation != 'home') {
-        debugging("print_header() was sent a string as 3rd ($navigation) parameter. "
-                . "This is deprecated in favour of an array built by build_navigation(). Please upgrade your code.", DEBUG_DEVELOPER);
-    }
-
-    // TODO $navigation
     // TODO $menu
 
     if ($meta) {
@@ -2155,7 +2146,7 @@ function print_header($title='', $heading='', $navigation='', $focus='',
         throw new coding_exception('The $bodytags parameter to print_header is no longer supported.');
     }
 
-    $output = $OUTPUT->header($navigation, $menu);
+    $output = $OUTPUT->header($menu);
 
     if ($return) {
         return $output;
@@ -3564,3 +3555,118 @@ function update_course_icon($courseid) {
     return $OUTPUT->edit_button(new moodle_url($CFG->wwwroot.'/course/view.php', array('id' => $courseid)));
 }
 
+/**
+ * Prints breadcrumb trail of links, called in theme/-/header.html
+ *
+ * This function has now been deprecated please use output's navbar method instead
+ * as shown below
+ *
+ * <code php>
+ * echo $OUTPUT->navbar();
+ * </code>
+ *
+ * @deprecated since 2.0
+ * @param mixed $navigation deprecated
+ * @param string $separator OBSOLETE, and now deprecated
+ * @param boolean $return False to echo the breadcrumb string (default), true to return it.
+ * @return string|void String or null, depending on $return.
+ */
+function print_navigation ($navigation, $separator=0, $return=false) {
+    global $OUTPUT,$PAGE;
+
+    # debugging('print_navigation has been deprecated please update your theme to use $OUTPUT->navbar() instead', DEBUG_DEVELOPER);
+
+    $output = $OUTPUT->navbar();
+
+    if ($return) {
+        return $output;
+    } else {
+        echo $output;
+    }
+}
+
+/**
+ * This function will build the navigation string to be used by print_header
+ * and others.
+ *
+ * It automatically generates the site and course level (if appropriate) links.
+ *
+ * If you pass in a $cm object, the method will also generate the activity (e.g. 'Forums')
+ * and activityinstances (e.g. 'General Developer Forum') navigation levels.
+ *
+ * If you want to add any further navigation links after the ones this function generates,
+ * the pass an array of extra link arrays like this:
+ * array(
+ *     array('name' => $linktext1, 'link' => $url1, 'type' => $linktype1),
+ *     array('name' => $linktext2, 'link' => $url2, 'type' => $linktype2)
+ * )
+ * The normal case is to just add one further link, for example 'Editing forum' after
+ * 'General Developer Forum', with no link.
+ * To do that, you need to pass
+ * array(array('name' => $linktext, 'link' => '', 'type' => 'title'))
+ * However, becuase this is a very common case, you can use a shortcut syntax, and just
+ * pass the string 'Editing forum', instead of an array as $extranavlinks.
+ *
+ * At the moment, the link types only have limited significance. Type 'activity' is
+ * recognised in order to implement the $CFG->hideactivitytypenavlink feature. Types
+ * that are known to appear are 'home', 'course', 'activity', 'activityinstance' and 'title'.
+ * This really needs to be documented better. In the mean time, try to be consistent, it will
+ * enable people to customise the navigation more in future.
+ *
+ * When passing a $cm object, the fields used are $cm->modname, $cm->name and $cm->course.
+ * If you get the $cm object using the function get_coursemodule_from_instance or
+ * get_coursemodule_from_id (as recommended) then this will be done for you automatically.
+ * If you don't have $cm->modname or $cm->name, this fuction will attempt to find them using
+ * the $cm->module and $cm->instance fields, but this takes extra database queries, so a
+ * warning is printed in developer debug mode.
+ *
+ * @deprecated since 2.0
+ * @param mixed $extranavlinks - Normally an array of arrays, keys: name, link, type. If you
+ *      only want one extra item with no link, you can pass a string instead. If you don't want
+ *      any extra links, pass an empty string.
+ * @param mixed $cm deprecated
+ * @return array Navigation array
+ */
+function build_navigation($extranavlinks, $cm = null) {
+    global $CFG, $COURSE, $DB, $SITE, $PAGE;
+
+    if (is_array($extranavlinks) && count($extranavlinks)>0) {
+        # debugging('build_navigation() has been deprecated, please replace with $PAGE->navbar methods', DEBUG_DEVELOPER);
+        foreach ($extranavlinks as $nav) {
+            if (array_key_exists('name', $nav)) {
+                $link = (array_key_exists('link', $nav))?$nav['link']:null;
+                $PAGE->navbar->add($nav['name'],null, null, navbar::TYPE_CUSTOM, $link);
+            }
+        }
+    }
+    
+    return(array('newnav' => true, 'navlinks' => array()));
+}
+
+/**
+ * Returns a small popup menu of course activity modules
+ *
+ * Given a course and a (current) coursemodule
+ * his function returns a small popup menu with all the
+ * course activity modules in it, as a navigation menu
+ * The data is taken from the serialised array stored in
+ * the course record
+ *
+ * @global object
+ * @global object
+ * @global object
+ * @global object
+ * @uses CONTEXT_COURSE
+ * @param object $course A {@link $COURSE} object.
+ * @param object $cm A {@link $COURSE} object.
+ * @param string $targetwindow The target window attribute to us
+ * @return string
+ */
+function navmenu($course, $cm=NULL, $targetwindow='self') {
+    global $CFG, $THEME, $USER, $DB, $OUTPUT;
+
+    // This function has been deprecated with the creation of the global nav in
+    // moodle 2.0
+
+    return '';
+}
\ No newline at end of file
diff --git a/lib/javascript-navigation.js b/lib/javascript-navigation.js
new file mode 100644 (file)
index 0000000..d41f6e2
--- /dev/null
@@ -0,0 +1,836 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package javascript
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Some very important general namespaces to act as containers for the general
+ * objects required to manage the navigation.
+ *
+ * For anyone looking to improve this javascript taking a little time to turn
+ * the classes into namespaced classes, and giving the class structure in this file
+ * a similar structure to YUI on a moodle namespace would be AWESOME
+ */
+YAHOO.namespace('moodle.navigation');
+YAHOO.namespace('moodle.navigation.sideblockwidth');
+YAHOO.namespace('moodle.navigation.tabpanel');
+YAHOO.namespace('moodle.navigation.treecollection');
+
+/**
+ * Instatiate some very important variables that allow us to manage the navigaiton
+ * objects without having to hit my arch enemy `undefined`
+ */
+YAHOO.moodle.navigation.sideblockwidth = null;
+YAHOO.moodle.navigation.tabpanel = null;
+YAHOO.moodle.navigation.treecollection = Array();
+
+/**
+ * Navigation Tree object (function) used to control a global navigation tree
+ * handling things such as collapse, expand, and AJAX requests for more branches
+ *
+ * You should never call this directly.. you should use {@link start_new_navtree()}
+ * which will create the class and make it accessible in a smart way
+ *
+ * @class navigation_tree
+ * @constructor
+ * @param {string} treename
+ * @param {string} key
+ */
+function navigation_tree (treename, key) {
+    this.name = treename;
+    this.key = key;
+    this.errorlog = '';
+    this.ajaxbranches = 0;
+    this.expansions = Array();
+    this.instance = null
+    this.cachedcontent = null;
+    this.cachedfooter = null;
+    this.position = 'block';
+    this.tooglesidetabdisplay = '[[tooglesidetabdisplay]]';
+    this.toogleblockdisplay = '[[toogleblockdisplay]]';
+    this.sideblockwidth = null;
+    if (window[this.name]) {
+        if (window[this.name].expansions) {
+            this.expansions = window[this.name].expansions;
+        }
+        if (window[this.name].instance) {
+            this.instance = window[this.name].instance;
+        }
+        if (window[this.name].tooglesidetabdisplay) {
+            this.tooglesidetabdisplay = window[this.name].tooglesidetabdisplay;
+        }
+        if (window[this.name].toogleblockdisplay) {
+            this.toogleblockdisplay = window[this.name].toogleblockdisplay;
+        }
+    }
+}
+/**
+ * Initialise function used to attach the initial events to the navigation tree
+ * This function attachs toogles and ajax calls
+ */
+navigation_tree.prototype.initialise = function() {
+    if (!document.getElementById(this.name)) {
+        return;
+    }
+    var e = document.getElementById(this.name);
+    var i = 0;
+    while (!YAHOO.util.Dom.hasClass(e, 'sideblock') && e.nodeName.toUpperCase()!='BODY') {
+        e = e.parentNode;
+    }
+    var movetos = YAHOO.util.Dom.getElementsByClassName('moveto', 'a', e);
+    if (movetos !== null && movetos.length > 0) {
+        for (i = 0;i<movetos.length;i++) {
+            YAHOO.util.Event.addListener(movetos[i], 'click', this.toggle_block_display, this, true);
+        }
+    }
+    for (i = 0; i<this.expansions.length; i++) {
+        try {
+            YAHOO.util.Event.addListener(this.expansions[i].id, 'click', this.init_load_ajax, this.expansions[i], this);
+        } catch (err) {
+            this.errorlog += "attaching ajax load events: \t"+err+"\n";
+        }
+    }
+    var items = YAHOO.util.Dom.getElementsByClassName('tree_item branch', '', document.getElementById(this.name));
+    if (items != null && items.length>0) {
+        for (i = 0; i<items.length; i++) {
+            try {
+                YAHOO.util.Event.addListener(items[i], 'click', this.toggleexpansion, this, true);
+            } catch (err) {
+                this.errorlog += "attaching toggleexpansion events: \t"+err+"\n";
+            }
+        }
+    }
+
+    var customcommands = YAHOO.util.Dom.getElementsByClassName('customcommand', 'a', e);
+    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', e);
+    if (commands.length === 1 && customcommands.length > 0) {
+        for (i = 0; i < customcommands.length; i++) {
+            customcommands[i].parentNode.removeChild(customcommands[i]);
+            commands[0].appendChild(customcommands[i]);
+        }
+    }
+
+    if (YAHOO.util.Dom.hasClass(e, 'sideblock_js_sidebarpopout')) {
+        YAHOO.util.Dom.removeClass(e, 'sideblock_js_sidebarpopout');
+        this.toggle_block_display(e, this);
+    } else if (YAHOO.util.Dom.hasClass(e, 'sideblock_js_expansion')) {
+        YAHOO.util.Event.addListener(e, 'mouseover', this.togglesize, e, this);
+        YAHOO.util.Event.addListener(e, 'mouseout', this.togglesize, e, this);
+    }
+}
+/**
+ * Toogle a branch either collapsed or expanded... CSS styled
+ * @param {object} e Event object
+ */
+navigation_tree.prototype.toggleexpansion = function(e) {
+    YAHOO.util.Event.stopPropagation(e);
+    var target = YAHOO.util.Event.getTarget(e);
+    var parent = target.parentNode;
+    if (YAHOO.util.Dom.hasClass(parent, 'collapsed')) {
+        YAHOO.util.Dom.removeClass(parent, 'collapsed');
+    } else {
+        YAHOO.util.Dom.addClass(parent, 'collapsed');
+    }
+    if (this.position === 'sidebar') {
+        YAHOO.moodle.navigation.tabpanel.resize_tab();
+    }
+}
+/**
+ * Toggles the size on an element by adding/removing the mouseover class
+ * @param {object} e Event object
+ * @param {element} element The element to add/remove the class from
+ */
+navigation_tree.prototype.togglesize = function(e, element) {
+    if (e.type == 'mouseout') {
+        var mp = YAHOO.util.Event.getXY(e);
+        if (mp[0] == -1) {
+            return true;
+        }
+        var ep = YAHOO.util.Dom.getXY(element);
+        ep[2] = ep[0]+element.offsetWidth;
+        ep[3] = ep[1]+element.offsetHeight;
+        var withinrealm = (mp[0] > ep[0] && mp[0] < ep[2] && mp[1] > ep[1] && mp[1] < ep[3]);
+        if (!withinrealm) {
+            YAHOO.util.Event.stopEvent(e);
+            YAHOO.util.Dom.removeClass(element, 'mouseover');
+        }
+    } else {
+        YAHOO.util.Event.stopEvent(e);
+        element.style.width = element.offsetWidth +'px';
+        YAHOO.util.Dom.addClass(element, 'mouseover');
+    }
+    return true;
+}
+/**
+ * This function makes the initial call to load a branch of the navigation
+ * tree by AJAX
+ * @param {object} e Event object
+ * @param {object} branch The branch object from navigation_tree::expansions
+ * @return {bool}
+ */
+navigation_tree.prototype.init_load_ajax = function(e, branch) {
+    YAHOO.util.Event.stopPropagation(e);
+    if (YAHOO.util.Event.getTarget(e).nodeName.toUpperCase() != 'P') {
+        return true;
+    }
+    var postargs = 'elementid='+branch.id+'&id='+branch.branchid+'&type='+branch.type+'&sesskey='+moodle_cfg.sesskey;
+    if (this.instance != null) {
+        postargs += '&instance='+this.instance;
+    }
+    YAHOO.util.Connect.asyncRequest('POST', moodle_cfg.wwwroot+'/lib/ajax/getnavbranch.php', callback={
+        success:function(o) {this.load_ajax(o);},
+        failure:function(o) {this.load_ajax(o);},
+        argument: {gntinstance:this,branch:branch,event:e, target:YAHOO.util.Event.getTarget(e)},
+        scope: this
+    }, postargs);
+    return true;
+}
+/**
+ * This function loads a branch returned by AJAX into the XHTML tree structure
+ * @param {object} outcome The AJAX response
+ * @return {bool}
+ */
+navigation_tree.prototype.load_ajax = function(outcome) {
+    // Check the status
+    if (outcome.status!=0 && outcome.responseXML!=null) {
+        var branch = outcome.responseXML.documentElement;
+        if (branch!=null && this.add_branch(branch,outcome.argument.target ,1)) {
+            // If we get here everything worked perfectly
+            YAHOO.util.Event.removeListener(outcome.argument.branch.id, 'click', this.init_load_ajax);
+            if (this.position === 'sidebar') {
+                YAHOO.moodle.navigation.tabpanel.resize_tab();
+            }
+            return true;
+        }
+    }
+    // Something went wrong or there simply wasn't anything more to display
+    // add the emptybranch css class so we can flag it
+    YAHOO.util.Dom.replaceClass(outcome.argument.target, 'branch', 'emptybranch');
+    return false;
+}
+/**
+ * This recursive function takes an XML branch and includes it in the tree
+ * @param {xmlnode} branchxml The XML node for the branch
+ * @param {element} target The target node to add to
+ * @param {int} depth The depth we have delved (recusive counter)
+ * @return {bool}
+ */
+navigation_tree.prototype.add_branch = function(branchxml, target, depth) {
+    var branch = new navigation_tree_branch();
+    branch.load_from_xml_node(branchxml);
+    if (depth>1) {
+        target = branch.inject_into_dom(target,this);
+    }
+    var dropcount = 5;
+    while (target.nodeName.toUpperCase() !== 'LI') {
+        target = target.parentNode;
+        if (dropcount==0 && moodle_cfg.developerdebug) {
+            return alert("dropped because of exceeding dropcount");
+        }
+        dropcount--;
+    }
+    if (branch.haschildren && branch.mychildren && branch.mychildren.childNodes) {
+        for (var i=0;i<branch.mychildren.childNodes.length;i++) {
+            if (branch.haschildren) {
+                var ul = document.createElement('ul');
+                target.appendChild(ul);
+            }
+            var child = branch.mychildren.childNodes[i];
+            this.add_branch(child, ul, depth+1);
+        }
+    } else if(depth==1) {
+        // If we are here then we got a valid response however there are no children
+        // to display for the branch that we are expanding, thus we will return false
+        // so we can add the emptybranch class
+        return false;
+    }
+    return true;
+}
+/**
+ * This switches a navigation block between its block position and the sidebar
+ *
+ * @param {element} e Event object
+ */
+navigation_tree.prototype.toggle_block_display = function(e) {
+    if (e !== null) {
+        YAHOO.util.Event.stopPropagation(e);
+    }
+    if (this.position === 'block') {
+        this.move_to_sidebar_popout(e);
+        this.position = 'sidebar';
+    } else {
+        this.move_to_block_position(e);
+        this.position = 'block';
+    }
+}
+/**
+ * This function gets called from {@link navigation_tree.toggle_block_display()}
+ * and is responsible for moving the block from the block position to the sidebar
+ * @return {bool}
+ */
+navigation_tree.prototype.move_to_sidebar_popout = function(e) {
+    var element = document.getElementById(this.name).parentNode;
+    if (element == null) {
+        return false;
+    }
+    var tabcontent = document.getElementById(this.name).parentNode;
+    while (!YAHOO.util.Dom.hasClass(element, 'sideblock')) {
+        element = element.parentNode;
+    }
+    this.cachedcontent = element;
+
+    var sideblocknode = element;
+    while (sideblocknode && !YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
+        sideblocknode = sideblocknode.parentNode;
+    }
+
+    var moveto = YAHOO.util.Dom.getElementsByClassName('moveto customcommand', 'a', this.cachedcontent);
+    if (moveto.length > 0) {
+        for (var i=0;i<moveto.length;i++) {
+            var moveicon = moveto[i].getElementsByTagName('img');
+            if (moveicon.length>0) {
+                for (var j=0;j<moveicon.length;j++) {
+                    moveicon[j].src = moveicon[j].src.replace(/movetosidetab/, 'movetoblock');
+                    moveicon[j].setAttribute('alt', this.toogleblockdisplay);
+                    moveicon[j].setAttribute('title', this.toogleblockdisplay);
+                }
+            }
+        }
+    }
+
+    var placeholder = document.createElement('div');
+    placeholder.setAttribute('id', this.name+'_content_placeholder');
+    element.parentNode.replaceChild(placeholder, element);
+    element = null;
+    var tabtitle = this.cachedcontent.getElementsByTagName('h2')[0].cloneNode(true);
+    tabtitle.innerHTML = tabtitle.innerHTML.replace(/([a-zA-Z0-9])/g, "$1<br />");
+    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', this.cachedcontent);
+    var tabcommands = null;
+    if (commands.length > 0) {
+        tabcommands = commands[0];
+    } else {
+        tabcommands = document.createElement('div');
+        YAHOO.util.Dom.addClass(tabcommands, 'commands');
+    }
+
+    if (YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
+        var blocks = YAHOO.util.Dom.getElementsByClassName('sideblock', 'div', sideblocknode);
+        if (blocks.length === 0) {
+            YAHOO.moodle.navigation.sideblockwidth = YAHOO.util.Dom.getStyle(sideblocknode, 'width');
+            YAHOO.util.Dom.setStyle(sideblocknode, 'width', '0px');
+        }
+    }
+
+    if (YAHOO.moodle.navigation.tabpanel === null) {
+        YAHOO.moodle.navigation.tabpanel = new navigation_tab_panel();
+    }
+    YAHOO.moodle.navigation.tabpanel.add_to_tab_panel(this.name, tabtitle, tabcontent, tabcommands);
+    return true;
+}
+/**
+ * This function gets called from {@link navigation_tree.toggle_block_display()}
+ * and is responsible for moving the block from the sidebar to the block position
+ * @return {bool}
+ */
+navigation_tree.prototype.move_to_block_position = function(e) {
+
+    if (this.sideblockwidth !== null) {
+        YAHOO.util.Dom.setStyle(sideblocknode, 'width', this.sideblockwidth);
+        this.sideblockwidth = null;
+    }
+
+    var placeholder = document.getElementById(this.name+'_content_placeholder');
+    if (!placeholder || YAHOO.moodle.navigation.tabpanel == null) {
+        return false;
+    }
+
+    if (YAHOO.moodle.navigation.tabpanel.showntab !== null) {
+        YAHOO.moodle.navigation.tabpanel.hide_tab(e, YAHOO.moodle.navigation.tabpanel.showntab.tabname);
+    }
+
+    var tabcontent = YAHOO.moodle.navigation.tabpanel.get_tab_panel_contents(this.name);
+    this.cachedcontent.appendChild(tabcontent);
+    placeholder.parentNode.replaceChild(this.cachedcontent, placeholder);
+
+    if (YAHOO.moodle.navigation.sideblockwidth !== null) {
+        var sideblocknode = this.cachedcontent;
+        while (sideblocknode && !YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
+            sideblocknode = sideblocknode.parentNode;
+        }
+        if (YAHOO.util.Dom.hasClass(sideblocknode, 'block-region')) {
+            YAHOO.util.Dom.setStyle(sideblocknode, 'width', YAHOO.moodle.navigation.sideblockwidth);
+        }
+    }
+
+    var moveto = YAHOO.util.Dom.getElementsByClassName('moveto customcommand', 'a', this.cachedcontent);
+    if (moveto.length > 0) {
+        for (var i=0;i<moveto.length;i++) {
+            var moveicon = moveto[i].getElementsByTagName('img');
+            if (moveicon.length>0) {
+                for (var j=0;j<moveicon.length;j++) {
+                    moveicon[j].src = moveicon[j].src.replace(/movetoblock/, 'movetosidetab');
+                    moveicon[j].setAttribute('alt', this.tooglesidetabdisplay);
+                    moveicon[j].setAttribute('title', this.tooglesidetabdisplay);
+                }
+            }
+        }
+    }
+
+    var commands = YAHOO.util.Dom.getElementsByClassName('commands', 'div', this.cachedcontent);
+    var blocktitle = YAHOO.util.Dom.getElementsByClassName('title', 'div', this.cachedcontent);
+    if (commands.length === 1 && blocktitle.length === 1) {
+        commands[0].parentNode.removeChild(commands[0]);
+        blocktitle[0].appendChild(commands[0]);
+    }
+
+    YAHOO.moodle.navigation.tabpanel.remove_from_tab_panel(this.name);
+    
+    var block = this.cachedcontent;
+    while (!YAHOO.util.Dom.hasClass(block, 'sideblock')) {
+        block = block.parentNode;
+    }
+    return true;
+}
+
+/**
+ * This class is used to manage the navigation tab panel
+ *
+ * Through this class you can add, remove, and manage items from the navigation
+ * tab panel.
+ * Note you only EVER need one of these
+ * @constructor
+ * @class navigation_tab_panel
+ */
+function navigation_tab_panel() {
+    this.tabpanelexists = false;
+    this.tabpanelelementnames = Array();
+    this.tabpanelelementcontents = Array();
+    this.navigationpanel = null;
+    this.tabpanel = null;
+    this.tabpanels = Array();
+    this.tabcount = 0;
+    this.showntab = null;
+}
+/**
+ * This creates a tab panel element and injects it into the DOM
+ * @method create_tab_panel
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.create_tab_panel = function () {
+    var navbar  = document.createElement('div');
+    navbar.style.display = 'none';
+    navbar.setAttribute('id', 'sidebarpopup');
+    YAHOO.util.Dom.addClass(navbar, 'navigation_bar');
+    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
+        YAHOO.util.Dom.setStyle(navbar, 'height', YAHOO.util.Dom.getViewportHeight()+'px');
+    }
+
+    var navbarcontrol = document.createElement('div');
+    YAHOO.util.Dom.addClass(navbarcontrol, 'controls');
+    var removeall = document.createElement('img');
+    removeall.setAttribute('src', moodle_cfg.wwwroot+'/pix/t/movetoblock.png');
+    removeall.setAttribute('title', mstr.moodle.moveallsidetabstoblock);
+    removeall.setAttribute('alt', mstr.moodle.moveallsidetabstoblock);
+    navbarcontrol.appendChild(removeall);
+    navbar.appendChild(navbarcontrol);
+
+    document.getElementsByTagName('body')[0].appendChild(navbar);
+    navbar.appendChild(create_shadow(false, true, true, false));
+    YAHOO.util.Dom.addClass(document.getElementsByTagName('body')[0], 'has_navigation_bar');
+    this.navigationpanel = navbar;
+    this.tabpanelexists = true;
+    navbar.style.display = 'block';
+
+    YAHOO.util.Event.addListener(removeall, 'click', move_all_sidetabs_to_block_position);
+
+    return true;
+}
+/**
+ * This removes the tab panel element from the page
+ * @method remove_tab_panel
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.remove_tab_panel = function () {
+    var panel = document.getElementById('sidebarpopup');
+    if (!panel) {
+        return false;
+    }
+    this.tabpanel = null;
+    panel.parentNode.removeChild(panel);
+    this.tabpanelexists = false;
+    this.navigationpanel = null;
+    if (YAHOO.util.Dom.hasClass(document.getElementsByTagName('body')[0], 'has_navigation_bar')) {
+        YAHOO.util.Dom.removeClass(document.getElementsByTagName('body')[0], 'has_navigation_bar')
+    }
+    return true;
+}
+/**
+ * This function retrieves the content of a tab in the navigation tab panel
+ * @method get_tab_panel_contents
+ * @param {string} tabname The name of the tab
+ * @return {element} The content element
+ */
+navigation_tab_panel.prototype.get_tab_panel_contents = function(tabname) {
+    remove_shadow(this.tabpanelelementcontents[tabname]);
+    return this.tabpanelelementcontents[tabname];
+}
+/**
+ * This function adds a tab to the navigation tab panel
+ *
+ * If you find that it takes a long time to make the initial transaction then I
+ * would first check the time that set_user_preference is taking, during development
+ * the code needed to be re-jigged because it was taking a very long time to execute
+ *
+ * @method add_to_tab_panel
+ * @param {string} tabname The string name of the tab
+ * @param {element} tabtitle The title of the tab
+ * @param {element} tabcontent The content for the tab
+ * @param {element} tabcommands The commands for the tab
+ */
+navigation_tab_panel.prototype.add_to_tab_panel = function (tabname, tabtitle, tabcontent, tabcommands) {
+    if (!this.tabpanelexists) {
+        this.create_tab_panel();
+    }
+
+    var firsttab = (this.tabcount==0);
+
+    var sidetab = document.createElement('div');
+    sidetab.setAttribute('id', tabname+'_sidebarpopup');
+    YAHOO.util.Dom.addClass(sidetab, 'sideblock_tab');
+
+    if (firsttab) {
+        YAHOO.util.Dom.addClass(sidetab, 'firsttab');
+    }
+    var sidetabtitle = document.createElement('div');
+    sidetabtitle.appendChild(tabtitle);
+    sidetabtitle.setAttribute('id', tabname+'_title');
+    YAHOO.util.Dom.addClass(sidetabtitle, 'title');
+    tabcontent.appendChild(create_shadow(true, true, true, false));
+    sidetab.appendChild(sidetabtitle);
+
+    if (tabcommands.childNodes.length>0) {
+        tabcontent.appendChild(tabcommands);
+    }
+
+    this.navigationpanel.appendChild(sidetab);
+
+    var position = YAHOO.util.Dom.getXY(sidetabtitle);
+    position[0] += sidetabtitle.offsetWidth;
+    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 8) {
+        position[0] -= 2;
+    }
+
+    this.tabpanels[tabname] = new YAHOO.widget.Panel('navigation_tab_panel_'+tabname, {
+        close:false,
+        draggable:false,
+        constraintoviewport: false,
+        underlay:"none",
+        visible:false,
+        monitorresize:false,
+        /*context:[tabname+'_title','tl','tr',['configChanged','beforeShow','changeBody']],*/
+        xy:position,
+        autofillheight:'body'});
+    this.tabpanels[tabname].showEvent.subscribe(this.resize_tab, this, true);
+    this.tabpanels[tabname].setBody(tabcontent);
+    this.tabpanels[tabname].render(this.navigationpanel);
+
+    this.tabpanelelementnames[this.tabpanelelementnames.length] = tabname;
+    this.tabpanelelementcontents[tabname] = tabcontent;
+    this.tabcount++;
+
+    YAHOO.util.Event.addListener(sidetab, "mouseover", this.show_tab, tabname, this);
+
+    set_user_preference('nav_in_tab_panel_'+tabname, 1);
+}
+/**
+ * This function handles checking the size, and positioning of the navigaiton
+ * panel when expansion events occur, or when the panel is shown, or if the window
+ * is resized
+ *
+ * There are undoubtably some bugs in this little bit of code. For one it relies
+ * on the padding set in CSS by the YUI:sam skin, if you are hitting a problem
+ * whereby the navigation extends beyond its border, or doesn't fill to its own
+ * border check the value assigned to padding for the panel body `.yui_panel .bd`
+ *
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.resize_tab = function () {
+    var screenheight = YAHOO.util.Dom.getViewportHeight();
+    var tabheight = parseInt(this.tabpanels[this.showntab.tabname].body.offsetHeight);
+    var tabtop = parseInt(this.tabpanels[this.showntab.tabname].cfg.getProperty('y'));
+    var titletop = YAHOO.util.Dom.getY(this.showntab.tabname+'_title');
+
+    // This makes sure that the panel is the same height as the tab title to
+    // begin with
+    if (tabtop > 10 && tabtop > titletop) {
+        this.tabpanels[this.showntab.tabname].cfg.setProperty('y', titletop);
+    }
+
+    // This makes sure that if the panel is big it is moved up to ensure we don't
+    // have wasted space above the panel
+    if ((tabtop+tabheight)>screenheight && tabtop > 10) {
+        tabtop = (screenheight-tabheight-10);
+        if (tabtop<10) {
+            tabtop = 10;
+        }
+        this.tabpanels[this.showntab.tabname].cfg.setProperty('y', tabtop);
+    }
+
+    // This makes the panel constrain to the screen's height if the panel is big
+    if (tabtop <= 10 && ((tabheight+tabtop*2) > screenheight || YAHOO.util.Dom.hasClass(this.tabpanels[this.showntab.tabname].body, 'oversized_content'))) {
+        this.tabpanels[this.showntab.tabname].cfg.setProperty('height', (screenheight-39));
+        YAHOO.util.Dom.setStyle(this.tabpanels[this.showntab.tabname].body, 'height', (screenheight-59)+'px');
+        YAHOO.util.Dom.addClass(this.tabpanels[this.showntab.tabname].body, 'oversized_content');
+    }
+    return true;
+}
+/**
+ * This function sets everything up for the show even and then calls the panel's
+ * show event once we are happy.
+ *
+ * This function is responsible for closing any open panels, removing show events
+ * so we don't refresh unnessecarily and adding events to trap closing, and resizing
+ * events
+ *
+ * @param {event} e The event that fired to get us here
+ * @param {string} tabname The tabname to open
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.show_tab = function (e, tabname) {
+    if (this.showntab !== null) {
+        this.hide_tab(e, this.showntab.tabname);
+    }
+    this.showntab = {event:e, tabname:tabname};
+    this.tabpanels[tabname].show(e, this.tabpanel);
+    YAHOO.util.Dom.addClass(tabname+'_title', 'active_tab');
+    YAHOO.util.Event.removeListener(tabname+'_sidebarpopup', "mouseover", this.show_tab);
+    YAHOO.util.Event.addListener(tabname+'_sidebarpopup', "click", this.hide_tab, tabname, this);
+    YAHOO.util.Event.addListener(window, 'resize', this.resize_tab, this, true);
+    YAHOO.util.Event.addListener(document.body, "click", this.hide_tab, tabname, this);
+    return true;
+}
+/**
+ * This function closes the open tab and sets the listeners up to handle the show
+ * event again
+ *
+ * @param {event} e The event that fired to get us here
+ * @param {string} tabname The tabname to close
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.hide_tab = function(e, tabname) {
+    this.showntab = null;
+    YAHOO.util.Event.addListener(tabname+'_sidebarpopup', "mouseover", this.show_tab, tabname, this);
+    YAHOO.util.Event.removeListener(window, 'resize', this.resize_tab);
+    YAHOO.util.Event.removeListener(document.body, "click", this.hide_tab);
+    YAHOO.util.Dom.removeClass(tabname+'_title', 'active_tab');
+    this.tabpanels[tabname].hide(e, this.tabpanel);
+    return true;
+}
+/**
+ * This function removes a tab from the navigation tab panel
+ * @param {string} tabname
+ * @return {bool}
+ */
+navigation_tab_panel.prototype.remove_from_tab_panel = function(tabname) {
+    set_user_preference('nav_in_tab_panel_'+tabname, 0);
+    var tab = document.getElementById(tabname+'_sidebarpopup');
+    if (!tab) {
+        return false;
+    }
+    tab.parentNode.removeChild(tab);
+    this.tabpanels[tabname].destroy();
+    this.tabpanels[tabname] = null;
+    this.tabcount--;
+    if (this.tabcount === 0) {
+        this.remove_tab_panel();
+    }
+    return true;
+}
+
+/**
+ * Global navigation tree branch object used to parse an XML branch
+ * into a usable object, and then to inject it into the DOM
+ * @class navigation_tree_branch
+ * @constructor
+ */
+function navigation_tree_branch() {
+    this.myname = null;
+    this.mytitle = null;
+    this.myclass = null;
+    this.myid = null;
+    this.mykey = null;
+    this.mytype = null;
+    this.mylink = null;
+    this.myicon = null;
+    this.myexpandable = null;
+    this.myhidden = false;
+    this.haschildren = false;
+    this.mychildren = false;
+}
+/**
+ * This function populates the object from an XML branch
+ * @param {xmlnode} branch The XML branch to turn into an object
+ */
+navigation_tree_branch.prototype.load_from_xml_node = function (branch) {
+    this.myname = null;
+    this.mytitle = branch.getAttribute('title');
+    this.myclass = branch.getAttribute('class');
+    this.myid = branch.getAttribute('id');
+    this.mylink = branch.getAttribute('link');
+    this.myicon = branch.getAttribute('icon');
+    this.mykey = branch.getAttribute('key');
+    this.mytype = branch.getAttribute('type');
+    this.myexpandable = branch.getAttribute('expandable');
+    this.myhidden = (branch.getAttribute('hidden')=='true');
+    this.haschildren = (branch.getAttribute('haschildren')=='true');
+    for (var i=0; i<branch.childNodes.length;i++) {
+        var node = branch.childNodes[i];
+        switch (node.nodeName.toLowerCase()) {
+            case 'name':
+                this.myname = node.firstChild.nodeValue;
+                break;
+            case 'children':
+                this.mychildren = node;
+        }
+    }
+}
+/**
+ * This function injects the node into the navigation tree
+ * @param {element} element The branch to inject into {element}
+ * @param {navigation_tree} gntinstance The instance of the navigaiton_tree that this branch
+ *         is associated with
+ * @return {element} The now added node
+ */
+navigation_tree_branch.prototype.inject_into_dom = function (element, gntinstance) {
+    var branchli = document.createElement('li');
+    var branchp = document.createElement('p');
+    YAHOO.util.Dom.addClass(branchp, 'tree_item');
+    if (this.myexpandable !==null || this.haschildren) {
+        YAHOO.util.Dom.addClass(branchp, 'branch');
+        YAHOO.util.Dom.addClass(branchli, 'collapsed');
+        YAHOO.util.Event.addListener(branchp, 'click', gntinstance.toggleexpansion, this, gntinstance);
+        if (this.myexpandable) {
+            YAHOO.util.Event.addListener(branchp, 'click', gntinstance.init_load_ajax, {branchid:this.mykey,id:this.myid,type:this.mytype}, gntinstance);
+        }
+    }
+    if (this.myclass != null) {
+        YAHOO.util.Dom.addClass(branchp, this.myclass);
+    }
+    if (this.myid != null) {
+        branchp.setAttribute('id',this.myid);
+    }
+    if (this.myicon != null) {
+        var branchicon = document.createElement('img');
+        branchicon.setAttribute('src',this.myicon);
+        branchicon.setAttribute('alt','');
+        branchp.appendChild(branchicon);
+        this.myname = ' '+this.myname;
+    }
+    if (this.mylink === null) {
+        branchp.innerHTML = this.myname.replace(/\n/g, '<br />');
+    } else {
+        var branchlink = document.createElement('a');
+        branchlink.setAttribute('title', this.mytitle);
+        branchlink.setAttribute('href', this.mylink);
+        branchlink.innerHTML = this.myname.replace(/\n/g, '<br />');
+        if (this.myhidden) {
+            YAHOO.util.Dom.addClass(branchlink, 'dimmed');
+        }
+        branchp.appendChild(branchlink);
+    }
+    branchli.appendChild(branchp);
+    element.appendChild(branchli);
+    return branchli;
+}
+
+/**
+ * Creates a new JS instance of a global navigation tree and kicks it into gear
+ * @param {string} treename The name of the tree
+ */
+function setup_new_navtree(treename) {
+    var key = YAHOO.moodle.navigation.treecollection.length;
+    YAHOO.moodle.navigation.treecollection[key] = new navigation_tree(treename, key);
+    YAHOO.moodle.navigation.treecollection[key].initialise();
+}
+
+/**
+ * This function moves all navigation tree instances that are currently
+ * displayed in the sidebar back into their block positions
+ */
+function move_all_sidetabs_to_block_position(e) {
+    for (var i=0; i<YAHOO.moodle.navigation.treecollection.length;i++) {
+        var navtree = YAHOO.moodle.navigation.treecollection[i];
+        if (navtree.position != 'block') {
+            navtree.move_to_block_position(e);
+        }
+    }
+}
+
+/**
+ * This function create a series of DIV's appended to an element to give it a
+ * shadow
+ * @param {bool} top Displays a top shadow if true
+ * @param {bool} right Displays a right shadow if true
+ * @param {bool} bottom Displays a bottom shadow if true
+ * @param {bool} left Displays a left shadow if true
+ * @return {element}
+ */
+function create_shadow(top, right, bottom, left) {
+    var shadow = document.createElement('div');
+    YAHOO.util.Dom.addClass(shadow, 'divshadow');
+    if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
+        // IE6 just doest like my shadow...
+        return shadow;
+    }
+    var createShadowDiv = function(cname) {
+        var shadowdiv = document.createElement('div');
+        YAHOO.util.Dom.addClass(shadowdiv, cname);
+        if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 7) {
+            // IE version less than 7 doesnt support alpha
+            YAHOO.util.Dom.setStyle(shadowdiv, 'opacity', 0.3);
+        }
+        return shadowdiv;
+    }
+    if (top) shadow.appendChild(createShadowDiv('shadow_top'));
+    if (right) shadow.appendChild(createShadowDiv('shadow_right'));
+    if (bottom) shadow.appendChild(createShadowDiv('shadow_bottom'));
+    if (left) shadow.appendChild(createShadowDiv('shadow_left'));
+    if (top && left) shadow.appendChild(createShadowDiv('shadow_top_left'));
+    if (bottom && left) shadow.appendChild(createShadowDiv('shadow_bottom_left'));
+    if (top && right) shadow.appendChild(createShadowDiv('shadow_top_right'));
+    if (bottom && right) shadow.appendChild(createShadowDiv('shadow_bottom_right'));
+    return shadow;
+}
+/**
+ * This function removes any shadows that a node and its children may have
+ * @param {element} el The element to remove the shadow from
+ * @return {bool}
+ */
+function remove_shadow(el) {
+    var shadows = YAHOO.util.Dom.getElementsByClassName('divshadow', 'div', el);
+    if (shadows == null || shadows.length == 0) return true;
+    for (var i=0;i<shadows.length;i++) {
+        shadows[i].parentNode.removeChild(shadows[i]);
+    }
+    return true;
+}
\ No newline at end of file
index 4ba42d1d43bc441b0a4cff26d0129c6361cbc6d2..7c8521ca690a57e05f6c3112af12939b9cec7437 100644 (file)
@@ -721,18 +721,45 @@ function addonload(fn) {
             fn();
     }
 }
-
-function getElementsByClassName(oElm, strTagName, oClassNames) {
+/**
+ * Replacement for getElementsByClassName in browsers that aren't cool enough
+ * 
+ * Relying on the built-in getElementsByClassName is far, far faster than
+ * using YUI.
+ * 
+ * Note: the third argument used to be an object with odd behaviour. It now
+ * acts like the 'name' in the HTML5 spec, though the old behaviour is still
+ * mimicked if you pass an object.
+ *
+ * @param {Node} oElm The top-level node for searching. To search a whole
+ *                    document, use `document`.
+ * @param {String} strTagName filter by tag names
+ * @param {String} name same as HTML5 spec
+ */
+function getElementsByClassName(oElm, strTagName, name) {
+    // for backwards compatibility
+    if(typeof name == "object") {
+        var names = new Array();
+        for(var i=0; i<name.length; i++) names.push(names[i]);
+        name = names.join('');
+    }
+    // use native implementation if possible
+    if (oElm.getElementsByClassName && Array.filter) {
+        if (strTagName == '*') {
+            return oElm.getElementsByClassName(name);
+        } else {
+            return Array.filter(oElm.getElementsByClassName(name), function(el) {
+                return el.nodeName.toLowerCase() == strTagName.toLowerCase();
+            });
+        }
+    }
+    // native implementation unavailable, fall back to slow method
     var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
     var arrReturnElements = new Array();
     var arrRegExpClassNames = new Array();
-    if(typeof oClassNames == "object") {
-        for(var i=0; i<oClassNames.length; i++) {
-            arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)"));
-        }
-    }
-    else{
-        arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)"));
+    var names = name.split(' ');
+    for(var i=0; i<names.length; i++) {
+        arrRegExpClassNames.push(new RegExp("(^|\\s)" + names[i].replace(/\-/g, "\\-") + "(\\s|$)"));
     }
     var oElement;
     var bMatchesAll;
diff --git a/lib/navigationlib.php b/lib/navigationlib.php
new file mode 100644 (file)
index 0000000..b5cd6b0
--- /dev/null
@@ -0,0 +1,3055 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains classes used to manage the navigation structures in Moodle
+ * and was introduced as part of the changes occuring in Moodle 2.0
+ *
+ * @since 2.0
+ * @package moodlecore
+ * @subpackage navigation
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (!function_exists('get_all_sections')) {
+    /** Include course lib for its functions */
+    require_once($CFG->dirroot.'/course/lib.php');
+}
+
+/**
+ * This class is used to represent a node in a navigation tree
+ *
+ * This class is used to represent a node in a navigation tree within Moodle,
+ * the tree could be one of global navigation, settings navigation, or the navbar.
+ * Each node can be one of two types either a Leaf (default) or a branch.
+ * When a node is first created it is created as a leaf, when/if children are added
+ * the node then becomes a branch.
+ *
+ * @package moodlecore
+ * @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) */
+    const NODETYPE_LEAF = 0;
+    /** Used to identify this node a branch, happens with children */
+    const NODETYPE_BRANCH = 1;
+    /** Unknown node type */
+    const TYPE_UNKNOWN =    null;
+    /**  System node type */
+    const TYPE_SYSTEM =     0;
+    /**  Category node type */
+    const TYPE_CATEGORY =   10;
+    /**  Course node type */
+    const TYPE_COURSE =     20;
+    /**  Course Structure node type */
+    const TYPE_SECTION =  30;
+    /**  Activity node type, e.g. Forum, Quiz */
+    const TYPE_ACTIVITY =   40;
+    /**  Resource node type, e.g. Link to a file, or label */
+    const TYPE_RESOURCE =   50;
+    /**  A custom node type, default when adding without specifing type */
+    const TYPE_CUSTOM =     60;
+    /**  Setting node type, used only within settings nav */
+    const TYPE_SETTING =    70;
+
+    /** @var int Parameter to aid the coder in tracking [optional] */
+    public $id = null;
+    /** @var string|int The identifier for the node, used to retrieve the node */
+    public $key = null;
+    /** @var string The text to use for the node */
+    public $text = null;
+    /** @var string Short text to use if requested [optional] */
+    public $shorttext = null;
+    /** @var string The title attribute for an action if one is defined */
+    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) */
+    public $action = null;
+    /** @var string The path to an icon to use for this node */
+    public $icon = null;
+    /** @var int See TYPE_* constants defined for this class */
+    public $type = self::TYPE_UNKNOWN;
+    /** @var int See NODETYPE_* constants defined for this class */
+    public $nodetype = self::NODETYPE_LEAF;
+    /** @var bool If set to true the node will be collapsed by default */
+    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 */
+    public $classes = array();
+    /** @var array 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 */
+    public $hidden = false;
+    /** @var bool If set to false the node will not be displayed */
+    public $display = true;
+    /** @var bool If set to true then an HR will be printed before the node */
+    public $preceedwithhr = false;
+    /** @var bool If set to true the the navigation bar should ignore this 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 array */
+    protected $namedtypes = array(0=>'system',10=>'category',20=>'course',30=>'structure',40=>'activity',50=>'resource',60=>'custom',70=>'setting');
+    /** @var moodle_url */
+    protected static $fullmeurl = null;
+
+    /**
+     * Establish the node, with either text string or array or properites
+     *
+     * 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.
+     *
+     * <code>
+     * $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'] = $OUTPUT->old_icon_url('i/course');
+     * $properties['type'] = navigation_node::TYPE_COURSE;
+     * $properties['key'] = 'newitem';
+     * $PAGE->navigation->newitem = $properties;
+     * </code>
+     *
+     * The following are properties that must/can be set in the properties array
+     * <ul>
+     * <li><b>text</b>: You must set text, if this is not set a coding exception is thrown.</li>
+     * <li><b>short</b> <i>optional</i>: A short description used for navbar optional.</li>
+     * <li><b>action</b> <i>optional</i>: This can be either a {@link moodle_url} for a link, or string that can be directly output in instead of the text.</li>
+     * <li><b>icon</b> <i>optional</i>: The path to an icon to display with the node.</li>
+     * <li><b>type</b> <i>optional</i>: This type of the node, defaults to TYPE_CUSTOM.</li>
+     * <li><b>key</b> <i>optional</i>: This can be set to allow you to easily retrieve a node you have created.</li>
+     * </ul>
+     *
+     * @param string|array $properties
+     */
+    public function __construct($properties) {
+        global $PAGE;
+        if (is_array($properties)) {
+            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'];
+                $this->check_if_active();
+            }
+            if (array_key_exists('icon', $properties)) {
+                $this->icon = $properties['icon'];
+            }
+            if (array_key_exists('type', $properties)) {
+                $this->type = $properties['type'];
+            } else {
+                $this->type = self::TYPE_CUSTOM;
+            }
+            if (array_key_exists('key', $properties)) {
+                $this->key = $properties['key'];
+            }
+        } 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.');
+        }
+        $this->title = $this->text;
+        if (strlen($this->text)>50) {
+            $this->text = substr($this->text, 0, 50).'...';
+        }
+        if (is_string($this->shorttext) && strlen($this->shorttext)>25) {
+            $this->shorttext = substr($this->shorttext, 0, 25).'...';
+        }
+    }
+
+    /**
+     * This function checks if the node is the active child by comparing its action
+     * to the current page URL obtained via $ME
+     *
+     * @staticvar moodle_url $fullmeurl
+     * @return bool True is active, false otherwise
+     */
+    public function check_if_active() {
+        global $FULLME;
+        if (self::$fullmeurl == null) {
+            $pos = strpos($FULLME, '?');
+            if ($pos===false) {
+                $pos = strlen($FULLME);
+            }
+            $url = substr($FULLME, 0, $pos);
+            $args = substr($FULLME, strpos($FULLME, '?')+1);
+            preg_match_all('#\&([^\=]*?)\=([^\&]*)#si', '&'.$args, $matches, PREG_SET_ORDER);
+            self::$fullmeurl = new moodle_url($url);
+            foreach ($matches as $pair) {
+                self::$fullmeurl->param($pair[1],$pair[2]);
+            }
+        }
+        if ($this->action instanceof moodle_url && $this->action->compare(self::$fullmeurl)) {
+            $this->make_active();
+            return true;
+        } else if (is_string($this->action) && $this->action==$FULLME) {
+            $this->make_active();
+            return true;
+        }
+        return false;
+    }
+    /**
+     * This function allows the user to add a child node to this 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 <i>optional</i>
+     * @param int $type The type of node should be one of the const types of navigation_node <i>optional</i>
+     * @param string $action Either a moodle_url or a bit of html to use instead of the text <i>optional</i>
+     * @param string $icon The path to an icon to use for this node <i>optional</i>
+     * @return string The key that was used for this node
+     */
+    public function add($text, $shorttext=null, $key=null, $type=null, $action=null, $icon=null) {
+        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;
+        }
+        if ($action!==null) {
+            $itemarray['action'] = $action;
+        }
+
+        if ($shorttext!==null) {
+            $itemarray['shorttext'] = $shorttext;
+        }
+        if ($icon!==null) {
+            $itemarray['icon'] = $icon;
+        }
+        if ($key===null) {
+            $key = count($this->children);
+        }
+        $itemarray['key'] = $key;
+        $this->children[$key] = new navigation_node($itemarray);
+        if ($type==self::TYPE_CATEGORY || (isloggedin() && $type==self::TYPE_COURSE)) {
+            $this->children[$key]->nodetype = self::NODETYPE_BRANCH;
+        }
+        if ($this->hidden) {
+            $this->children[$key]->hidden = true;
+        }
+        return $key;
+    }
+
+    /**
+     * Adds a new node to a particular point by recursing through an array of node keys
+     *
+     * @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 <i>optional</i>
+     * @param int $type The type of node should be one of the const types of navigation_node <i>optional</i>
+     * @param string $action Either a moodle_url or a bit of html to use instead of the text <i>optional</i>
+     * @param string $icon The path to an icon to use for this node <i>optional</i>
+     * @return mixed Either the key used for the node once added or false for failure
+     */
+    public function add_to_path($patharray, $key=null, $text=null, $shorttext=null, $type=null, $action=null, $icon=null) {
+        if (count($patharray)==0) {
+            $key = $this->add($text, $shorttext, $key, $type, $action, $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;
+            }
+        }
+    }
+
+    /**
+     * Add a css class to this particular node
+     * 
+     * @param string $class The css class to add
+     * @return bool Returns true
+     */
+    public function add_class($class) {
+        if (!in_array($class, $this->classes)) {
+            $this->classes[] = $class;
+        }
+        return true;
+    }
+
+    /**
+     * Removes a given class from this node if it exists
+     *
+     * @param string $class
+     * @return bool
+     */
+    public function remove_class($class) {
+        if (in_array($class, $this->classes)) {
+            $key = array_search($class,$this->classes);
+            if ($key!==false) {
+                unset($this->classes[$key]);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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.
+     *
+     * @param int $depth defualts to 2
+     * @return bool Returns true
+     */
+    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);
+            }
+            return true;
+        } else {
+            $this->collapse_children();
+            return true;
+        }
+    }
+
+    /**
+     * Collapses all of the child nodes recursion optional
+     *
+     * @param bool $recurse If set to true child nodes are closed recursively
+     * @return bool Returns true
+     */
+    protected function collapse_children($recurse=true) {
+        if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) {
+            foreach ($this->children as &$child) {
+                if (!$this->forceopen) {
+                    $child->collapse = true;
+                }
+                if ($recurse && $child instanceof navigation_node) {
+                    $child->collapse_children($recurse);
+                }
+            }
+            unset($child);
+        }
+        return true;
+    }
+
+    /**
+     * Produce the actual HTML content for the node including any action or icon
+     *
+     * @param bool $shorttext If true then short text is used rather than text if it has been set
+     * @return string The HTML content
+     */
+    public function content($shorttext=false) {
+        global $OUTPUT, $CFG;
+        if (!$this->display) {
+            return '';
+        }
+        if ($shorttext && $this->shorttext!==null) {
+            $content = s($this->shorttext);
+        } else {
+            $content = s($this->text);
+        }
+        if ($content != '' && ((is_object($this->action) && $this->action instanceof moodle_url) || is_string($this->action))) {
+            $link = new html_link();
+            if ($this->forcetitle || ($this->shorttext!==null && $this->title !== $this->shorttext) || $this->title !== $this->text) {
+                $link->title = $this->title;
+            }
+            if ($this->hidden) {
+                $link->add_class('dimmed');
+            }
+            $link->url = $this->action;
+            $link->text = $content;
+            $content = $OUTPUT->link($link);
+        } else {
+            if ($this->hidden) {
+                $content = sprintf('<span class="dimmed_text">%s</span>', $content);
+            } else {
+                $content = sprintf('<span>%s</span>', $content);
+            }
+        }
+        if ($this->icon!==null) {
+            $content = sprintf('<img src="%s" alt="" /> %s',$this->icon,$content);
+        } else if ($this->helpbutton!==null) {
+            $content = sprintf('%s<span class="clearhelpbutton">%s</span>',trim($this->helpbutton),$content);
+        }
+        return $content;
+    }
+    
+    /**
+     * Get the CSS type for this node
+     * 
+     * @return string
+     */
+    public function get_css_type() {
+        if (array_key_exists($this->type, $this->namedtypes)) {
+            return 'type_'.$this->namedtypes[$this->type];
+        }
+        return 'type_unknown';
+    }
+
+    /**
+     * Find and return a child node if it exists (returns a reference to the child)
+     *
+     * 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)
+     */
+    public function find_child($key, $type=self::TYPE_CATEGORY, $default = false) {
+        if (array_key_exists($key, $this->children) && $this->children[$key]->type == $type) {
+            return $this->children[$key];
+        } 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;
+                }
+            }
+        }
+        return $default;
+    }
+
+    /**
+     * Find the active child
+     *
+     * @param null|int $type
+     * @return navigation_node|bool
+     */
+    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) {
+                        return $child;
+                    } else {
+                        $outcome = $child->find_active_node($type);
+                        if ($outcome!==false) {
+                            return $outcome;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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
+     */
+    public function find_child_depth($key, $type=self::TYPE_CATEGORY) {
+        $depth = 0;
+        if (array_key_exists($key, $this->children) && $this->children[$key]->type == $type) {
+            $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;
+    }
+
+    /**
+     * Toogles display of nodes and child nodes based on 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
+     *
+     * @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
+     *
+     * @return bool True if it does fales otherwise
+     */
+    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;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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.
+     */
+    public function find_expandable(&$expandable) {
+        static $branchcount;
+        if ($branchcount==null) {
+            $branchcount=1;
+        }
+        if ($this->nodetype == self::NODETYPE_BRANCH && count($this->children)==0) {
+            $this->id = 'expandable_branch_'.$branchcount;
+            $branchcount++;
+            $expandable[] = array('id'=>$this->id,'branchid'=>$this->key,'type'=>$this->type);
+        } else if ($this->nodetype==self::NODETYPE_BRANCH) {
+            foreach ($this->children as $child) {
+                $child->find_expandable($expandable);
+            }
+        }
+    }
+
+    /**
+     * Used to return a child node with a given key
+     *
+     * 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 navigation_node|bool The child if it exists or false
+     */
+    public function get($key) {
+        if ($key===false) {
+            return false;
+        }
+        if ($this->nodetype === self::NODETYPE_BRANCH && count($this->children)>0) {
+            if (array_key_exists($key, $this->children)) {
+                return $this->children[$key];
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Fetch a node given a set of keys that describe its path
+     *
+     * @param array $keys An array of keys
+     * @return navigation_node|bool The node or false
+     */
+    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;
+        }
+    }
+
+    /**
+     * 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) {
+        if (array_key_exists($key, $this->children)) {
+            unset($this->children[$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.
+     *
+     * @return bool True is a node was marked active false otherwise
+     */
+    public function reiterate_active_nodes() {
+        if ($this->nodetype !== self::NODETYPE_BRANCH) {
+            return false;
+        }
+        foreach ($this->children as $child) {
+            $outcome = $child->check_if_active();
+            if (!$outcome && $child->nodetype === self::NODETYPE_BRANCH) {
+                $outcome = $child->reiterate_active_nodes();
+            }
+            if ($outcome) {
+                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');
+    }
+}
+
+/**
+ * The global navigation class used for... the global navigation
+ *
+ * This class is used by PAGE to store the global navigation for the site
+ * and is then used by the settings nav and navbar to save on processing and DB calls
+ *
+ * See
+ * <ul>
+ * <li><b>{@link lib/pagelib.php}</b> {@link moodle_page::initialise_theme_and_output()}<li>
+ * <li><b>{@link lib/ajax/getnavbranch.php}</b> Called by ajax<li>
+ * </ul>
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @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;
+    /** @var bool */
+    protected $initialised = false;
+
+    /** @var null|int */
+    public $expansionlimit = null;
+    /** @var stdClass */
+    public $context = null;
+    /** @var mixed */
+    public $expandable = null;
+    /** @var bool */
+    public $showemptybranches = true;
+    /** @var bool  */
+    protected $isloggedin = false;
+
+    /**
+     * Sets up the object with basic settings and preparse it for use
+     */
+    public function __construct() {
+        global $CFG, $PAGE;
+        if (during_initial_install()) {
+            return false;
+        }
+        $this->key = 0;
+        $this->type = self::TYPE_SYSTEM;
+        $this->isloggedin = isloggedin();
+        $this->text = get_string('home');
+        $this->forceopen = true;
+        $this->action = new moodle_url($CFG->wwwroot);
+        $this->cache = new navigation_cache('navigation');
+        $PAGE->requires->string_for_js('moveallsidetabstoblock','moodle');
+        $regenerate = optional_param('regenerate', null, PARAM_TEXT);
+        if ($regenerate==='navigation') {
+            $this->cache->clear();
+        }
+    }
+
+    /**
+     * Override: This function generated the content of the navigation
+     *
+     * If an expansion limit has been set then we hide everything to after that
+     * set limit type
+     *
+     * @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.
+     *
+     * It also passed Javascript args and function calls as required
+     *
+     * @return bool Returns true
+     */
+    public function initialise($jsargs = null) {
+        global $PAGE, $SITE;
+        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;
+
+        switch ($contextlevel) {
+            case CONTEXT_SYSTEM: 
+                $depth = $this->load_for_category(false);
+                break;
+            case CONTEXT_COURSECAT: 
+                $depth = $this->load_for_category();
+                break;
+            case CONTEXT_BLOCK:
+            case CONTEXT_COURSE: 
+                $depth = $this->load_for_course();
+                break;
+            case CONTEXT_MODULE: 
+                $depth = $this->load_for_activity();
+                break;
+            case CONTEXT_USER: 
+                $depth = $this->load_for_user();
+                break;
+        }
+        $this->collapse_at_depth($this->depthforward+$depth);
+        $this->respect_forced_open();
+        $expandable = array();
+        $this->find_expandable($expandable);
+        $this->expandable = $expandable;
+        $this->initialised = true;
+        return true;
+    }
+    /**
+     * This function loads the global navigation structure for a user.
+     *
+     * This gets called by {@link initialise()} when the context is CONTEXT_USER
+     */
+    protected function load_for_user() {
+        global $DB, $SITE;
+        $courseid = optional_param('course', false, PARAM_INT);
+        if ($courseid!==false && $courseid!=$SITE->id) {
+            $course = $DB->get_record('course', array('id'=>$courseid));
+        }
+        if (isset($course) && $course) {
+            $this->load_for_course();
+        } else {
+            $this->load_categories();
+        }
+    }
+
+    /**
+     * 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);
+        if ($lookforid && $id!==null) {
+            $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;
+        $keys = array();
+        $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;
+        }
+        $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, $OUTPUT, $CFG, $FULLME;
+
+        if ($course === null) {
+            $course = $PAGE->course;
+        }
+
+        if (!$this->cache->cached('modinfo'.$course->id)) {
+            $this->cache->{'modinfo'.$course->id} = get_fast_modinfo($course);
+        }
+        $modinfo =  $this->cache->{'modinfo'.$course->id};
+
+        $resources = array('resource', 'label');
+        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 ($module->sectionnum!='0' || (!$viewhiddenactivities && !$module->visible)) {
+                continue;
+            }
+            $icon = null;
+            if (!in_array($module->modname, $resources)) {
+                if ($module->icon=='') {
+                    $icon = $OUTPUT->mod_icon_url('icon', $module->modname);
+                }
+                $url = new moodle_url($CFG->wwwroot.'/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                $type = navigation_node::TYPE_ACTIVITY;
+            } else {
+                $url = null;
+                $type = navigation_node::TYPE_RESOURCE;
+                if ($module->modname!='label') {
+                    $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                }
+                if ($module->icon!=='') {
+                    $icon = $OUTPUT->old_icon_url(preg_replace('#\.(png|gif)$#i','',$module->icon));
+                }
+            }
+            $this->add_to_path($keys, $module->id, $module->name, $module->name, $type, $url, $icon);
+            $child = $this->find_child($module->id, $type);
+            if ($child != false) {
+                $child->title(get_string($module->modname, $module->modname));
+                if ($type==navigation_node::TYPE_ACTIVITY && $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, $OUTPUT, $CFG, $FULLME;
+
+        if ($course === null) {
+            $course = $PAGE->course;
+        }
+
+        if (!$this->cache->cached('modinfo'.$course->id)) {
+            $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};
+
+        $resources = array('resource', 'label');
+
+        if (!$this->cache->cached('canviewhiddenactivities')) {
+            $this->cache->canviewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $this->context);
+        }
+        $viewhiddenactivities = $this->cache->viewhiddenactivities;
+        foreach ($modinfo->cms as $module) {
+            if ($module->sectionnum=='0' || (!$viewhiddenactivities && !$module->visible) || ($singlesectionid!=false && $module->sectionnum!==$singlesectionid)) {
+                continue;
+            }
+            $icon = null;
+            if (!in_array($module->modname, $resources)) {
+                if ($module->icon=='') {
+                    $icon = $OUTPUT->mod_icon_url('icon', $module->modname);
+                }
+                $url = new moodle_url($CFG->wwwroot.'/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                $type = navigation_node::TYPE_ACTIVITY;
+            } else {
+                $url = null;
+                $type = navigation_node::TYPE_RESOURCE;
+                if ($module->modname!='label') {
+                    $url = new moodle_url($CFG->wwwroot.'/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                }
+                if ($module->icon!=='') {
+                    $icon = $OUTPUT->old_icon_url(preg_replace('#\.(png|gif)$#i','',$module->icon));
+                }
+            }
+            $path = $keys;
+            $path[] = $sections[$module->sectionnum]->id;
+            $this->add_to_path($path, $module->id, $module->name, $module->name, $type, $url, $icon);
+            $child = $this->find_child($module->id, $type);
+            if ($child != false) {
+                $child->title(get_string($module->modname, $module->modname));
+                if (!$module->visible) {
+                    $child->hidden = true;
+                }
+                if ($type==navigation_node::TYPE_ACTIVITY && $this->module_extends_navigation($module->modname)) {
+                    $child->nodetype = self::NODETYPE_BRANCH;
+                }
+            }
+        }
+    }
+
+    /**
+     * Check if a given module has a method to extend the navigation
+     *
+     * @param string $modname
+     * @return bool
+     */
+    protected function module_extends_navigation($modname) {
+        global $CFG;
+        if ($this->cache->cached($modname.'_extends_navigation')) {
+            return $this->cache->{$modname.'_extends_navigation'};
+        }
+        $file = $CFG->dirroot.'/mod/'.$modname.'/lib.php';
+        $function = $modname.'_extend_navigation';
+        if (function_exists($function)) {
+            $this->cache->{$modname.'_extends_navigation'} = true;
+            return true;
+        } else if (file_exists($file)) {
+            require_once($file);
+            if (function_exists($function)) {
+                $this->cache->{$modname.'_extends_navigation'} = true;
+                return true;
+            }
+        }
+        $this->cache->{$modname.'_extends_navigation'} = false;
+        return false;
+    }
+    /**
+     * Load the global navigation structure for an activity
+     *
+     * @return int
+     */
+    protected function load_for_activity() {
+        global $PAGE, $DB;
+        $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;
+            }
+        }
+
+        $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,$section->section);
+        $depth += $this->load_activity($keys);
+        return $depth;
+    }
+
+    /**
+     * 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($keys) {
+        global $DB, $CFG, $PAGE;
+
+        $module = $DB->get_record('modules', array('id'=>$PAGE->cm->module));
+        if (!$module) {
+            echo "Invalid Module ID";
+            return;
+        }
+
+        $this->context = $PAGE->course->context;
+
+        $file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php';
+        $function = $module->name.'_extend_navigation';
+
+        if (file_exists($file)) {
+            require_once($file);
+            if (function_exists($function)) {
+                $node = $this->find_child($PAGE->cm->id, self::TYPE_ACTIVITY);
+                if ($node) {
+                    $node->make_active();
+                    $function(&$node, $PAGE->course, $module, $PAGE->cm);
+                }
+            }
+        }
+    }
+
+    /**
+     * 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) {
+        global $CFG;
+        if (is_array($categories) && count($categories)>0) {
+            foreach ($categories as $category) {
+                $url = new moodle_url($CFG->wwwroot.'/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);
+                }
+            }
+        }
+    }
+
+    /**
+     * This function adds a category to the nav tree based on the categories path
+     * 
+     * @param stdClass $category
+     */
+    protected function add_category_by_path($category) {
+        global $CFG;
+        $url = new moodle_url($CFG->wwwroot.'/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey()));
+        $keys = explode('/',trim($category->path,'/ '));
+        $currentcategory = array_pop($keys);
+        $categorykey = $this->add_to_path($keys,  $category->id, $category->name, $category->name, self::TYPE_CATEGORY, $url);
+        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 $CFG, $OUTPUT, $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($CFG->wwwroot.'/course/view.php', array('id'=>$course->id));
+                if ($categoryid===null) {
+                    $category = $this->find_child($course->category);
+                } else {
+                    $category = $this->find_child($categoryid);
+                }
+                if ($category!==false) {
+                    $coursekey = $category->add($course->fullname, $course->shortname, $course->id, self::TYPE_COURSE, $url, $OUTPUT->old_icon_url('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, $OUTPUT;
+        if ($course===null) {
+            $course = $PAGE->course;
+        }
+        if (is_object($course)) {
+            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;
+            }
+            $url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id'=>$course->id));
+            $keys[] = $this->add_to_path($keys, $course->id, $course->fullname, $course->shortname, self::TYPE_COURSE, $url, $OUTPUT->old_icon_url('i/course'));
+            $currentcourse = $this->find_child($course->id, self::TYPE_COURSE);
+            if ($currentcourse!==false){
+                $currentcourse->make_active();
+                if (!$course->visible) {
+                    $currentcourse->hidden = true;
+                }
+            }
+
+            if (!$this->can_display_type(self::TYPE_SECTION)) {
+                if ($currentcourse!==false) {
+                    $currentcourse->nodetype = self::NODETYPE_LEAF;
+                }
+                return true;
+            }
+        }
+    }
+    /**
+     * Loads the sections for a 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
+     *
+     * @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
+     * @return bool
+     */
+    public function add_course_section_generic(&$keys, $course=null, $name=null, $activeparam = null) {
+        global $PAGE, $CFG, $OUTPUT;
+
+        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->cached('modinfo'.$course->id)) {
+            $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;
+
+        $selectedstructure = optional_param($activeparam,false,PARAM_INT);
+        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($CFG->wwwroot.'/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;
+    }
+
+    /**
+     * 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, $CFG, $DB;
+        $categories = $PAGE->categories;
+        if (is_array($categories) && count($categories)>0) {
+            $categories = array_reverse($categories);
+            foreach ($categories as $category) {
+                $url = new moodle_url($CFG->wwwroot.'/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);
+            }
+        }
+        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 $PAGE, $CFG, $DB, $USER;
+
+        // Cache capability moodle/site:config we use this in the next bit of code
+        if (!$this->cache->cached('hassiteconfig')) {
+            $this->cache->hassiteconfig = has_capability('moodle/site:config', 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() && !$this->cache->hassiteconfig && !isguest() && 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);
+                }
+                // Get the available courses using get_user_courses_bycap
+                $this->cache->availablecourses = get_user_courses_bycap($USER->id, 'moodle/course:view',
+                                                                        $accessinfo, true, 'c.sortorder ASC',
+                                                                        array('fullname','visible', 'category'),
+                                                                        FRONTPAGECOURSELIMIT);
+            }
+            // Cache the available courses for a refresh
+            $courses = $this->cache->availablecourses;
+        }
+
+        // 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)) {
+                $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);
+
+        // 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 {
+            $keys = array();
+            if ($categoryid!=0) {
+                if (!$this->cache->cached('category'.$categoryid)) {
+                    $this->cache->{'category'.$categoryid} = $DB->get_record('course_categories', array('id' => $categoryid), 'id,name,path');
+                }
+                $category = $this->cache->{'category'.$categoryid};
+                if ($category!=false) {
+                    $keys = explode('/',trim($category->path,'/ '));
+                    $categories = $DB->get_records_select('course_categories', 'id IN ('.join(',', $keys).')', array(), 'path ASC, sortorder ASC');
+                    foreach ($categories as $category) {
+                        $this->add_category_by_path($category);
+                    }
+                }
+            }   
+            $categories = $DB->get_records('course_categories', array('parent' => $categoryid), 'sortorder ASC');
+            $this->add_categories($keys, $categories);
+            #$courses = $DB->get_records('course', array('category' => $categoryid), 'sortorder ASC', 'id,fullname,shortname,visible,category'); 
+            $this->add_courses($courses, $categoryid);
+        }
+        return 0;
+    }
+}
+
+/**
+ * The limited global navigation class used for the AJAX extension of the global
+ * navigation class.
+ *
+ * The primary methods that are used in the global navigation class have been overriden
+ * to ensure that only the relevant branch is generated at the root of the tree.
+ * This can be done because AJAX is only used when the backwards structure for the
+ * requested branch exists.
+ * This has been done only because it shortens the amounts of information that is generated
+ * which of course will speed up the response time.. because no one likes laggy AJAX.
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class limited_global_navigation extends global_navigation {
+    /**
+     * 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
+     *
+     * @param int $type What to load for e.g. TYPE_SYSTEM
+     * @param int $instanceid The instance id for what ever is being loaded
+     * @return array An array of nodes that are expandable by AJAX
+     */
+    public function initialise($type, $instanceid) {
+        if ($this->initialised || during_initial_install()) {
+            return true;
+        }
+        $depth = 0;
+        switch ($type) {
+            case self::TYPE_CATEGORY: 
+                $depth = $this->load_category($instanceid);
+                break;
+            case self::TYPE_COURSE: 
+                $depth = $this->load_course($instanceid);
+                break;
+            case self::TYPE_SECTION: 
+                $depth = $this->load_section($instanceid);
+                break;
+            case self::TYPE_ACTIVITY: 
+                $depth = $this->load_activity($instanceid);
+                break;
+        }
+        $this->collapse_at_depth($this->depthforward+$depth);
+        $this->respect_forced_open();
+        $expandable = array();
+        $this->find_expandable($expandable);
+        $this->expandable = $expandable;
+        $this->initialised = true;
+        return $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('coursecontext'.$instanceid)) {
+            $this->cache->{'coursecontext'.$instanceid} = get_context_instance(CONTEXT_COURSE, $instanceid);
+        }
+        $this->context = $this->cache->{'coursecontext'.$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)) {
+            $this->cache->{'course'.$instanceid} = $DB->get_record('course', array('id'=>$instanceid));
+        }
+        $course = $this->cache->{'course'.$instanceid};
+
+        if (!$course) {
+            echo "Invalid Course ID";
+            break;
+        }
+
+        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 (!$this->cache->cached('course'.$course->id.'section0')) {
+            $this->cache->{'course'.$course->id.'section0'} = $DB->get_record('course_sections', array('course'=>$course->id, 'section'=>'0'));
+        }
+        $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));
+        
+        if (!$section) {
+            echo "Invalid Course Section ID";
+        }
+
+        if (!$this->cache->cached('course'.$section->course)) {
+            $this->cache->{'course'.$section->course} = $DB->get_record('course', array('id'=>$section->course));
+        }
+        $course = $this->cache->{'course'.$section->course};
+        if (!$course) {
+            echo "Invalid Course ID";
+        }
+
+        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 = null) {
+        global $PAGE, $CFG;
+        if ($name === null) {
+            $name = get_string('topic');
+        }
+
+        if ($activeparam === null) {
+            $activeparam = 'topic';
+        }
+
+        if (!$this->cache->cached('canviewhiddensections')) {
+            $this->cache->canviewhiddensections = has_capability('moodle/course:viewhiddensections', $this->context);
+        }
+        $viewhiddensections = $this->cache->canviewhiddensections;
+
+        $selectedstructure = optional_param($activeparam,false,PARAM_INT);
+        if (!$viewhiddensections && !$section->visible) {
+            continue;
+        }
+        if ($section->section!=0) {
+            $url = new moodle_url($CFG->wwwroot.'/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 $OUTPUT, $CFG;
+        if (!is_object($section)) {
+            return;
+        }
+        if ($section->section=='0') {
+            $keys = array($section->course);
+        } else {
+            $keys = array($section->id);
+        }
+
+        $modinfo = get_fast_modinfo($course);
+        
+        $resources = array('resource', 'label');
+
+        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 (!in_array($module->modname, $resources)) {
+                if ($module->icon=='') {
+                    $icon = $OUTPUT->mod_icon_url('icon', $module->modname);
+                }
+                $url = new moodle_url($CFG->wwwroot.'/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                $type = navigation_node::TYPE_ACTIVITY;
+            } else {
+                $url = null;
+                $type = navigation_node::TYPE_RESOURCE;
+                if ($module->modname!='label') {
+                    $url = new moodle_url($CFG->wwwroot.'/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
+                }
+                if ($module->icon!=='') {
+                    $icon = $OUTPUT->old_icon_url(preg_replace('#\.(png|gif)$#i','',$module->icon));
+                }
+            }
+            $this->add_to_path($keys, $module->id, $module->name, $module->name, $type, $url, $icon);
+            $child = $this->find_child($module->id, $type);
+            if ($child != false) {
+                $child->title(get_string($module->modname, $module->modname));
+                if (!$module->visible) {
+                    $child->hidden = true;
+                }
+                if ($type==navigation_node::TYPE_ACTIVITY && $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 $DB, $CFG;
+        $cm = $DB->get_record('course_modules', array('id'=>$instanceid));
+        if (!$cm) {
+            echo "Invalid Course Module ID";
+            return;
+        }
+        $module = $DB->get_record('modules', array('id'=>$cm->module));
+        if (!$module) {
+            echo "Invalid Module ID";
+            return;
+        }
+        $course = $DB->get_record('course', array('id'=>$cm->course));
+        if (!$course) {
+            echo "Invalid Course ID";
+            return;
+        }
+        $this->context = get_context_instance(CONTEXT_COURSE, $course->id);
+
+        $key = $this->add($module->name, null, $instanceid, self::TYPE_ACTIVITY);
+
+        $file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php';
+        $function = $module->name.'_extend_navigation';
+
+        if (file_exists($file)) {
+            require_once($file);
+            if (function_exists($function)) {
+                $node = $this->get($key);
+                $function(&$node, $course, $module, $cm);
+            }
+        }
+    }
+}
+
+/**
+ * Navbar class
+ *
+ * This class is used to manage the navbar, which is initialised from the navigation
+ * object held by PAGE
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class navbar extends navigation_node {
+    /** @var bool */
+    protected $initialised = false;
+    /** @var mixed */
+    protected $keys = array();
+    /** @var null|string */
+    protected $content = null;
+    /** @var page object */
+    protected $page;
+    /** @var bool */
+    protected $duringinstall = false;
+
+    /**
+     * The almighty constructor
+     */
+    public function __construct(&$page) {
+        global $SITE, $CFG;
+        if (during_initial_install()) {
+            $this->duringinstall = true;
+            return false;
+        }
+        $this->page = $page;
+        $this->text = get_string('home');
+        $this->shorttext = get_string('home');
+        $this->action = new moodle_url($CFG->wwwroot);
+        $this->nodetype = self::NODETYPE_BRANCH;
+        $this->type = self::TYPE_SYSTEM;
+    }
+
+    /**
+     * Quick check to see if the navbar will have items in.
+     *
+     * @return bool Returns true if the navbar will have items, false otherwise
+     */
+    public function has_items() {
+        if ($this->duringinstall) {
+            return false;
+        }
+        $this->page->navigation->initialise();
+        return (count($this->page->navbar->children)>0 || $this->page->navigation->contains_active_node() || $this->page->settingsnav->contains_active_node());
+    }
+
+    /**
+     * 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
+     */
+    public function content() {
+        if ($this->duringinstall) {
+            return '';
+        }
+
+        // Make sure that navigation is initialised
+        $this->page->navigation->initialise();
+
+        if ($this->content !== null) {
+            return $this->content;
+        }
+
+        // For screen readers
+        $output = get_accesshide(get_string('youarehere','access'), 'h2')."<ul>\n";
+        
+        $firstnode = true;
+        // Check if navigation contains the active node
+        if ($this->page->navigation->contains_active_node()) {
+            // Parse the navigation tree to get the active node
+            $output .= $this->parse_branch_to_html($this->page->navigation->children);
+            $firstnode = false;
+        } else if ($this->page->settingsnav->contains_active_node()) {
+            // Parse the settings navigation to get the active node
+            $output .= $this->parse_branch_to_html($this->page->settingsnav->children);
+            $firstnode = false;
+        }
+
+        // Check if there are any children added by code
+        if (count($this->children)>0) {
+            // Add the custom children
+            $output .= $this->parse_branch_to_html($this->children,$firstnode);
+        }
+        $output .= "</ul>\n";
+        $this->content = $output;
+        return $output;
+    }
+    /**
+     * 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) {
+        $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 .= '<li class="first">'.parent::content(true).'</li>';
+        }
+        $count = 0;
+        // 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) {
+                return $output;
+            }
+            $child = false;
+            // Iterate the nodes in navarray and finde 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;
+                // Now display the node
+                $output .= '<li>'.$separator.' '.$child->content(true).'</li>';
+            }
+        }
+        // XHTML
+        return $output;
+    }
+    /**
+     * Add a new 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
+     * end of the navbar
+     *
+     * @param string $text
+     * @param string $shorttext
+     * @param string|int $key
+     * @param int $type
+     * @param string|moodle_url $action
+     * @param string $icon
+     * @return string|int Identifier for this particular node
+     */
+    public function add($text, $shorttext=null, $key=null, $type=self::TYPE_CUSTOM, $action=null, $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, $shorttext, $key, $type, $action, $icon);
+        } else {
+            $key = $this->add_to_path($this->keys, $key, $text, $shorttext, $type, $action, $icon);
+        }
+        $this->keys[] = $key;
+        $child = $this->get_by_path($this->keys);
+        if ($child!==false) {
+            $child->forceopen = true;
+        }
+        return $key;
+    }
+}
+
+/**
+ * Class used to manage the settings option for the current page
+ *
+ * This class is used to manage the settings options in a tree format (recursively)
+ * and was created initially for use with the settings blocks.
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class settings_navigation extends navigation_node {
+    /** @var stdClass */
+    protected $context;
+    /** @var cache */
+    protected $cache;
+    /** @var page object */
+    protected $page;
+    /**
+     * Sets up the object with basic settings and preparse it for use
+     */
+    public function __construct(&$page) {
+        if (during_initial_install()) {
+            return false;
+        }
+        static $settingsnavcount;
+        $this->page = $page;
+        // Initialise the main navigation. It is most important that this is done
+        // before we try anything
+        $this->page->navigation->initialise();
+        // Initialise the navigation cache
+        $this->cache = new navigation_cache('navigation');
+    }
+    /**
+     * 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.
+     */
+    public function initialise() {
+        global $SITE, $OUTPUT, $CFG, $ME;
+        if (during_initial_install()) {
+            return false;
+        }
+        $this->id = 'settingsnav';
+        $this->context = $this->page->context;
+        switch ($this->context->contextlevel) {
+            case CONTEXT_SYSTEM:
+                $adminkey = $this->load_administration_settings();
+                $settingskey = $this->load_user_settings(SITEID);
+                break;
+            case CONTEXT_COURSECAT:
+                $adminkey = $this->load_administration_settings();
+                $adminnode = $this->get($adminkey);
+                if ($adminnode!==false) {
+                    $adminnode->forceopen =  true;
+                }
+                $settingskey = $this->load_user_settings(SITEID);
+                break;
+            case CONTEXT_COURSE:
+                if ($this->page->course->id!==SITEID) {
+                    $coursekey = $this->load_course_settings();
+                    $coursenode = $this->get($coursekey);
+                    if ($coursenode!==false) {
+                        $coursenode->forceopen =  true;
+                    }
+                    $settingskey = $this->load_user_settings($this->page->course->id);
+                    $adminkey = $this->load_administration_settings();
+                } else {
+                    $this->load_front_page_settings();
+                    $settingskey = $this->load_user_settings($SITE->id);
+                    $adminkey = $this->load_administration_settings();
+                }
+                break;
+            case CONTEXT_MODULE:
+                $modulekey = $this->load_module_settings();
+                $modulenode = $this->get($modulekey);
+                if ($modulenode!==false) {
+                    $modulenode->forceopen =  true;
+                }
+                $coursekey = $this->load_course_settings();
+                $settingskey = $this->load_user_settings($this->page->course->id);
+                $adminkey = $this->load_administration_settings();
+                break;
+            case CONTEXT_USER:
+                $settingskey = $this->load_user_settings($this->page->course->id);
+                $settingsnode = $this->get($settingskey);
+                if ($settingsnode!==false) {
+                    $settingsnode->forceopen =  true;
+                }
+                if ($this->page->course->id!==SITEID) {
+                    $coursekey = $this->load_course_settings();
+                }
+                $adminkey = $this->load_administration_settings();
+                break;
+            default:
+                debugging('An unknown context has passed into settings_navigation::initialise', DEBUG_DEVELOPER);
+                break;
+        }
+
+        // Check if the user is currently logged in as another user
+        if (session_is_loggedinas()) {
+            // Get the actual user, we need this so we can display an informative return link
+            $realuser = session_get_realuser();
+            // Add the informative return to original user link
+            $url = new moodle_url($CFG->wwwroot.'/course/loginas.php',array('id'=>$this->page->course->id, 'return'=>1,'sesskey'=>sesskey()));
+            $this->add(get_string('returntooriginaluser', 'moodle', fullname($realuser, true)), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('t/left'));
+        }
+
+        // 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();
+    }
+    /**
+     * Override the parent function so that we can add preceeding hr's and set a
+     * root node class against all first level element
+     *
+     * It does this by first calling the parent's add method {@link navigation_node::add()}
+     * and then proceeds to use the key to set class and hr 
+     *
+     * @param string $text
+     * @param string $shorttext
+     * @param string|int $key
+     * @param int $type
+     * @param sting|moodle_url $url
+     * @param string $icon
+     * @return sting|int A key that can be used to reference the newly added node
+     */
+    public function add($text, $shorttext=null, $key=null, $type=null, $url=null, $icon=null) {
+        $key = parent::add($text, $shorttext, $key, $type, $url, $icon);
+        $this->get($key)->add_class('root_node');
+        $this->get($key)->preceedwithhr = true;
+        return $key;
+    }
+    /**
+     * Load the site administration tree
+     *
+     * This function loads the site administration tree by using the lib/adminlib library functions
+     *
+     * @param navigation_node $referencebranch A reference to a branch in the settings
+     *      navigation tree
+     * @param null|object $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
+     */
+    protected function load_administration_settings($referencebranch=null, $adminbranch=null) {
+        global $CFG, $OUTPUT, $FULLME, $ME;
+        // Check if we are just starting to generate this navigation.
+        if ($referencebranch === null) {
+            // Check if we have cached an appropriate generation of the admin branch
+            if (!$this->cache->cached('adminbranch')) {
+                // We dont have a cached admin branch for this page so regenerate
+                if (!function_exists('admin_get_root')) {
+                    require_once($CFG->dirroot.'/lib/adminlib.php');
+                }
+                $adminroot = admin_get_root();
+                $branchkey = $this->add(get_string('administrationsite'),null, null, self::TYPE_SETTING);
+                $referencebranch = $this->get($branchkey);
+                foreach ($adminroot->children as $adminbranch) {
+                    $this->load_administration_settings(&$referencebranch, $adminbranch);
+                }
+                $this->cache->adminbranch = $this->get($branchkey);
+            } else {
+                // We have a cached admin branch so we simply need to stick it back in the tree
+                $adminbranch = $this->cache->adminbranch;
+                $outcome = $adminbranch->reiterate_active_nodes();
+                $branchkey = count($this->children);
+                $adminbranch->key = $branchkey;
+                $this->nodetype = self::NODETYPE_BRANCH;
+                $this->children[$branchkey] = $adminbranch;
+            }
+            // Return the branch key
+            return $branchkey;
+        } else if ($adminbranch->check_access() && !$adminbranch->is_hidden()) {
+            // 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
+            $url = null;
+            $icon = null;
+            if ($adminbranch instanceof admin_settingpage) {
+                $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/settings.php', array('section'=>$adminbranch->name));
+            } else if ($adminbranch instanceof admin_externalpage) {
+                $url = $adminbranch->url;
+            }
+
+            // Add the branch
+            $branchkey = $referencebranch->add($adminbranch->visiblename, null, null, self::TYPE_SETTING, $url, $icon);
+            $reference = $referencebranch->get($branchkey);
+            // Check if we are generating the admin notifications and whether notificiations exist
+            if ($adminbranch->name === 'adminnotifications' && admin_critical_warnings_present()) {
+                $reference->add_class('criticalnotification');
+            }
+            // Check if this branch has children
+            if ($reference && isset($adminbranch->children) && is_array($adminbranch->children) && count($adminbranch->children)>0) {
+                foreach ($adminbranch->children as $branch) {
+                    // Generate the child branches as well now using this branch as the reference
+                    $this->load_administration_settings(&$reference, $branch);
+                }
+            } else {
+                $reference->icon = $OUTPUT->old_icon_url('i/settings');
+            }
+        }
+    }
+
+    /**
+     * Generate the list of modules for the given course.
+     *
+     * The array of resources and activities that can be added to a course is then
+     * stored in the cache so that we can access it for anywhere.
+     * It saves us generating it all the time
+     *
+     * <code php>
+     * // To get resources:
+     * $this->cache->{'course'.$courseid.'resources'}
+     * // To get activities:
+     * $this->cache->{'course'.$courseid.'activities'}
+     * </code>
+     *
+     * @param stdClass $course The course to get modules for
+     */
+    protected function get_course_modules($course) {
+        global $CFG;
+        $mods = $modnames = $modnamesplural = $modnamesused = array();
+        // This function is included when we include course/lib.php at the top
+        // of this file
+        get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
+        $resources = array();
+        $activities = array();
+        foreach($modnames as $modname=>$modnamestr) {
+            if (!course_allowed_module($course, $modname)) {
+                continue;
+            }
+
+            $libfile = "$CFG->dirroot/mod/$modname/lib.php";
+            if (!file_exists($libfile)) {
+                continue;
+            }
+            include_once($libfile);
+            $gettypesfunc =  $modname.'_get_types';
+            if (function_exists($gettypesfunc)) {
+                $types = $gettypesfunc();
+                foreach($types as $type) {
+                    if (!isset($type->modclass) || !isset($type->typestr)) {
+                        debugging('Incorrect activity type in '.$modname);
+                        continue;
+                    }
+                    if ($type->modclass == MOD_CLASS_RESOURCE) {
+                        $resources[html_entity_decode($type->type)] = $type->typestr;
+                    } else {
+                        $activities[html_entity_decode($type->type)] = $type->typestr;
+                    }
+                }
+            } else {
+                $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
+                if ($archetype == MOD_ARCHETYPE_RESOURCE) {
+                    $resources[$modname] = $modnamestr;
+                } else {
+                    // all other archetypes are considered activity
+                    $activities[$modname] = $modnamestr;
+                }
+            }
+        }
+        $this->cache->{'course'.$course->id.'resources'} = $resources;
+        $this->cache->{'course'.$course->id.'activities'} = $activities;
+    }
+
+    /**
+     * This function loads the course settings that are available for the user
+     *
+     * @return bool|mixed Either false of a key to access the course tree by
+     */
+    protected function load_course_settings() {
+        global $CFG, $OUTPUT, $USER, $SESSION;
+
+        $course = $this->page->course;
+        if (empty($course->context)) {
+            if (!$this->cache->cached('coursecontext'.$course->id)) {
+                $this->cache->{'coursecontext'.$course->id} = get_context_instance(CONTEXT_COURSE, $course->id);   // Course context
+            }
+            $course->context = $this->cache->{'coursecontext'.$course->id};
+        }
+        if (!$this->cache->cached('canviewcourse'.$course->id)) {
+            $this->cache->{'canviewcourse'.$course->id} = has_capability('moodle/course:view', $course->context);
+        }
+        if ($course->id === SITEID || !$this->cache->{'canviewcourse'.$course->id}) {
+            return false;
+        }
+
+        $coursenode = $this->page->navigation->find_child($course->id, global_navigation::TYPE_COURSE);
+        
+        $coursenodekey = $this->add(get_string('courseadministration'), null, null, $coursenode->type);
+        $coursenode = $this->get($coursenodekey);
+        
+        if (has_capability('moodle/course:update', $course->context)) {
+            // Add the turn on/off settings
+            $url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
+            if ($this->page->user_is_editing()) {
+                $url->param('edit', 'off');
+                $editstring = get_string('turneditingoff');
+            } else {
+                $url->param('edit', 'on');
+                $editstring = get_string('turneditingon');
+            }
+            $coursenode->add($editstring, null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/edit'));
+
+
+            if ($this->page->user_is_editing()) {
+                // Add `add` resources|activities branches
+                $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php';
+                if (file_exists($structurefile)) {
+                    require_once($structurefile);
+                    $formatstring = call_user_func('callback_'.$course->format.'_definition');
+                    $formatidentifier = optional_param(call_user_func('callback_'.$course->format.'_request_key'), 0, PARAM_INT);
+                } else {
+                    $formatstring = get_string('topic');
+                    $formatidentifier = optional_param('topic', 0, PARAM_INT);
+                }
+                if (!$this->cache->cached('coursesections'.$course->id)) {
+                    $this->cache->{'coursesections'.$course->id} = get_all_sections($course->id);
+                }
+                $sections = $this->cache->{'coursesections'.$course->id};
+                
+                $addresource = $this->get($this->add(get_string('addresource')));
+                $addactivity = $this->get($this->add(get_string('addactivity')));
+                if ($formatidentifier!==0) {
+                    $addresource->forceopen = true;
+                    $addactivity->forceopen = true;
+                }
+
+                if (!$this->cache->cached('course'.$course->id.'resources')) {
+                    $this->get_course_modules(&$course);
+                }
+                $resources = $this->cache->{'course'.$course->id.'resources'};
+                $activities = $this->cache->{'course'.$course->id.'activities'};
+                
+                foreach ($sections as $section) {
+                    if ($formatidentifier !== 0 && $section->section != $formatidentifier) {
+                        continue;
+                    }
+                    $sectionurl = new moodle_url($CFG->wwwroot.'/course/view.php', array('id'=>$course->id, $formatstring=>$section->section));
+                    if ($section->section == 0) {
+                        $sectionresources = $addresource->add(get_string('course'), null, null, self::TYPE_SETTING, $sectionurl);
+                        $sectionactivities = $addactivity->add(get_string('course'), null, null, self::TYPE_SETTING, $sectionurl);
+                    } else {
+                        $sectionresources = $addresource->add($formatstring.' '.$section->section, null, null, self::TYPE_SETTING, $sectionurl);
+                        $sectionactivities = $addactivity->add($formatstring.' '.$section->section, null, null, self::TYPE_SETTING, $sectionurl);
+                    }
+                    foreach ($resources as $value=>$resource) {
+                        $url = new moodle_url($CFG->wwwroot.'/course/mod.php', array('id'=>$course->id, 'sesskey'=>sesskey(), 'section'=>$section->section));
+                        $pos = strpos($value, '&type=');
+                        if ($pos!==false) {
+                            $url->param('add', substr($value, 0,$pos));
+                            $url->param('type', substr($value, $pos+6));
+                        } else {
+                            $url->param('add', $value);
+                        }
+                        $addresource->get($sectionresources)->add($resource, null, null, self::TYPE_SETTING, $url);
+                    }
+                    $subbranch = false;
+                    foreach ($activities as $activityname=>$activity) {
+                        if ($activity==='--') {
+                            $subbranch = false;
+                            continue;
+                        }
+                        if (strpos($activity, '--')===0) {
+                            $subbranch = $addactivity->get($sectionactivities)->add(trim($activity, '-'));
+                            continue;
+                        }
+                        $url = new moodle_url($CFG->wwwroot.'/course/mod.php', array('id'=>$course->id, 'sesskey'=>sesskey(), 'section'=>$section->section));
+                        $pos = strpos($activityname, '&type=');
+                        if ($pos!==false) {
+                            $url->param('add', substr($activityname, 0,$pos));
+                            $url->param('type', substr($activityname, $pos+6));
+                        } else {
+                            $url->param('add', $activityname);
+                        }
+                        if ($subbranch !== false) {
+                            $addactivity->get($sectionactivities)->get($subbranch)->add($activity, null, null, self::TYPE_SETTING, $url);
+                        } else {
+                            $addactivity->get($sectionactivities)->add($activity, null, null, self::TYPE_SETTING, $url);
+                        }
+                    }
+                }
+            }
+
+            // Add the course settings link
+            $url = new moodle_url($CFG->wwwroot.'/course/edit.php', array('id'=>$course->id));
+            $coursenode->add(get_string('settings'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/settings'));
+        }
+        
+        // Add assign or override roles if allowed
+        if (has_capability('moodle/role:assign', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$course->context->id));
+            $coursenode->add(get_string('assignroles', 'role'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/roles'));
+        } else if (get_overridable_roles($course->context, ROLENAME_ORIGINAL)) {
+            $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/override.php', array('contextid'=>$course->context->id));
+            $coursenode->add(get_string('overridepermissions', 'role'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/roles'));
+        }
+
+        // Add view grade report is permitted
+        $reportavailable = false;
+        if (has_capability('moodle/grade:viewall', $course->context)) {
+            $reportavailable = true;
+        } else if (!empty($course->showgrades)) {
+            $reports = get_plugin_list('gradereport');
+            if (is_array($reports) && count($reports)>0) {     // Get all installed reports
+                arsort($reports); // user is last, we want to test it first
+                foreach ($reports as $plugin => $plugindir) {
+                    if (has_capability('gradereport/'.$plugin.':view', $course->context)) {
+                        //stop when the first visible plugin is found
+                        $reportavailable = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if ($reportavailable) {
+            $url = new moodle_url($CFG->wwwroot.'/grade/report/index.php', array('id'=>$course->id));
+            $coursenode->add(get_string('grades'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/grades'));
+        }
+
+        //  Add outcome if permitted
+        if (!empty($CFG->enableoutcomes) && has_capability('moodle/course:update', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/grade/edit/outcome/course.php', array('id'=>$course->id));
+            $coursenode->add(get_string('outcomes', 'grades'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/outcomes'));
+        }
+
+        // Add meta course links
+        if ($course->metacourse) {
+            if (has_capability('moodle/course:managemetacourse', $course->context)) {
+                $url = new moodle_url($CFG->wwwroot.'/course/importstudents.php', array('id'=>$course->id));
+                $coursenode->add(get_string('childcourses'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/course'));
+            } else if (has_capability('moodle/role:assign', $course->context)) {
+                $key = $coursenode->add(get_string('childcourses'), null, null, self::TYPE_SETTING, null, $OUTPUT->old_icon_url('i/course'));
+                $coursenode->get($key)->hidden = true;;
+            }
+        }
+
+        // Manage groups in this course
+        if (($course->groupmode || !$course->groupmodeforce) && has_capability('moodle/course:managegroups', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/group/index.php', array('id'=>$course->id));
+            $coursenode->add(get_string('groups'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/group'));
+        }
+
+        //Participants
+        if (has_capability('moodle/course:viewparticipants', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/user/index.php?contextid='.$course->context->id);
+            $coursenode->add(get_string('participants'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/users'));
+        }
+
+        // Backup this course
+        if (has_capability('moodle/site:backup', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/backup/backup.php', array('id'=>$course->id));
+            $coursenode->add(get_string('backup'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/backup'));
+        }
+
+        // Restore to this course
+        if (has_capability('moodle/site:restore', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/files/index.php', array('id'=>$course->id, 'wdir'=>'/backupdata'));
+            $coursenode->add(get_string('restore'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/restore'));
+        }
+
+        // Import data from other courses
+        if (has_capability('moodle/site:import', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/course/import.php', array('id'=>$course->id));
+            $coursenode->add(get_string('import'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/restore'));
+        }
+
+        // Reset this course
+        if (has_capability('moodle/course:reset', $course->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/course/reset.php', array('id'=>$course->id));
+            $coursenode->add(get_string('reset'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/return'));
+        }
+        
+        // View course reports
+        if (has_capability('moodle/site:viewreports', $course->context)) { // basic capability for listing of reports
+            $url = new moodle_url($CFG->wwwroot.'/course/report.php', array('id'=>$course->id));
+            $coursenode->add(get_string('reports'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/stats'));
+        }
+
+        // Manage questions
+        $questioncaps = array('moodle/question:add',
+                              'moodle/question:editmine',
+                              'moodle/question:editall',
+                              'moodle/question:viewmine',
+                              'moodle/question:viewall',
+                              'moodle/question:movemine',
+                              'moodle/question:moveall');
+        if (has_any_capability($questioncaps, $this->context)) {
+            $questionlink = $CFG->wwwroot.'/question/edit.php';
+        } else if (has_capability('moodle/question:managecategory', $this->context)) {
+            $questionlink = $CFG->wwwroot.'/question/category.php';
+        }
+        if (isset($questionlink)) {
+            $url = new moodle_url($questionlink, array('courseid'=>$course->id));
+            $coursenode->add(get_string('questions','quiz'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/questions'));
+        }
+
+        // Repository Instances
+        require_once($CFG->dirroot.'/repository/lib.php');
+        $editabletypes = repository::get_editable_types($this->context);
+        if (has_capability('moodle/course:update', $this->context) && !empty($editabletypes)) {
+            $url = new moodle_url($CFG->wwwroot.'/repository/manage_instances.php', array('contextid'=>$this->context->id));
+            $coursenode->add(get_string('repositories'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/repository'));
+        }
+
+        // Manage files
+        if (has_capability('moodle/course:managefiles', $this->context)) {
+            $url = new moodle_url($CFG->wwwroot.'/files/index.php', array('id'=>$course->id));
+            $coursenode->add(get_string('files'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/files'));
+        }
+
+        // Authorize hooks
+        if ($course->enrol == 'authorize' || (empty($course->enrol) && $CFG->enrol == 'authorize')) {
+            require_once($CFG->dirroot.'/enrol/authorize/const.php');
+            $url = new moodle_url($CFG->wwwroot.'/enrol/authorize/index.php', array('course'=>$course->id));
+            $coursenode->add(get_string('payments'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/payment'));
+            if (has_capability('enrol/authorize:managepayments', $this->page->context)) {
+                $cnt = $DB->count_records('enrol_authorize', array('status'=>AN_STATUS_AUTH, 'courseid'=>$course->id));
+                if ($cnt) {
+                    $url = new moodle_url($CFG->wwwroot.'/enrol/authorize/index.php', array('course'=>$course->id,'status'=>AN_STATUS_AUTH));
+                    $coursenode->add(get_string('paymentpending', 'moodle', $cnt), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/payment'));
+                }
+            }
+        }
+
+        // Unenrol link
+        if (empty($course->metacourse)) {
+            if (has_capability('moodle/legacy:guest', $this->context, NULL, false)) {   // Are a guest now
+                $url = new moodle_url($CFG->wwwroot.'/course/enrol.php', array('id'=>$course->id));
+                $coursenode->add(get_string('enrolme', '', format_string($course->shortname)), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/user'));
+            } else if (has_capability('moodle/role:unassignself', $this->context, NULL, false) && get_user_roles($this->context, 0, false)) {  // Have some role
+                $url = new moodle_url($CFG->wwwroot.'/course/unenrol.php', array('id'=>$course->id));
+                $coursenode->add(get_string('unenrolme', '', format_string($course->shortname)), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/user'));
+            }
+        }
+
+        // Link to the user own profile (except guests)
+        if (!isguestuser() and isloggedin()) {
+            $url = new moodle_url($CFG->wwwroot.'/user/view.php', array('id'=>$USER->id, 'course'=>$course->id));
+            $coursenode->add(get_string('profile'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/user'));
+        }
+
+        // Switch roles
+        $roles = array();
+        $assumedrole = $this->in_alternative_role();
+        if ($assumedrole!==false) {
+            $roles[0] = get_string('switchrolereturn');
+        }
+        if (has_capability('moodle/role:switchroles', $this->context)) {
+            $availableroles = get_switchable_roles($this->context);
+            if (is_array($availableroles)) {
+                foreach ($availableroles as $key=>$role) {
+                    if ($key == $CFG->guestroleid || $assumedrole===(int)$key) {
+                        continue;
+                    }
+                    $roles[$key] = $role;
+                }
+            }
+        }
+        if (is_array($roles) && count($roles)>0) {
+            $switchroleskey = $this->add(get_string('switchroleto'));
+            if ((count($roles)==1 && array_key_exists(0, $roles))|| $assumedrole!==false) {
+                $this->get($switchroleskey)->forceopen = true;
+            }
+            $returnurl = $this->page->url;
+            $returnurl->param('sesskey', sesskey());
+            $SESSION->returnurl = serialize($returnurl);
+            foreach ($roles as $key=>$name) {
+                $url = new moodle_url($CFG->wwwroot.'/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>$key, 'returnurl'=>'1'));
+                $this->get($switchroleskey)->add($name, null, $key, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/roles'));
+            }
+        }
+        // Return we are done
+        return $coursenodekey;
+    }
+
+    /**
+     * This function calls the module function to inject module settings into the
+     * settings navigation tree.
+     *
+     * This only gets called if there is a corrosponding function in the modules
+     * lib file.
+     *
+     * For examples mod/forum/lib.php ::: forum_extend_settings_navigation()
+     *
+     * @return void|mixed The key to access the module method by
+     */
+    protected function load_module_settings() {
+        global $CFG, $DB;
+        $cm = $this->page->cm;
+        $module = $DB->get_record('modules', array('id'=>$cm->module));
+        if (!$module) {
+            return;
+        }
+
+        $file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php';
+        $function = $module->name.'_extend_settings_navigation';
+        
+        if (file_exists($file)) {
+            require_once($file);
+        }
+        if (!function_exists($function)) {
+            return;
+        }
+        return $function(&$this,$module);
+    }
+
+    /**
+     * Loads the user settings block of the settings nav
+     * 
+     * This function is simply works out the userid and whether we need to load
+     * just the current users profile settings, or the current user and the user the 
+     * current user is viewing.
+     * 
+     * This function has some very ugly code to work out the user, if anyone has
+     * any bright ideas please feel free to intervene.
+     *
+     * @param int $courseid The course id of the current course
+     */
+    protected function load_user_settings($courseid=SITEID) {
+        global $USER, $FULLME;
+
+        if (isguestuser() || !isloggedin()) {
+            return false;
+        }
+
+        // This is terribly ugly code, but I couldn't see a better way around it
+        // we need to pick up the user id, it can be the current user or someone else
+        // and the key depends on the current location
+        // Default to look at id
+        $userkey='id';
+        if ($this->context->contextlevel >= CONTEXT_COURSECAT && strpos($FULLME, '/message/')===false && strpos($FULLME, '/mod/forum/user')===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';
+        } else if (strpos($FULLME,'/blog/') || strpos($FULLME, '/roles/')) {
+            // And blog and roles just do thier own thing using `userid`
+            $userkey = 'userid';
+        }
+
+        $userid = optional_param($userkey, $USER->id, PARAM_INT);
+        if ($userid!=$USER->id) {
+            $this->generate_user_settings($courseid,$userid,'userviewingsettings');
+            $this->generate_user_settings($courseid,$USER->id);
+        } else {
+            $this->generate_user_settings($courseid,$USER->id);
+        }
+    }
+
+    /**
+     * This function gets called by {@link load_user_settings()} and actually works out
+     * what can be shown/done
+     *
+     * @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
+     */
+    protected function generate_user_settings($courseid, $userid, $gstitle='usercurrentsettings') {
+        global $DB, $CFG, $USER;
+
+        $course = $DB->get_record("course", array("id"=>$courseid));
+        if (!$course) {
+            return false;
+        }
+
+        $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);   // Course context
+        $systemcontext   = get_context_instance(CONTEXT_SYSTEM);
+        $currentuser = ($USER->id == $userid);
+        if ($currentuser) {
+            $user = $USER;
+            $usercontext = get_context_instance(CONTEXT_USER, $user->id);       // User context
+        } else {
+            $user = $DB->get_record('user', array('id'=>$userid));
+            if (!$user) {
+                return false;
+            }
+            // Check that the user can view the profile
+            $usercontext = get_context_instance(CONTEXT_USER, $user->id);       // User context
+            if ($course->id==SITEID) {
+                if ($CFG->forceloginforprofiles && !isteacherinanycourse() && !isteacherinanycourse($user->id) && !has_capability('moodle/user:viewdetails', $usercontext)) {  // Reduce possibility of "browsing" userbase at site level
+                    // Teachers can browse and be browsed at site level. If not forceloginforprofiles, allow access (bug #4366)
+                    return false;
+                }
+            } else {
+                if ((!has_capability('moodle/user:viewdetails', $coursecontext) && !has_capability('moodle/user:viewdetails', $usercontext)) || !has_capability('moodle/course:view', $coursecontext, $user->id, false)) {
+                    return false;
+                }
+                if (groups_get_course_groupmode($course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $coursecontext)) {
+                    // If groups are in use, make sure we can see that group
+                    return false;
+                }
+            }
+        }
+
+        $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->id = 'usersettings';
+
+        // URL to the users profile
+        $profileurl = new moodle_url($CFG->wwwroot.'/user/view.php', array('id'=>$user->id, 'course'=>$course->id));
+
+        // Check if the user has been deleted
+        if ($user->deleted) {
+            if (!has_capability('moodle/user:update', $coursecontext)) {
+                // We can't edit the user so just show the user deleted message
+                $usersetting->add(get_string('userdeleted'), null, null, self::TYPE_SETTING);
+            } else {
+                // We can edit the user so show the user deleted message and link it to the profile
+                $usersetting->add(get_string('userdeleted'), null, null, self::TYPE_SETTING, $profileurl);
+            }
+            return true;
+        }
+
+        // Add a link to view the user profile
+        if ($currentuser) {
+            $usersetting->add(get_string('viewmyprofile'), null, null, self::TYPE_SETTING, $profileurl);
+        } else {
+            $usersetting->add(get_string('userprofilefor','',$fullname), null, null, self::TYPE_SETTING, $profileurl);
+        }
+
+        // Add the profile edit link
+        if (isloggedin() && !isguestuser($user) && !is_mnet_remote_user($user)) {
+            $url = false;
+            if (($currentuser && has_capability('moodle/user:update', $systemcontext)) || (has_capability('moodle/user:update', $systemcontext) && !is_primary_admin($user->id))) {
+                $url = new moodle_url($CFG->wwwroot.'/user/editadvanced.php', array('id'=>$user->id, 'course'=>$course->id));
+            } else if ((has_capability('moodle/user:editprofile', $usercontext) && !is_primary_admin($user->id)) || ($currentuser && has_capability('moodle/user:editownprofile', $systemcontext))) {
+                $url = new moodle_url($CFG->wwwroot.'/user/edit.php', array('id'=>$user->id, 'course'=>$course->id));
+            }
+            if ($url!==false) {
+                $usersetting->add(get_string('editmyprofile'), null, null, self::TYPE_SETTING, $url);
+            }
+        }
+
+        // Change password link
+        if (!empty($user->auth)) {
+            $userauth = get_auth_plugin($user->auth);
+            if ($currentuser && !session_is_loggedinas() && $userauth->can_change_password() && !isguestuser() && has_capability('moodle/user:changeownpassword', $systemcontext)) {
+                $passwordchangeurl = $userauth->change_password_url();
+                if (!$passwordchangeurl) {
+                    if (empty($CFG->loginhttps)) {
+                        $wwwroot = $CFG->wwwroot;
+                    } else {
+                        $wwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
+                    }
+                    $passwordchangeurl = new moodle_url($CFG->wwwroot.'/login/change_password.php');
+                } else {
+                    $urlbits = explode($passwordchangeurl. '?', 1);
+                    $passwordchangeurl = new moodle_url($urlbits[0]);
+                    if (count($urlbits)==2 && preg_match_all('#\&([^\=]*?)\=([^\&]*)#si', '&'.$urlbits[1], $matches)) {
+                        foreach ($matches as $pair) {
+                            $fullmeurl->param($pair[1],$pair[2]);
+                        }
+                    }
+                }
+                $passwordchangeurl->param('id', $course->id);
+                $usersetting->add(get_string("changepassword"), null, null, self::TYPE_SETTING, $passwordchangeurl);
+            }
+        }
+
+        // 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, null, self::TYPE_SETTING);
+
+            $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/usersroles.php', array('userid'=>$user->id, 'courseid'=>$course->id));
+            $usersetting->get($roleskey)->add(get_string('thisusersroles', 'role'), null, null, self::TYPE_SETTING, $url);
+
+            $assignableroles = get_assignable_roles($usercontext, ROLENAME_BOTH);
+            $overridableroles = get_overridable_roles($usercontext, ROLENAME_BOTH);
+
+            if (!empty($assignableroles)) {
+                $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id));
+                $usersetting->get($roleskey)->add(get_string('assignrolesrelativetothisuser', 'role'), null, null, self::TYPE_SETTING, $url);
+            }
+
+            if (!empty($overridableroles)) {
+                $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/override.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id));
+                $usersetting->get($roleskey)->add(get_string('overridepermissions', 'role'), null, null, self::TYPE_SETTING, $url);
+            }
+
+            $url = new moodle_url($CFG->wwwroot.'/'.$CFG->admin.'/roles/check.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id));
+            $usersetting->get($roleskey)->add(get_string('checkpermissions', 'role'), null, null, self::TYPE_SETTING, $url);
+        }
+
+        // Portfolio
+        if (empty($userindexpage) && $currentuser && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', $systemcontext) && portfolio_instances(true, false)) {
+            $portfoliokey = $usersetting->add(get_string('portfolios', 'portfolio'), null, null, self::TYPE_SETTING);
+            $url = new moodle_url($CFG->wwwroot .'/user/portfolio.php');
+            $usersetting->get($portfoliokey)->add(get_string('configure', 'portfolio'), null, null, self::TYPE_SETTING, $url);
+            $url = new moodle_url($CFG->wwwroot .'/user/portfoliologs.php');
+            $usersetting->get($portfoliokey)->add(get_string('logs', 'portfolio'), null, null, self::TYPE_SETTING, $url);
+        }
+
+        // Repository
+        if (!$currentuser) {
+            require_once($CFG->dirroot . '/repository/lib.php');
+            $editabletypes = repository::get_editable_types($usercontext);
+            if ($usercontext->contextlevel == CONTEXT_USER && !empty($editabletypes)) {
+                $url = new moodle_url($CFG->wwwroot .'/repository/manage_instances.php', array('contextid'=>$usercontext->id));
+                $usersetting->add(get_string('repositories', 'repository'), null, null, self::TYPE_SETTING, $url);
+            }
+        }
+
+        // Messaging
+        if (empty($userindexpage) && has_capability('moodle/user:editownmessageprofile', $systemcontext)) {
+            $url = new moodle_url($CFG->wwwroot.'/message/edit.php', array('id'=>$user->id, 'course'=>$course->id));
+            $usersetting->add(get_string('editmymessage', 'message'), null, null, self::TYPE_SETTING, $url);
+        }
+
+        return $usersettingskey;
+    }
+
+    /**
+     * Determine whether the user is assuming another role
+     *
+     * This function checks to see if the user is assuming another role by means of
+     * role switching. In doing this we compare each RSW key (context path) against
+     * the current context path. This ensures that we can provide the switching
+     * options against both the course and any page shown under the course.
+     *
+     * @return bool|int The role(int) if the user is in another role, false otherwise
+     */
+    protected function in_alternative_role() {
+        global $USER;
+        if (!empty($USER->access['rsw']) && is_array($USER->access['rsw'])) {
+            if (!empty($this->page->context) && !empty($USER->access['rsw'][$this->page->context->path])) {
+                return $USER->access['rsw'][$this->page->context->path];
+            }
+            foreach ($USER->access['rsw'] as $key=>$role) {
+                if (strpos($this->context->path,$key)===0) {
+                    return $role;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 
+     */
+    protected function load_front_page_settings() {
+        global $CFG, $USER, $OUTPUT, $SITE;
+
+        $course = $SITE;
+        if (empty($course->context)) {
+            $course->context = get_context_instance(CONTEXT_COURSE, $course->id);   // Course context
+        }
+        if (has_capability('moodle/course:update', $course->context)) {
+
+            $frontpage = $this->add(get_string('frontpagesettings'));
+            $this->get($frontpage)->id = 'frontpagesettings';
+            $this->get($frontpage)->forceopen = true;
+
+            // Add the turn on/off settings
+            $url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
+            if ($this->page->user_is_editing()) {
+                $url->param('edit', 'off');
+                $editstring = get_string('turneditingoff');
+            } else {
+                $url->param('edit', 'on');
+                $editstring = get_string('turneditingon');
+            }
+            $this->get($frontpage)->add($editstring, null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/edit'));
+
+            // Add the course settings link
+            $url = new moodle_url($CFG->wwwroot.'/admin/settings.php', array('section'=>'frontpagesettings'));
+            $this->get($frontpage)->add(get_string('settings'), null, null, self::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/settings'));
+        }
+    }
+
+
+    /**
+     * 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);
+            }
+        }
+    }
+}
+
+/**
+ * Simple class used to output a navigation branch in XML
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class navigation_xml {
+    /** @var array */
+    protected $nodetype = array('node','branch');
+    /** @var array */
+    protected $expandable = array();
+    /**
+     * Turns a branch and all of its children into XML
+     *
+     * @param navigation_node $branch
+     * @return string XML string
+     */
+    public function convert($branch) {
+        $xml = $this->convert_child($branch);
+        return $xml;
+    }
+    /**
+     * Set the expandable items in the array so that we have enough information
+     * to attach AJAX events
+     */
+    public function set_expandable($expandable) {
+        foreach ($expandable as $node) {
+            $this->expandable[(string)$node['branchid']] = $node;
+        }
+    }
+    /**
+     * 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
+     */
+    protected function convert_child($child, $depth=1) {
+        global $OUTPUT;
+
+        if (!$child->display) {
+            return '';
+        }
+        $attributes = array();
+        $attributes['id'] = $child->id;
+        $attributes['type'] = $child->type;
+        $attributes['key'] = $child->key;
+        $attributes['icon'] = $child->icon;
+        $attributes['class'] = $child->get_css_type();
+        if ($child->forcetitle || $child->title !== $child->text) {
+            $attributes['title'] = htmlentities($child->title);
+        }
+        if (array_key_exists((string)$child->key, $this->expandable)) {
+            $attributes['expandable'] = $child->key;
+            $child->add_class($this->expandable[$child->key]['id']);
+        }
+        if (count($child->classes)>0) {
+            $attributes['class'] .= ' '.join(' ',$child->classes);
+        }
+        if (is_string($child->action)) {
+            $attributes['link'] = $child->action;
+        } else if ($child->action instanceof moodle_url) {
+            $attributes['link'] = $child->action->out();
+        }
+        $attributes['hidden'] = ($child->hidden);
+        $attributes['haschildren'] = (count($child->children)>0 || $child->type == navigation_node::TYPE_CATEGORY);
+
+        $xml = '<'.$this->nodetype[$child->nodetype];
+        if (count($attributes)>0) {
+            foreach ($attributes as $key=>$value) {
+                if (is_bool($value)) {
+                    if ($value) {
+                        $xml .= ' '.$key.'="true"';
+                    } else {
+                        $xml .= ' '.$key.'="false"';
+                    }
+                } else if ($value !== null) {
+                    $xml .= ' '.$key.'="'.$value.'"';
+                }
+            }
+        }
+        $xml .= '>';
+        $xml .= '<name>'.htmlentities($child->text).'</name>';
+        if (count($child->children)>0) {
+            $xml .= '<children>';
+            foreach ($child->children as $subchild) {
+                $xml .= $this->convert_child($subchild, $depth+1);
+            }
+            $xml .= '</children>';
+        }
+        $xml .= '</'.$this->nodetype[$child->nodetype].'>';
+        return $xml;
+    }
+}
+
+/**
+ * The cache class used by global navigation and settings navigation to cache bits
+ * and bobs that are used during their generation.
+ *
+ * It is basically an easy access point to session with a bit of smarts to make
+ * sure that the information that is cached is valid still.
+ *
+ * Example use:
+ * <code php>
+ * if (!$cache->viewdiscussion()) {
+ *     // Code to do stuff and produce cachable content
+ *     $cache->viewdiscussion = has_capability('mod/forum:viewdiscussion', $coursecontext);
+ * }
+ * $content = $cache->viewdiscussion;
+ * </code>
+ *
+ * @package moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class navigation_cache {
+    /** @var int */
+    protected $creation;
+    /** @var array */
+    protected $session;
+    /** @var string */
+    protected $area;
+    /** @var int */
+    protected $timeout;
+    /** @var stdClass */
+    protected $currentcontext;
+    /** @var int */
+    const CACHETIME = 0;
+    /** @var int */
+    const CACHEUSERID = 1;
+    /** @var int */
+    const CACHEVALUE = 2;
+    
+    /**
+     * Contructor for the cache. Requires two arguments
+     *
+     * @param string $area The string to use to segregate this particular cache
+     *                it can either be unique to start a fresh cache or if you want
+     *                to share a cache then make it the string used in the original
+     *                cache
+     * @param int $timeout The number of seconds to time the information out after
+     */
+    public function __construct($area, $timeout=60) {
+        global $SESSION, $PAGE;
+        $this->creation = time();
+        $this->area = $area;
+
+        if (!isset($SESSION->navcache)) {
+            $SESSION->navcache = new stdClass;
+        }
+
+        if (!isset($SESSION->navcache->{$area})) {
+            $SESSION->navcache->{$area} = array();
+        }
+        $this->session = &$SESSION->navcache->{$area};
+        $this->timeout = time()-$timeout;
+        if (rand(0,10)===0) {
+            $this->garbage_collection();
+        }
+    }
+
+    /**
+     * Magic Method to retrieve something by simply calling using = cache->key
+     *
+     * @param mixed $key The identifier for the information you want out again
+     * @return void|mixed Either void or what ever was put in
+     */
+    public function __get($key) {
+        if (!$this->cached($key)) {
+            return;
+        }
+        $information = $this->session[$key][self::CACHEVALUE];
+        return unserialize($information);
+    }
+
+    /**
+     * Magic method that simply uses {@link set();} to store something in the cache
+     *
+     * @param string|int $key
+     * @param mixed $information
+     */
+    public function __set($key, $information) {
+        $this->set($key, $information);
+    }
+    
+    /**
+     * Sets some information against the cache (session) for later retrieval
+     *
+     * @param string|int $key
+     * @param mixed $information
+     */
+    public function set($key, $information) {
+        global $USER;
+        $information = serialize($information);
+        $this->session[$key]= array(self::CACHETIME=>time(), self::CACHEUSERID=>$USER->id, self::CACHEVALUE=>$information);
+    }
+    /**
+     * Check the existence of the identifier in the cache
+     *
+     * @param string|int $key
+     * @return bool
+     */
+    public function cached($key) {
+        global $USER;
+        if (!array_key_exists($key, $this->session) || !is_array($this->session[$key]) || $this->session[$key][self::CACHEUSERID]!=$USER->id || $this->session[$key][self::CACHETIME] < $this->timeout) {
+            return false;
+        }
+        return true;
+    }
+    /**
+     * Whipes the entire cache, good to force regeneration
+     */
+    public function clear() {
+        $this->session = array();
+    }
+    /**
+     * Checks all cache entries and removes any that have expired, good ole cleanup
+     */
+    protected function garbage_collection() {
+        foreach ($this->session as $key=>$cachedinfo) {
+            if (is_array($cachedinfo) && $cachedinfo[self::CACHETIME]<$this->timeout) {
+                unset($this->session[$key]);
+            }
+        }
+    }
+}
\ No newline at end of file
index 09ef4459636d4ae4e3e25bab8c32ae880e3d5616..a34ef08f18c92c8cc67c447ee4b0b123cdca8f2d 100644 (file)
@@ -495,21 +495,21 @@ class moodle_core_renderer extends moodle_renderer_base {
         $this->page->requires->js('lib/overlib/overlib.js')->in_head();
         $this->page->requires->js('lib/overlib/overlib_cssstyle.js')->in_head();
         $this->page->requires->js('lib/cookies.js')->in_head();
-        $this->page->requires->js_function_call('setTimeout', Array('fix_column_widths()', 20));
+        $this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));
 
         $focus = $this->page->focuscontrol;
         if (!empty($focus)) {
             if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
                 // This is a horrifically bad way to handle focus but it is passed in
                 // through messy formslib::moodleform
-                $this->page->requires->js_function_call('old_onload_focus', Array($matches[1], $matches[2]));
+                $this->page->requires->js_function_call('old_onload_focus', array($matches[1], $matches[2]));
             } else if (strpos($focus, '.')!==false) {
                 // Old style of focus, bad way to do it
                 debugging('This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.', DEBUG_DEVELOPER);
                 $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
             } else {
                 // Focus element with given id
-                $this->page->requires->js_function_call('focuscontrol', Array($focus));
+                $this->page->requires->js_function_call('focuscontrol', array($focus));
             }
         }
 
@@ -691,7 +691,7 @@ class moodle_core_renderer extends moodle_renderer_base {
      *      removed when there is a $PAGE->... replacement.
      * @return string HTML that you must output this, preferably immediately.
      */
-    public function header($navigation = '', $menu='') {
+    public function header($menu='') {
         // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation
         global $USER, $CFG;
 
@@ -701,10 +701,10 @@ class moodle_core_renderer extends moodle_renderer_base {
         $templatefile = $this->page->theme->template_for_page($this->page->generaltype);
         if ($templatefile) {
             // Render the template.
-            $template = $this->render_page_template($templatefile, $menu, $navigation);
+            $template = $this->render_page_template($templatefile, $menu);
         } else {
             // New style template not found, fall back to using header.html and footer.html.
-            $template = $this->handle_legacy_theme($navigation, $menu);
+            $template = $this->handle_legacy_theme($menu);
         }
 
         // Slice the template output into header and footer.
@@ -734,7 +734,7 @@ class moodle_core_renderer extends moodle_renderer_base {
      * @param array $navigation The navigation that will be used in the included file
      * @return string HTML code
      */
-    protected function render_page_template($templatefile, $menu, $navigation) {
+    protected function render_page_template($templatefile, $menu) {
         global $CFG, $SITE, $THEME, $USER;
         // The next lines are a bit tricky. The point is, here we are in a method
         // of a renderer class, and this object may, or may not, be the same as
@@ -746,6 +746,9 @@ class moodle_core_renderer extends moodle_renderer_base {
         $PAGE = $this->page;
         $COURSE = $this->page->course;
 
+        // Required for legacy template uses
+        $navigation = $this->has_navbar();
+
         ob_start();
         include($templatefile);
         $template = ob_get_contents();
@@ -759,12 +762,13 @@ class moodle_core_renderer extends moodle_renderer_base {
      * @param array $menu The menu that will be used in the included file
      * @return string HTML code
      */
-    protected function handle_legacy_theme($navigation, $menu) {
+    protected function handle_legacy_theme($menu) {
         global $CFG, $SITE, $USER;
         // Set a pretend global from the properties of this class.
         // See the comment in render_page_template for a fuller explanation.
         $COURSE = $this->page->course;
         $THEME = $this->page->theme;
+        $OUTPUT = $this;
 
         // Set up local variables that header.html expects.
         $direction = $this->htmlattributes();
@@ -789,6 +793,7 @@ class moodle_core_renderer extends moodle_renderer_base {
         $course = $this->page->course;
         $performanceinfo = self::PERFORMANCE_INFO_TOKEN;
 
+        $navigation = $this->has_navbar();
         if (!$menu && $navigation) {
             $menu = $loggedinas;
         }
@@ -2259,6 +2264,84 @@ class moodle_core_renderer extends moodle_renderer_base {
     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:
+     *
+     * <pre>
+     * <<ul>>
+     * <<li>><div class='tree_item parent'>(item contents)</div>
+     *      <<ul>
+     *      <<li>><div class='tree_item'>(item contents)</div><</li>>
+     *      <</ul>>
+     * <</li>>
+     * <</ul>>
+     * </pre>
+     *
+     * @param array[]tree_item $items
+     * @param array[string]string $attrs html attributes passed to the top of
+     * the list
+     * @return string HTML
+     */
+    function tree_block_contents($items, $attrs=array()) {
+        // exit if empty, we don't want an empty ul element
+        if (empty($items)) {
+            return '';
+        }
+        // array of nested li elements
+        $lis = array();
+        foreach ($items as $item) {
+            // this applies to the li item which contains all child lists too
+            $content = $item->content($this);
+            $liclasses = array($item->get_css_type());
+            if (!$item->forceopen || (!$item->forceopen && $item->collapse) || (count($item->children)==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 (!empty($item->children)  || $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 = $this->output_tag('p', $divattr, $content) . $this->tree_block_contents($item->children);
+            if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
+                $content = $this->output_tag('hr', array(), null).$content;
+            }
+            $content = $this->output_tag('li', $liattr, $content);
+            $lis[] = $content;
+        }
+        return $this->output_tag('ul', $attrs, implode("\n", $lis));
+    }
+
+    /**
+     * Return the navbar content so that it can be echoed out by the layout
+     * @return string XHTML navbar
+     */
+    public function navbar() {
+        return $this->page->navbar->content();
+    }
+
+    /**
+     * Checks to see if there are any items on the navbar object
+     * @return bool true if there are, false if not
+     */
+    public function has_navbar() {
+        return $this->page->navbar->has_items();
+    }
 }
 
 
index 5cd170837d882e5f792dc6446958942944e510b7..376df134d8d3741e016a0faa6a4c2dee9ab506fe 100644 (file)
@@ -126,6 +126,12 @@ class moodle_page {
     protected $_button = '';
 
     protected $_theme = null;
+    /** @var null|global_navigation Contains the global navigation structure*/
+    protected $_navigation = null;
+    /** @var null|settings_navigation Contains the settings navigation structure*/
+    protected $_settingsnav = null;
+    /** @var null|navbar Contains the navbar structure*/
+    protected $_navbar = null;
 
     /**
      * Then the theme is initialsed, we save the stack trace, for use in error messages.
@@ -437,6 +443,40 @@ class moodle_page {
         return $this->_opencontainers;
     }
 
+    /**
+     * Return the navigation object
+     * @return global_navigation
+     */
+    public function get_navigation() {
+        if ($this->_navigation === null) {
+            $this->_navigation = new global_navigation();
+        }
+        return $this->_navigation;
+    }
+
+    /**
+     * Return a navbar object
+     * @return navbar
+     */
+    public function get_navbar() {
+        if ($this->_navbar === null) {
+            $this->_navbar = new navbar($this);
+        }
+        return $this->_navbar;
+    }
+
+    /**
+     * Returns the settings navigation object
+     * @return settings_navigation
+     */
+    public function get_settingsnav() {
+        if ($this->_settingsnav === null) {
+            $this->_settingsnav = new settings_navigation($this);
+            $this->_settingsnav->initialise();
+        }
+        return $this->_settingsnav;
+    }
+
     /**
      * PHP overloading magic to make the $PAGE->course syntax work by redirecting
      * it to the corresponding $PAGE->get_course() method if there is one, and
index e1a289292d5e9ace052318e07830dd801213d551..23a4175f162f0dc14c61ee5022c744501115ad13 100644 (file)
@@ -241,6 +241,7 @@ global $SCRIPT;
     require_once($CFG->libdir .'/ajax/ajaxlib.php');    // Functions for managing our use of JavaScript and YUI
     require_once($CFG->libdir .'/weblib.php');          // Functions relating to HTTP and content
     require_once($CFG->libdir .'/outputlib.php');       // Functions for generating output
+    require_once($CFG->libdir .'/navigationlib.php');   // Class for generating Navigation structure
     require_once($CFG->libdir .'/dmllib.php');          // Database access
     require_once($CFG->libdir .'/datalib.php');         // Legacy lib with a big-mix of functions.
     require_once($CFG->libdir .'/accesslib.php');       // Access control functions
diff --git a/lib/simpletest/testnavigationlib.php b/lib/simpletest/testnavigationlib.php
new file mode 100644 (file)
index 0000000..40e8bc0
--- /dev/null
@@ -0,0 +1,694 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for lib/navigationlib.php
+ *
+ * @package   moodlecore
+ * @copyright 2009 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later (5)
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
+}
+require_once($CFG->libdir . '/navigationlib.php');
+
+class navigation_node_test extends UnitTestCase {
+    protected $tree;
+    public static $includecoverage = array('./lib/navigationlib.php');
+    public static $excludecoverage = array();
+
+    public function setUp() {
+        global $CFG, $FULLME;
+        parent::setUp();
+        $oldfullme = $FULLME;
+        $FULLME = 'http://www.moodle.org/test.php';
+        $this->node = new navigation_node('Test Node');
+        $this->node->type = navigation_node::TYPE_SYSTEM;
+        $this->node->add('demo1', null, 'demo1', navigation_node::TYPE_COURSE, 'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->add('demo2', null, 'demo2', navigation_node::TYPE_COURSE, 'http://www.moodle.com/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->add('demo3', null, 'demo3', navigation_node::TYPE_CATEGORY, 'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('demo3')->add('demo4', null, 'demo4', navigation_node::TYPE_COURSE, new moodle_url('http://www.moodle.org/'),$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('demo3')->add('demo5', null, 'demo5', navigation_node::TYPE_COURSE, 'http://www.moodle.org/test.php',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('demo3')->get('demo5')->make_active();
+        $this->node->get('demo3')->get('demo5')->add('activity1', null, 'activity1',navigation_node::TYPE_ACTIVITY);
+        $this->node->get('demo3')->get('demo5')->get('activity1')->make_active();
+        $this->node->add('hiddendemo1', null, 'hiddendemo1', navigation_node::TYPE_CATEGORY, 'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('hiddendemo1')->hidden = true;
+        $this->node->get('hiddendemo1')->add('hiddendemo2', null, 'hiddendemo2', navigation_node::TYPE_COURSE, new moodle_url('http://www.moodle.org/'),$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('hiddendemo1')->add('hiddendemo3', null, 'hiddendemo3', navigation_node::TYPE_COURSE, new moodle_url('http://www.moodle.org/'),$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get('hiddendemo1')->get('hiddendemo2')->helpbutton = 'Here is a help button';
+        $this->node->get('hiddendemo1')->get('hiddendemo3')->display = false;
+        $FULLME = $oldfullme;
+    }
+    public function test___construct() {
+        global $CFG;
+        $properties = array();
+        $properties['text'] = 'text';
+        $properties['shorttext'] = 'shorttext';
+        $properties['key'] = 'key';
+        $properties['type'] = 'navigation_node::TYPE_COURSE';
+        $properties['action'] = 'http://www.moodle.org/';
+        $properties['icon'] = $CFG->httpswwwroot . '/pix/i/course.gif';
+        $node = new navigation_node($properties);
+        $this->assertEqual($node->text, $properties['text']);
+        $this->assertEqual($node->title, $properties['text']);
+        $this->assertEqual($node->shorttext, $properties['shorttext']);
+        $this->assertEqual($node->key, $properties['key']);
+        $this->assertEqual($node->type, $properties['type']);
+        $this->assertEqual($node->action, $properties['action']);
+        $this->assertEqual($node->icon, $properties['icon']);
+    }
+    public function test_add() {
+        global $CFG;
+        // Add a node with all args set
+        $key1 = $this->node->add('test_add_1','testadd1','key',navigation_node::TYPE_COURSE,'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        // Add a node with the minimum args required
+        $key2 = $this->node->add('test_add_2','testadd2');
+        $key3 = $this->node->add(str_repeat('moodle ', 15),str_repeat('moodle', 15));
+        $this->assertEqual('key',$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');
+    }
+
+    public function test_add_class() {
+        $node = $this->node->get('demo1');
+        $this->assertIsA($node, 'navigation_node');
+        if ($node !== false) {
+            $node->add_class('myclass');
+            $classes = $node->classes;
+            $this->assertTrue(in_array('myclass', $classes));
+        }
+    }
+
+    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/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->assertEqual($key1, 'testatp1');
+
+        // 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() {
+        global $FULLME;
+        $oldfullme = $FULLME;
+
+        // First test the string urls
+        $FULLME = 'http://www.moodle.org/';
+        // demo1 -> action is http://www.moodle.org/, thus should be true
+        $this->assertTrue($this->node->get('demo1')->check_if_active());
+        // demo2 -> action is http://www.moodle.com/, thus should be false
+        $this->assertFalse($this->node->get('demo2')->check_if_active());
+
+        $FULLME = $oldfullme;
+    }
+
+    public function test_contains_active_node() {
+        // demo5, and activity1 were set to active during setup
+        // Should be true as it contains all nodes
+        $this->assertTrue($this->node->contains_active_node());
+        // Should be true as demo5 is a child of demo3
+        $this->assertTrue($this->node->get('demo3')->contains_active_node());
+        // Obviously duff
+        $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());
+        // Obviously duff
+        $this->assertFalse($this->node->get('demo3')->get('demo4')->contains_active_node());
+    }
+
+    public function test_content() {
+        $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), $content1);
+        $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('demo3')->action), $content2);
+        $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('demo3')->get('demo5')->action), $content3);
+        $this->assert(new ContainsTagWithAttribute('a','href',$this->node->get('hiddendemo1')->get('hiddendemo2')->action->out()), $content4);
+        $this->assertTrue(empty($content5));
+    }
+
+    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'));
+        }
+        $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'));
+        }
+        $this->assertNotA($activenode4, '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);
+        $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);
+        $this->assertEqual(count($expandable), 5);
+        if (count($expandable) === 5) {
+            $name = $expandable[0]['branchid'];
+            $name .= $expandable[1]['branchid'];
+            $name .= $expandable[2]['branchid'];
+            $name .= $expandable[3]['branchid'];
+            $name .= $expandable[4]['branchid'];
+            $this->assertEqual($name, 'demo1demo2demo4hiddendemo2hiddendemo3');
+        }
+    }
+
+    public function test_get() {
+        $node1 = $this->node->get('demo1'); // Exists
+        $node2 = $this->node->get('demo4'); // Doesn't exist for this node
+        $node3 = $this->node->get('demo0'); // Doesn't exist at all
+        $node4 = $this->node->get(false);   // Sometimes occurs in nature code
+        $this->assertIsA($node1, 'navigation_node');
+        $this->assertFalse($node2);
+        $this->assertFalse($node3);
+        $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();
+        $this->node->get('demo3')->get('demo5')->type = 1000;
+        $csstype3 = $this->node->get('demo3')->get('demo5')->get_css_type();
+        $this->assertEqual($csstype1, 'type_category');
+        $this->assertEqual($csstype2, 'type_course');
+        $this->assertEqual($csstype3, 'type_unknown');
+    }
+
+    public function test_make_active() {
+        global $CFG;
+        $key1 = $this->node->add('active node 1', null, 'anode1');
+        $key2 = $this->node->add('active node 2', null, 'anode2', navigation_node::TYPE_COURSE, new moodle_url($CFG->wwwroot));
+        $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_reiterate_active_nodes() {
+        global $FULLME;
+        $oldfullme = $FULLME;
+        $FULLME = 'http://www.moodle.org/test.php';
+        $cachenode = serialize($this->node);
+        $cachenode = unserialize($cachenode);
+        $this->assertFalse($cachenode->get('demo3')->get('demo5')->isactive);
+        $this->assertTrue($cachenode->reiterate_active_nodes());
+        $this->assertTrue($cachenode->get('demo3')->get('demo5')->isactive);
+        $FULLME = $oldfullme;
+    }
+    public function test_remove_child() {
+        $this->node->add('child to remove 1', null, 'remove1');
+        $this->node->add('child to remove 2', null, 'remove2');
+        $this->node->get('remove2')->add('child to remove 3', null, 'remove3');
+        $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'));
+    }
+    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);
+    }
+}
+
+/**
+ * This is a dummy object that allows us to call protected methods within the
+ * global navigation class by prefixing the methods with `exposed_`
+ */
+class exposed_global_navigation extends global_navigation {
+    protected $exposedkey = 'exposed_';
+    function __construct() {
+        parent::__construct();
+        $this->cache = new navigation_cache('simpletest_nav');
+    }
+    function __call($method, $arguments) {
+        if (strpos($method,$this->exposedkey) !== false) {
+            $method = substr($method, strlen($this->exposedkey));
+        }
+        if (method_exists($this, $method)) {
+            return call_user_func_array(array($this, $method), $arguments);
+        }
+        throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
+    }
+}
+
+class global_navigation_test extends UnitTestCase {
+    /**
+     * @var global_navigation
+     */
+    public $node;
+    protected $cache;
+    public static $includecoverage = array('./lib/navigationlib.php');
+    public static $excludecoverage = array();
+    
+    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, 'cat1', navigation_node::TYPE_CATEGORY);
+        $this->node->add('category 2', null, 'cat2', navigation_node::TYPE_CATEGORY);
+        $this->node->add('category 3', null, 'cat3', navigation_node::TYPE_CATEGORY);
+        $this->node->get('cat2')->add('sub category 1', null, 'sub1', navigation_node::TYPE_CATEGORY);
+        $this->node->get('cat2')->add('sub category 2', null, 'sub2', navigation_node::TYPE_CATEGORY);
+        $this->node->get('cat2')->add('sub category 3', null, 'sub3', navigation_node::TYPE_CATEGORY);
+        $this->node->get('cat2')->get('sub2')->add('course 1', null, 'course1', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->add('course 2', null, 'course2', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->add('course 3', null, 'course3', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->add('section 1', null, 'sec1', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->add('section 2', null, 'sec2', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->add('section 3', null, 'sec3', navigation_node::TYPE_COURSE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 1', null, 'act1', navigation_node::TYPE_ACTIVITY);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 2', null, 'act2', navigation_node::TYPE_ACTIVITY);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('activity 3', null, 'act3', navigation_node::TYPE_ACTIVITY);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 1', null, 'res1', navigation_node::TYPE_RESOURCE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 2', null, 'res2', navigation_node::TYPE_RESOURCE);
+        $this->node->get('cat2')->get('sub2')->get('course2')->get('sec2')->add('resource 3', null, 'res3', navigation_node::TYPE_RESOURCE);
+
+        $this->cache->clear();
+        $this->cache->modinfo5 = unserialize('O:6:"object":6:{s:8:"courseid";s:1:"5";s:6:"userid";s:1:"2";s:8:"sections";a:1:{i:0;a:1:{i:0;s:3:"288";}}s:3:"cms";a:1:{i:288;O:6:"object":17:{s:2:"id";s:3:"288";s:8:"instance";s:2:"19";s:6:"course";s:1:"5";s:7:"modname";s:5:"forum";s:4:"name";s:10:"News forum";s:7:"visible";s:1:"1";s:10:"sectionnum";s:1:"0";s:9:"groupmode";s:1:"0";s:10:"groupingid";s:1:"0";s:16:"groupmembersonly";s:1:"0";s:6:"indent";s:1:"0";s:10:"completion";s:1:"0";s:5:"extra";s:0:"";s:4:"icon";s:0:"";s:11:"uservisible";b:1;s:9:"modplural";s:6:"Forums";s:9:"available";b:1;}}s:9:"instances";a:1:{s:5:"forum";a:1:{i:19;R:8;}}s:6:"groups";N;}');
+        $this->cache->coursesections5 = unserialize('a:5:{i:0;O:8:"stdClass":6:{s:7:"section";s:1:"0";s:2:"id";s:2:"14";s:6:"course";s:1:"5";s:7:"summary";N;s:8:"sequence";s:3:"288";s:7:"visible";s:1:"1";}i:1;O:8:"stdClass":6:{s:7:"section";s:1:"1";s:2:"id";s:2:"97";s:6:"course";s:1:"5";s:7:"summary";s:0:"";s:8:"sequence";N;s:7:"visible";s:1:"1";}i:2;O:8:"stdClass":6:{s:7:"section";s:1:"2";s:2:"id";s:2:"98";s:6:"course";s:1:"5";s:7:"summary";s:0:"";s:8:"sequence";N;s:7:"visible";s:1:"1";}i:3;O:8:"stdClass":6:{s:7:"section";s:1:"3";s:2:"id";s:2:"99";s:6:"course";s:1:"5";s:7:"summary";s:0:"";s:8:"sequence";N;s:7:"visible";s:1:"1";}i:4;O:8:"stdClass":6:{s:7:"section";s:1:"4";s:2:"id";s:3:"100";s:6:"course";s:1:"5";s:7:"summary";s:0:"";s:8:"sequence";N;s:7:"visible";s:1:"1";}}');
+        $this->cache->canviewhiddenactivities = true;
+        $this->cache->canviewhiddensections = true;
+        $this->cache->canviewhiddencourses = true;
+        $this->node->get('cat2')->get('sub2')->add('Test Course 5',null,'5',navigation_node::TYPE_COURSE, new moodle_url('http://moodle.org'));
+    }
+    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();
+    }
+    public function test_add_course_section_generic() {
+        $keys = array('cat2', 'sub2', '5');
+        $course = new stdClass;
+        $course->id = '5';
+        $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_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";
+            $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);
+    }
+    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() {
+        $course = new stdClass;
+        $course->id = 'tcourse10';
+        $course->fullname = 'Test Course 10';
+        $course->shortname = 'tcourse10';
+        $course->visible = true;
+        $this->node->exposed_load_course(array('cat2','sub3'), $course);
+        $this->assertIsA($this->node->get('cat2')->get('sub3')->get('tcourse10'), 'navigation_node');
+    }
+    public function test_load_course_activities() {
+        $keys = array('cat2', 'sub2', '5');
+        $course = new stdClass;
+        $course->id = '5';
+        $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;
+        $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_RESOURCE);
+    }
+    public function test_load_course_sections() {
+        $keys = array('cat2', 'sub2', '5');
+        $course = new stdClass;
+        $course->id = '5';
+        $course->format = 'topics';
+        $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');
+        $course = new stdClass;
+        $course->id = '5';
+        $this->node->get_by_path($keys)->add('Test Section 1', null, $this->cache->coursesections5[1]->id, navigation_node::TYPE_SECTION);
+        $modinfo = $this->cache->modinfo5;
+        $modinfo->sections[1] = array(289, 290);
+        $modinfo->cms[289] = clone($modinfo->cms[288]);
+        $modinfo->cms[289]->id = 289;
+        $modinfo->cms[289]->sectionnum = 1;
+        $modinfo->cms[290]->modname = 'forum';
+        $modinfo->cms[289]->instance = 20;
+        $modinfo->cms[290] = clone($modinfo->cms[288]);
+        $modinfo->cms[290]->id = 290;
+        $modinfo->cms[290]->modname = 'resource';
+        $modinfo->cms[290]->sectionnum = 1;
+        $modinfo->cms[290]->instance = 21;
+        $modinfo->instances['forum'][20] = clone($modinfo->instances['forum'][19]);
+        $modinfo->instances['forum'][20]->id = 20;
+        $modinfo->instances['resource'] = array();
+        $modinfo->instances['resource'][21] = clone($modinfo->instances['forum'][19]);
+        $modinfo->instances['resource'][21]->id = 21;
+        $this->cache->modinfo5 = $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_RESOURCE);
+    }
+    public function test_module_extends_navigation() {
+        $this->cache->test1_extends_navigation = true;
+        $this->cache->test2_extends_navigation = false;
+        $this->assertTrue($this->node->exposed_module_extends_navigation('forum'));
+        $this->assertTrue($this->node->exposed_module_extends_navigation('test1'));
+        $this->assertFalse($this->node->exposed_module_extends_navigation('test2'));
+        $this->assertFalse($this->node->exposed_module_extends_navigation('test3'));
+    }
+}
+
+/**
+ * This is a dummy object that allows us to call protected methods within the
+ * global navigation class by prefixing the methods with `exposed_`
+ */
+class exposed_navbar extends navbar {
+    protected $exposedkey = 'exposed_';
+    function __construct() {
+        global $PAGE;
+        parent::__construct($PAGE);
+        $this->cache = new navigation_cache('simpletest_nav');
+    }
+    function __call($method, $arguments) {
+        if (strpos($method,$this->exposedkey) !== false) {
+            $method = substr($method, strlen($this->exposedkey));
+        }
+        if (method_exists($this, $method)) {
+            return call_user_func_array(array($this, $method), $arguments);
+        }
+        throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
+    }
+}
+
+class navbar_test extends UnitTestCase {
+    protected $node;
+    protected $oldnav;
+
+    public static $includecoverage = array('./lib/navigationlib.php');
+    public static $excludecoverage = array();
+
+    public function setUp() {
+        global $PAGE;
+        $this->oldnav = $PAGE->navigation;
+        $this->cache = new navigation_cache('simpletest_nav');
+        $this->node = new exposed_navbar();
+        $temptree = new global_navigation_test();
+        $temptree->setUp();
+        $temptree->node->get_by_path(array('cat2','sub2', 'course2'))->make_active();
+        $PAGE->navigation = $temptree->node;
+    }
+    public function tearDown() {
+        global $PAGE;
+        $PAGE->navigation = $this->oldnav;
+    }
+    public function test_add() {
+        global $CFG;
+        // Add a node with all args set
+        $this->node->add('test_add_1','testadd1','testadd1',navigation_node::TYPE_COURSE,'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        // Add a node with the minimum args required
+        $key2 = $this->node->add('test_add_2');
+        $this->assertIsA($this->node->get('testadd1'), 'navigation_node');
+        $this->assertIsA($this->node->get('testadd1')->get($key2), 'navigation_node');
+    }
+    public function test_content() {
+        $html = $this->node->content();
+        $this->assert(new ContainsTagWithAttribute('a','href',$this->node->action->out()), $html);
+    }
+    public function test_has_items() {
+        global $PAGE;
+        $this->assertTrue($this->node->has_items());
+        $PAGE->navigation->get_by_path(array('cat2','sub2', 'course2'))->remove_class('active_tree_node');
+        $PAGE->navigation->get_by_path(array('cat2','sub2', 'course2'))->isactive = false;
+        $this->assertFalse($this->node->has_items());
+        $PAGE->navigation->get_by_path(array('cat2','sub2', 'course2'))->make_active();
+    }
+    public function test_parse_branch_to_html() {
+        global $CFG;
+        $key = $this->node->add('test_add_1','testadd1','testadd1',navigation_node::TYPE_COURSE,'http://www.moodle.org/',$CFG->httpswwwroot . '/pix/i/course.gif');
+        $this->node->get($key)->make_active();
+        $html = $this->node->exposed_parse_branch_to_html($this->node->children, true);
+        $this->assert(new ContainsTagWithAttribute('a','href',$this->node->action->out()), $html);
+    }
+}
+
+class navigation_cache_test extends UnitTestCase {
+    protected $cache;
+
+    public static $includecoverage = array('./lib/navigationlib.php');
+    public static $excludecoverage = array();
+
+    public function setUp() {
+        $this->cache = new navigation_cache('simpletest_nav');
+        $this->cache->anysetvariable = true;
+    }
+    public function test___get() {
+        $this->assertTrue($this->cache->anysetvariable);
+        $this->assertEqual($this->cache->notasetvariable, null);
+    }
+    public function test___set() {
+        $this->cache->myname = 'Sam Hemelryk';
+        $this->assertTrue($this->cache->cached('myname'));
+        $this->assertEqual($this->cache->myname, 'Sam Hemelryk');
+    }
+    public function test_cached() {
+        $this->assertTrue($this->cache->cached('anysetvariable'));
+        $this->assertFalse($this->cache->cached('notasetvariable'));
+    }
+    public function test_clear() {
+        $cache = clone($this->cache);
+        $this->assertTrue($cache->cached('anysetvariable'));
+        $cache->clear();
+        $this->assertFalse($cache->cached('anysetvariable'));
+    }
+    public function test_set() {
+        $this->cache->set('software', 'Moodle');
+        $this->assertTrue($this->cache->cached('software'));
+        $this->assertEqual($this->cache->software, 'Moodle');
+    }
+}
+
+/**
+ * This is a dummy object that allows us to call protected methods within the
+ * global navigation class by prefixing the methods with `exposed_`
+ */
+class exposed_settings_navigation extends settings_navigation {
+    protected $exposedkey = 'exposed_';
+    function __construct() {
+        global $PAGE;
+        parent::__construct($PAGE);
+        $this->cache = new navigation_cache('simpletest_nav');
+    }
+    function __call($method, $arguments) {
+        if (strpos($method,$this->exposedkey) !== false) {
+            $method = substr($method, strlen($this->exposedkey));
+        }
+        if (method_exists($this, $method)) {
+            return call_user_func_array(array($this, $method), $arguments);
+        }
+        throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
+    }
+}
+
+class settings_navigation_test extends UnitTestCase {
+    protected $node;
+    protected $cache;
+
+    public static $includecoverage = array('./lib/navigationlib.php');
+    public static $excludecoverage = array();
+
+    public function setUp() {
+        global $PAGE;
+        $this->cache = new navigation_cache('simpletest_nav');
+        $this->node = new exposed_settings_navigation();
+    }
+    public function test___construct() {
+        $this->node = new exposed_settings_navigation();
+    }
+    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, 'rootbranch1');
+        $this->node->add('rootbranch2', null, 'rootbranch2');
+        $this->node->add('rootbranch3', null, 'rootbranch3');
+        $this->node->get('rootbranch2')->add('something', null, 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'));
+    }
+}
\ No newline at end of file
index 3bd4e0ba473c4a86eeeda3a7692077a5ac5c9637..b5b6316d7f1e7010039839a790d05616b5bf7516 100644 (file)
@@ -2082,262 +2082,6 @@ function get_separator() {
     return ' '.link_arrow_right($text='/', $url='', $accesshide=true, 'sep').' ';
 }
 
-/**
- * Prints breadcrumb trail of links, called in theme/-/header.html
- *
- * @global object
- * @global object
- * @global object
- * @uses CONTEXT_SYSTEM
- * @param mixed $navigation The breadcrumb navigation string to be printed
- * @param string $separator OBSOLETE, mostly not used any more. See build_navigation instead.
- * @param boolean $return False to echo the breadcrumb string (default), true to return it.
- * @return string|void String or null, depending on $return.
- */
-function print_navigation ($navigation, $separator=0, $return=false) {
-    global $CFG, $THEME, $SITE;
-    $output = '';
-
-    if (0 === $separator) {
-        $separator = get_separator();
-    }
-    else {
-        $separator = '<span class="sep">'. $separator .'</span>';
-    }
-
-    if ($navigation) {
-
-        if (is_newnav($navigation)) {
-            if ($return) {
-                return($navigation['navlinks']);
-            } else {
-                echo $navigation['navlinks'];
-                return;
-            }
-        } else {
-            debugging('Navigation needs to be updated to use build_navigation()', DEBUG_DEVELOPER);
-        }
-
-        if (!is_array($navigation)) {
-            $ar = explode('->', $navigation);
-            $navigation = array();
-
-            foreach ($ar as $a) {
-                if (strpos($a, '</a>') === false) {
-                    $navigation[] = array('title' => $a, 'url' => '');
-                } else {
-                    if (preg_match('/<a.*href="([^"]*)">(.*)<\/a>/', $a, $matches)) {
-                        $navigation[] = array('title' => $matches[2], 'url' => $matches[1]);
-                    }
-                }
-            }
-        }
-
-        if (!$SITE) {
-            $site = new object();
-            $site->shortname = get_string('home');
-        } else {
-            $site = $SITE;
-        }
-
-        //Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
-        $output .= get_accesshide(get_string('youarehere','access'), 'h2')."<ul>\n";
-
-        $output .= '<li class="first">'."\n".'<a '.$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
-               .$CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))
-                                 && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
-                                 ? '/my' : '') .'/">'. format_string($site->shortname) ."</a>\n</li>\n";
-
-
-        foreach ($navigation as $navitem) {
-            $title = trim(strip_tags(format_string($navitem['title'], false)));
-            $url   = $navitem['url'];
-
-            if (empty($url)) {
-                $output .= '<li>'."$separator $title</li>\n";
-            } else {
-                $output .= '<li>'."$separator\n<a ".$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
-                           .$url.'">'."$title</a>\n</li>\n";
-            }
-        }
-
-        $output .= "</ul>\n";
-    }
-
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
-}
-
-/**
- * This function will build the navigation string to be used by print_header
- * and others.
- *
- * It automatically generates the site and course level (if appropriate) links.
- *
- * If you pass in a $cm object, the method will also generate the activity (e.g. 'Forums')
- * and activityinstances (e.g. 'General Developer Forum') navigation levels.
- *
- * If you want to add any further navigation links after the ones this function generates,
- * the pass an array of extra link arrays like this:
- * array(
- *     array('name' => $linktext1, 'link' => $url1, 'type' => $linktype1),
- *     array('name' => $linktext2, 'link' => $url2, 'type' => $linktype2)
- * )
- * The normal case is to just add one further link, for example 'Editing forum' after
- * 'General Developer Forum', with no link.
- * To do that, you need to pass
- * array(array('name' => $linktext, 'link' => '', 'type' => 'title'))
- * However, becuase this is a very common case, you can use a shortcut syntax, and just
- * pass the string 'Editing forum', instead of an array as $extranavlinks.
- *
- * At the moment, the link types only have limited significance. Type 'activity' is
- * recognised in order to implement the $CFG->hideactivitytypenavlink feature. Types
- * that are known to appear are 'home', 'course', 'activity', 'activityinstance' and 'title'.
- * This really needs to be documented better. In the mean time, try to be consistent, it will
- * enable people to customise the navigation more in future.
- *
- * When passing a $cm object, the fields used are $cm->modname, $cm->name and $cm->course.
- * If you get the $cm object using the function get_coursemodule_from_instance or
- * get_coursemodule_from_id (as recommended) then this will be done for you automatically.
- * If you don't have $cm->modname or $cm->name, this fuction will attempt to find them using
- * the $cm->module and $cm->instance fields, but this takes extra database queries, so a
- * warning is printed in developer debug mode.
- *
- * @global object
- * @global object
- * @global object
- * @global object
- * @uses SITEID
- * @uses DEBUG_DEVELOPER
- * @uses CONTEXT_SYSTEM
- * @param mixed $extranavlinks - Normally an array of arrays, keys: name, link, type. If you
- *      only want one extra item with no link, you can pass a string instead. If you don't want
- *      any extra links, pass an empty string.
- * @param mixed $cm - optionally the $cm object, if you want this function to generate the
- *      activity and activityinstance levels of navigation too.
- * @return array Navigation array
- */
-function build_navigation($extranavlinks, $cm = null) {
-    global $CFG, $COURSE, $DB, $SITE;
-
-    if (is_string($extranavlinks)) {
-        if ($extranavlinks == '') {
-            $extranavlinks = array();
-        } else {
-            $extranavlinks = array(array('name' => $extranavlinks, 'link' => '', 'type' => 'title'));
-        }
-    }
-
-    $navlinks = array();
-
-    //Site name
-    if (!empty($SITE->shortname)) {
-        $navlinks[] = array(
-                'name' => format_string($SITE->shortname),
-                'link' => "$CFG->wwwroot/",
-                'type' => 'home');
-    }
-
-    // Course name, if appropriate.
-    if (isset($COURSE) && $COURSE->id != SITEID) {
-        $navlinks[] = array(
-                'name' => format_string($COURSE->shortname),
-                'link' => "$CFG->wwwroot/course/view.php?id=$COURSE->id",
-                'type' => 'course');
-    }
-
-    // Activity type and instance, if appropriate.
-    if (is_object($cm)) {
-        if (!isset($cm->modname)) {
-            debugging('The field $cm->modname should be set if you call build_navigation with '.
-                    'a $cm parameter. If you get $cm using get_coursemodule_from_instance or '.
-                    'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
-            if (!$cm->modname = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
-                print_error('cannotmoduletype');
-            }
-        }
-        if (!isset($cm->name)) {
-            debugging('The field $cm->name should be set if you call build_navigation with '.
-                    'a $cm parameter. If you get $cm using get_coursemodule_from_instance or '.
-                    'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
-            if (!$cm->name = $DB->get_field($cm->modname, 'name', array('id'=>$cm->instance))) {
-                print_error('cannotmodulename');
-            }
-        }
-        $navlinks[] = array(
-                'name' => get_string('modulenameplural', $cm->modname),
-                'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/index.php?id=' . $cm->course,
-                'type' => 'activity');
-        $navlinks[] = array(
-                'name' => format_string($cm->name),
-                'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/view.php?id=' . $cm->id,
-                'type' => 'activityinstance');
-    }
-
-    //Merge in extra navigation links
-    $navlinks = array_merge($navlinks, $extranavlinks);
-
-    // Work out whether we should be showing the activity (e.g. Forums) link.
-    // Note: build_navigation() is called from many places --
-    // install & upgrade for example -- where we cannot count on the
-    // roles infrastructure to be defined. Hence the during_initial_install() check.
-    if (!isset($CFG->hideactivitytypenavlink)) {
-        $CFG->hideactivitytypenavlink = 0;
-    }
-    if ($CFG->hideactivitytypenavlink == 2) {
-        $hideactivitylink = true;
-    } else if ($CFG->hideactivitytypenavlink == 1 && !during_initial_install() &&
-            !empty($COURSE->id) && $COURSE->id != SITEID) {
-        if (!isset($COURSE->context)) {
-            $COURSE->context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
-        }
-        $hideactivitylink = !has_capability('moodle/course:manageactivities', $COURSE->context);
-    } else {
-        $hideactivitylink = false;
-    }
-
-    //Construct an unordered list from $navlinks
-    //Accessibility: heading hidden from visual browsers by default.
-    $navigation = get_accesshide(get_string('youarehere','access'), 'h2')." <ul>\n";
-    $lastindex = count($navlinks) - 1;
-    $i = -1; // Used to count the times, so we know when we get to the last item.
-    $first = true;
-    foreach ($navlinks as $navlink) {
-        $i++;
-        $last = ($i == $lastindex);
-        if (!is_array($navlink)) {
-            continue;
-        }
-        if (!empty($navlink['type']) && $navlink['type'] == 'activity' && !$last && $hideactivitylink) {
-            continue;
-        }
-        if ($first) {
-            $navigation .= '<li class="first">';
-        } else {
-            $navigation .= '<li>';
-        }
-        if (!$first) {
-            $navigation .= get_separator();
-        }
-        if ((!empty($navlink['link'])) && !$last) {
-            $navigation .= "<a onclick=\"this.target='$CFG->framename'\" href=\"{$navlink['link']}\">";
-        }
-        $navigation .= "{$navlink['name']}";
-        if ((!empty($navlink['link'])) && !$last) {
-            $navigation .= "</a>";
-        }
-
-        $navigation .= "</li>";
-        $first = false;
-    }
-    $navigation .= "</ul>";
-
-    return(array('newnav' => true, 'navlinks' => $navigation));
-}
-
 /**
  * Print (or return) a collapisble region, that has a caption that can
  * be clicked to expand or collapse the region.
@@ -2676,165 +2420,6 @@ function switchroles_form($courseid) {
     return '';
 }
 
-/**
- * Returns a small popup menu of course activity modules
- *
- * Given a course and a (current) coursemodule
- * his function returns a small popup menu with all the
- * course activity modules in it, as a navigation menu
- * The data is taken from the serialised array stored in
- * the course record
- *
- * @global object
- * @global object
- * @global object
- * @global object
- * @uses CONTEXT_COURSE
- * @param object $course A {@link $COURSE} object.
- * @param object $cm A {@link $COURSE} object.
- * @param string $targetwindow The target window attribute to us
- * @return string
- */
-function navmenu($course, $cm=NULL, $targetwindow='self') {
-    global $CFG, $THEME, $USER, $DB, $OUTPUT;
-    require_once($CFG->dirroot . '/course/lib.php'); // Required for get_fast_modinfo
-
-    if (empty($THEME->navmenuwidth)) {
-        $width = 50;
-    } else {
-        $width = $THEME->navmenuwidth;
-    }
-
-    if ($cm) {
-        $cm = $cm->id;
-    }
-
-    if ($course->format == 'weeks') {
-        $strsection = get_string('week');
-    } else {
-        $strsection = get_string('topic');
-    }
-    $strjumpto = get_string('jumpto');
-
-    $modinfo = get_fast_modinfo($course);
-    $context = get_context_instance(CONTEXT_COURSE, $course->id);
-
-    $section = -1;
-    $selected = '';
-    $url = '';
-    $previousmod = NULL;
-    $backmod = NULL;
-    $nextmod = NULL;
-    $selectmod = NULL;
-    $logslink = NULL;
-    $flag = false;
-    $menu = array();
-    $menustyle = array();
-
-    $sections = $DB->get_records('course_sections', array('course'=>$course->id), 'section', 'section,visible,summary');
-
-    if (!empty($THEME->makenavmenulist)) {   /// A hack to produce an XHTML navmenu list for use in themes
-        $THEME->navmenulist = navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width, $cm);
-    }
-
-    foreach ($modinfo->cms as $mod) {
-        if ($mod->modname == 'label') {
-            continue;
-        }
-
-        if ($mod->sectionnum > $course->numsections) {   /// Don't show excess hidden sections
-            break;
-        }
-
-        if (!$mod->uservisible) { // do not icnlude empty sections at all
-            continue;
-        }
-
-        if ($mod->sectionnum > 0 and $section != $mod->sectionnum) {
-            $thissection = $sections[$mod->sectionnum];
-
-            if ($thissection->visible or !$course->hiddensections or
-                has_capability('moodle/course:viewhiddensections', $context)) {
-                $thissection->summary = strip_tags(format_string($thissection->summary,true));
-                if ($course->format == 'weeks' or empty($thissection->summary)) {
-                    $menu[] = '--'.$strsection ." ". $mod->sectionnum;
-                } else {
-                    if (strlen($thissection->summary) < ($width-3)) {
-                        $menu[] = '--'.$thissection->summary;
-                    } else {
-                        $menu[] = '--'.substr($thissection->summary, 0, $width).'...';
-                    }
-                }
-                $section = $mod->sectionnum;
-            } else {
-                // no activities from this hidden section shown
-                continue;
-            }
-        }
-
-        $url = $mod->modname.'/view.php?id='. $mod->id;
-        if ($flag) { // the current mod is the "next" mod
-            $nextmod = $mod;
-            $flag = false;
-        }
-        $localname = $mod->name;
-        if ($cm == $mod->id) {
-            $selected = $url;
-            $selectmod = $mod;
-            $backmod = $previousmod;
-            $flag = true; // set flag so we know to use next mod for "next"
-            $localname = $strjumpto;
-            $strjumpto = '';
-        } else {
-            $localname = strip_tags(format_string($localname,true));
-            $tl=textlib_get_instance();
-            if ($tl->strlen($localname) > ($width+5)) {
-                $localname = $tl->substr($localname, 0, $width).'...';
-            }
-            if (!$mod->visible) {
-                $localname = '('.$localname.')';
-            }
-        }
-        $menu[$url] = $localname;
-        if (empty($THEME->navmenuiconshide)) {
-            $menustyle[$url] = 'style="background-image: url('.$OUTPUT->mod_icon_url('icon', $mod->modname) . ');"';  // Unfortunately necessary to do this here
-        }
-        $previousmod = $mod;
-    }
-    //Accessibility: added Alt text, replaced &gt; &lt; with 'silent' character and 'accesshide' text.
-
-    if ($selectmod and has_capability('coursereport/log:view', $context)) {
-        $logstext = get_string('alllogs');
-        $logslink = '<li>'."\n".'<a title="'.$logstext.'" '.
-                    $CFG->frametarget.'onclick="this.target=\''.$CFG->framename.'\';"'.' href="'.
-                    $CFG->wwwroot.'/course/report/log/index.php?chooselog=1&amp;user=0&amp;date=0&amp;id='.
-                       $course->id.'&amp;modid='.$selectmod->id.'">'.
-                    '<img class="icon log" src="'.$OUTPUT->old_icon_url('i/log') . '" alt="'.$logstext.'" /></a>'."\n".'</li>';
-
-    }
-    if ($backmod) {
-        $backtext= get_string('activityprev', 'access');
-        $backmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$backmod->modname.'/view.php" '.
-                   'onclick="this.target=\''.$CFG->framename.'\';"'.'><fieldset class="invisiblefieldset">'.
-                   '<input type="hidden" name="id" value="'.$backmod->id.'" />'.
-                   '<button type="submit" title="'.$backtext.'">'.link_arrow_left($backtext, $url='', $accesshide=true).
-                   '</button></fieldset></form></li>';
-    }
-    if ($nextmod) {
-        $nexttext= get_string('activitynext', 'access');
-        $nextmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$nextmod->modname.'/view.php"  '.
-                   'onclick="this.target=\''.$CFG->framename.'\';"'.'><fieldset class="invisiblefieldset">'.
-                   '<input type="hidden" name="id" value="'.$nextmod->id.'" />'.
-                   '<button type="submit" title="'.$nexttext.'">'.link_arrow_right($nexttext, $url='', $accesshide=true).
-                   '</button></fieldset></form></li>';
-    }
-
-    return '<div class="navigation">'."\n".'<ul>'.$logslink . $backmod .
-            '<li>'.popup_form($CFG->wwwroot .'/mod/', $menu, 'navmenupopup', $selected, $strjumpto,
-                       '', '', true, $targetwindow, '', $menustyle).'</li>'.
-            $nextmod . '</ul>'."\n".'</div>';
-}
-
 /**
  * Returns a popup menu with course activity modules
  *
index 168bd559ea8b7cb4d76b993978b197706db16465..f7d2e35f343b15b7249c1ebc26c716e917a29236 100644 (file)
@@ -8177,3 +8177,180 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
         return get_string('modulename', 'forum');
     }
 }
+
+/**
+ * This function is used to extend the global navigation by add forum nodes if there
+ * is relevant content.
+ *
+ * @param navigation_node $navref
+ * @param stdClass $course
+ * @param stdClass $module
+ * @param stdClass $cm
+ */
+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);
+    $discussioncount = forum_get_discussions_count($cm);
+    if (!is_array($discussions) || count($discussions)==0) {
+        return;
+    }
+    $discussionkey = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')');
+    $navref->get($discussionkey)->mainnavonly = true;
+
+    foreach ($discussions as $discussion) {
+        $icon = $OUTPUT->old_icon_url('i/feedback');
+        $url = new moodle_url($CFG->wwwroot.'/mod/forum/discuss.php', array('d'=>$discussion->discussion));
+        $navref->get($discussionkey)->add($discussion->subject, null, null, null, $url, $icon);
+    }
+
+    if ($discussioncount > count($discussions)) {
+        if (!empty($navref->action)) {
+            $url = $navref->action;
+        } else {
+            $url = new moodle_url($CFG->wwwroot.'/mod/forum/view.php', array('id'=>$cm->id));
+        }
+        $childkey = $navref->get($discussionkey)->add(get_string('viewalldiscussions', 'forum'), null, null, null, $url, $icon);
+    }
+
+    $index = 0;
+    $recentposts = array();
+    $lastlogin = time() - COURSE_MAX_RECENT_PERIOD;
+    if (!isguestuser() and !empty($USER->lastcourseaccess[$course->id])) {
+        if ($USER->lastcourseaccess[$course->id] > $lastlogin) {
+            $lastlogin = $USER->lastcourseaccess[$course->id];
+        }
+    }
+    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;
+        foreach ($recentposts as $post) {
+            $icon = $OUTPUT->old_icon_url('i/feedback');
+            $url = new moodle_url($CFG->wwwroot.'/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, null, null, null, $url, $icon);
+        }
+    }
+}
+
+/**
+ * This function is used to extend the settings navigation with settings for the module
+ * It is called when the context for the page is a forum module
+ *
+ * @param settings_navigation $settingsnav {@link settings_navigation}
+ * @param stdClass $module
+ * @return void|mixed The key to the modules branch
+ */
+function forum_extend_settings_navigation($settingsnav, $module=null) {
+    global $USER, $PAGE, $FULLME, $CFG, $DB, $OUTPUT;
+    $forumkey = $settingsnav->add(get_string('forumadministration', 'forum'));
+    $forum = $settingsnav->get($forumkey);
+    $forum->forceopen = true;
+
+    $forumobject = $DB->get_record("forum", array("id" => $PAGE->cm->instance));
+
+    if (!empty($USER->id) && !has_capability('moodle/legacy:guest', $PAGE->cm->context, NULL, false)) {
+        $notekey = false;
+        $helpbutton = false;
+        if (forum_is_forcesubscribed($forumobject)) {
+            $notekey = $forum->add(get_string("forcessubscribe", 'forum'));
+            $string = get_string('allowchoice', 'forum');
+            $helpbutton = new moodle_help_icon();
+            $helpbutton->page = "subscription";
+            $helpbutton->text = $string;
+            $helpbutton->module = "forum";
+            if (has_capability('mod/forum:managesubscriptions', $PAGE->cm->context)) {
+                $url = new moodle_url($CFG->wwwroot.'/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'force'=>'no'));
+                $forum->add($string, null, null, settings_navigation::TYPE_SETTING, $url);
+            } else {
+                $forum->add(get_string('everyoneisnowsubscribed', 'forum'));
+            }
+        } else if ($forumobject->forcesubscribe == FORUM_DISALLOWSUBSCRIBE) {
+            $string = get_string('disallowsubscribe', 'forum');
+            $notekey = $forum->add($string);
+            $helpbutton = new moodle_help_icon();
+            $helpbutton->page = "subscription";
+            $helpbutton->text = $string;
+            $helpbutton->module = "forum";
+        } else {
+            $string = get_string("forcesubscribe", "forum");
+            $notekey = $forum->add(get_string("allowsallsubscribe", 'forum'));
+
+            $helpbutton = new moodle_help_icon();
+            $helpbutton->page = "subscription";
+            $helpbutton->text = $string;
+            $helpbutton->module = "forum";
+
+            if (has_capability('mod/forum:managesubscriptions', $PAGE->cm->context)) {
+                $url = new moodle_url($CFG->wwwroot.'/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'force'=>'yes'));
+                $forum->add($string, null, null, settings_navigation::TYPE_SETTING, $url);
+            } else {
+                $forum->add(get_string('everyonecannowchoose', 'forum'));
+            }
+            if(has_capability('mod/forum:viewsubscribers', $PAGE->cm->context)){
+                $url = new moodle_url($CFG->wwwroot.'/mod/forum/subscribers.php', array('id'=>$forumobject->id));
+                $forum->add(get_string('showsubscribers', 'forum'), null, null, settings_navigation::TYPE_SETTING, $url);
+            }
+            
+            if (forum_is_forcesubscribed($forumobject) || ($forumobject->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $PAGE->cm->context))) {
+                // Do nothing
+            } else {
+                if (forum_is_subscribed($USER->id, $forumobject)) {
+                    $linktext = get_string('unsubscribe', 'forum');
+                } else {
+                    $linktext = get_string('subscribe', 'forum');
+                }
+                $url = new moodle_url($CFG->wwwroot . '/mod/forum/subscribe.php', array('id'=>$forumobject->id));
+                $forum->add($linktext, null, null, settings_navigation::TYPE_SETTING, $url);
+            }
+        }
+
+        if (forum_tp_can_track_forums($forumobject)) {
+            if (forum_tp_is_tracked($forumobject)) {
+                $linktext = get_string('notrackforum', 'forum');
+            } else {
+                $linktext = get_string('trackforum', 'forum');
+            }
+            $url = new moodle_url($CFG->wwwroot . '/mod/forum/settracking.php', array('id'=>$forumobject->id));
+            $forum->add($linktext, null, null, settings_navigation::TYPE_SETTING, $url);
+        }
+        if ($notekey!==false) {
+            $forum->get($notekey)->add_class('note');
+            if ($helpbutton!==false) {
+                $forum->get($notekey)->helpbutton = $OUTPUT->help_icon($helpbutton);
+            }
+        }
+        if (has_capability('moodle/course:manageactivities', $PAGE->cm->context)) {
+            $modulename = get_string('modulename', 'forum');
+            $string = get_string('updatethis', '', $modulename);
+            $url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $PAGE->cm->id, 'return' => true, 'sesskey' => sesskey()));
+            $forum->add($string, null, null, settings_navigation::TYPE_SETTING, $url);
+        }
+    }
+
+    if (!empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds) && $forumobject->rsstype && $forumobject->rssarticles) {
+
+        if (!function_exists('rss_get_url')) {
+            require_once("$CFG->libdir/rsslib.php");
+        }
+
+        if ($forumobject->rsstype == 1) {
+            $string = get_string("rsssubscriberssdiscussions","forum",format_string($forumobject->name));
+        } else {
+            $string = get_string("rsssubscriberssposts","forum",format_string($forumobject->name));
+        }
+        if (empty($USER->id)) {
+            $userid = 0;
+        } else {
+            $userid = $USER->id;
+        }
+        $url = new moodle_url(rss_get_url($PAGE->course->id, $userid, "forum", $forumobject->id));
+        $forum->add($string, null, null, settings_navigation::TYPE_SETTING, $url, $OUTPUT->old_icon_url('i/rss'));
+    }
+
+    return $forumkey;
+}
\ No newline at end of file
index 5be3c7810dbef2fd831129bad4cc3708554a3049..b1e0f452691107fe210f8465f3ff34c67c3bdcac 100644 (file)
@@ -42,8 +42,6 @@
         require_course_login($course, true, $cm);
         $strforums = get_string("modulenameplural", "forum");
         $strforum = get_string("modulename", "forum");
-        $PAGE->set_button(update_module_button($cm->id, $course->id, $strforum));
-
     } else if ($f) {
 
         if (! $forum = $DB->get_record("forum", array("id" => $f))) {
@@ -61,8 +59,6 @@
         require_course_login($course, true, $cm);
         $strforums = get_string("modulenameplural", "forum");
         $strforum = get_string("modulename", "forum");
-        $PAGE->set_button(update_module_button($cm->id, $course->id, $strforum));
-
     } else {
         print_error('missingparameter');
     }
 
 
 /// Print header.
-    $navigation = build_navigation('', $cm);
     $PAGE->set_title(format_string($forum->name));
     $PAGE->set_heading(format_string($course->fullname));
-    echo $OUTPUT->header($navigation, navmenu($course, $cm));
+    echo $OUTPUT->header(navmenu($course, $cm));
 
 /// Some capability checks.
     if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities', $context)) {
         }
     }
 
-
-//    print_box_start('forumcontrol clearfix');
-
-//    print_box_start('subscription clearfix');
-    echo '<div class="subscription">';
-
-    if (!empty($USER->id) && !has_capability('moodle/legacy:guest', $context, NULL, false)) {
-        $SESSION->fromdiscussion = "$FULLME";
-        if (forum_is_forcesubscribed($forum)) {
-            $streveryoneisnowsubscribed = get_string('everyoneisnowsubscribed', 'forum');
-            $strallowchoice = get_string('allowchoice', 'forum');
-            echo '<span class="helplink">' . get_string("forcessubscribe", 'forum') . '</span><br />';
-            echo $OUTPUT->help_icon(moodle_help_icon::make("subscription", $strallowchoice, "forum"));
-            echo '&nbsp;<span class="helplink">';
-            if (has_capability('mod/forum:managesubscriptions', $context)) {
-                echo "<a title=\"$strallowchoice\" href=\"subscribe.php?id=$forum->id&amp;force=no\">$strallowchoice</a>";
-            } else {
-                echo $streveryoneisnowsubscribed;
-            }
-            echo '</span><br />';
-
-        } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE) {
-            $strsubscriptionsoff = get_string('disallowsubscribe','forum');
-            echo $strsubscriptionsoff;
-            echo $OUTPUT->help_icon(moodle_help_icon::make("subscription", $strsubscriptionsoff, "forum"));
-        } else {
-            $streveryonecannowchoose = get_string("everyonecannowchoose", "forum");
-            $strforcesubscribe = get_string("forcesubscribe", "forum");
-            $strshowsubscribers = get_string("showsubscribers", "forum");
-            echo '<span class="helplink">' . get_string("allowsallsubscribe", 'forum') . '</span><br />';
-            echo $OUTPUT->help_icon(moodle_help_icon::make("subscription", $strforcesubscribe, "forum"));
-            echo '&nbsp;';
-
-            if (has_capability('mod/forum:managesubscriptions', $context)) {
-                echo "<span class=\"helplink\"><a title=\"$strforcesubscribe\" href=\"subscribe.php?id=$forum->id&amp;force=yes\">$strforcesubscribe</a></span>";
-            } else {
-                echo '<span class="helplink">'.$streveryonecannowchoose.'</span>';
-            }
-
-            if(has_capability('mod/forum:viewsubscribers', $context)){
-                echo "<br />";
-                echo "<span class=\"helplink\"><a href=\"subscribers.php?id=$forum->id\">$strshowsubscribers</a></span>";
-            }
-
-            echo '<div class="helplink" id="subscriptionlink">', forum_get_subscribe_link($forum, $context,
-                    array('forcesubscribed' => '', 'cantsubscribe' => '')), '</div>';
-        }
-
-        if (forum_tp_can_track_forums($forum)) {
-            echo '<div class="helplink" id="trackinglink">'. forum_get_tracking_link($forum). '</div>';
-        }
-
-    }
-
-    /// If rss are activated at site and forum level and this forum has rss defined, show link
-    if (isset($CFG->enablerssfeeds) && isset($CFG->forum_enablerssfeeds) &&
-        $CFG->enablerssfeeds && $CFG->forum_enablerssfeeds && $forum->rsstype and $forum->rssarticles) {
-
-        if ($forum->rsstype == 1) {
-            $tooltiptext = get_string("rsssubscriberssdiscussions","forum",format_string($forum->name));
-        } else {
-            $tooltiptext = get_string("rsssubscriberssposts","forum",format_string($forum->name));
-        }
-        if (empty($USER->id)) {
-            $userid = 0;
-        } else {
-            $userid = $USER->id;
-        }
-//        print_box_start('rsslink');
-        echo '<span class="wrap rsslink">';
-        rss_print_link($course->id, $userid, "forum", $forum->id, $tooltiptext);
-        echo '</span>';
-//        print_box_end(); // subscription
-
-    }
-//    print_box_end(); // subscription
-    echo '</div>';
-
-//    print_box_end();  // forumcontrol
-
-//    print_box('&nbsp;', 'clearer');
-
-
     if (!empty($forum->blockafter) && !empty($forum->blockperiod)) {
         $a->blockafter = $forum->blockafter;
         $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
diff --git a/pix/t/collapsed_empty.png b/pix/t/collapsed_empty.png
new file mode 100644 (file)
index 0000000..8c6ee9e
Binary files /dev/null and b/pix/t/collapsed_empty.png differ
diff --git a/pix/t/movetoblock.png b/pix/t/movetoblock.png
new file mode 100644 (file)
index 0000000..5ace5af
Binary files /dev/null and b/pix/t/movetoblock.png differ
diff --git a/pix/t/movetosidetab.png b/pix/t/movetosidetab.png
new file mode 100644 (file)
index 0000000..d72690c
Binary files /dev/null and b/pix/t/movetosidetab.png differ
diff --git a/pix/t/reload.png b/pix/t/reload.png
new file mode 100644 (file)
index 0000000..34e31de
Binary files /dev/null and b/pix/t/reload.png differ
diff --git a/theme/standard/gradient_vertical.jpg b/theme/standard/gradient_vertical.jpg
new file mode 100644 (file)
index 0000000..c930c05
Binary files /dev/null and b/theme/standard/gradient_vertical.jpg differ
index 7e6a41062d3b4ab5723e7d6fadb3a2eb48901652..8227aa41bcdeab1308b7a6dc2e14c92efd8bb0c6 100644 (file)
@@ -17,9 +17,9 @@
     </div>
 <?php } ?>
 
-<?php if ($navigation) { // This is the navigation bar with breadcrumbs  ?>
+<?php if ($OUTPUT->has_navbar()) { // This is the navigation bar with breadcrumbs  ?>
     <div class="navbar clearfix">
-        <div class="breadcrumb"><?php print_navigation($navigation); ?></div>
+        <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
         <div class="navbutton"><?php echo $PAGE->button; ?></div>
     </div>
 <?php } else if ($PAGE->heading) { // If no navigation, but a heading, then print a line ?>
diff --git a/theme/standard/shadow_corners.png b/theme/standard/shadow_corners.png
new file mode 100644 (file)
index 0000000..7b15825
Binary files /dev/null and b/theme/standard/shadow_corners.png differ
diff --git a/theme/standard/shadow_left_right.png b/theme/standard/shadow_left_right.png
new file mode 100644 (file)
index 0000000..4ea98dc
Binary files /dev/null and b/theme/standard/shadow_left_right.png differ
diff --git a/theme/standard/shadow_top_bottom.png b/theme/standard/shadow_top_bottom.png
new file mode 100644 (file)
index 0000000..7ee3c7e
Binary files /dev/null and b/theme/standard/shadow_top_bottom.png differ
index a14666476e0c7d738283fb139484661853523f98..41c15a2d3c86b59ae612a40689d541a955cc2b4d 100644 (file)
@@ -1792,6 +1792,180 @@ a.skip:focus, a.skip:active {
  padding:3px;
 }
 
+/* Navigation and settings block */
+.sideblock .block_tree_box {
+    margin:2px;
+}
+.sideblock .block_tree {
+  width:180px;
+  overflow-x:auto;
+  overflow-y:visible;
+}
+.ie6 .sideblock .block_tree,
+.ie7 .sideblock .block_tree {
+  overflow-x:scroll;
+}
+.block_tree {
+  list-style: none;
+  padding-left:0px;
+  margin:3px;
+}
+.block_tree ul {
+  margin-left: 0px;
+  padding-left:16px;
+  list-style: none;
+}
+.block_tree .current_branch {
+  background-color:#eee;
+}
+.block_tree .tree_item {
+  white-space:nowrap;
+  margin:2px 0px;
+  padding-left: 16px;
+  margin:3px 0px;
+  white-space:nowrap;
+}
+.ie6 .block_tree .tree_item {
+  width:100%;
+}
+.block_tree .tree_item.note {
+  white-space:normal;
+  font-size:90%;
+}
+.block_tree .tree_item.branch {
+  background-image: url(../../pix/t/expanded.png);
+  background-position: center left;
+  background-repeat: no-repeat;
+}
+.block_tree .root_node.leaf {
+  padding-left:0px;
+}
+.jsenabled .sideblock_js_sidebarpopout,
+.jsenabled .block_tree .collapsed ul {
+  display: none;
+}
+.jsenabled .block_tree .tree_item.branch {
+  cursor:pointer;
+}
+.jsenabled .block_tree .collapsed .tree_item.branch {
+  background-image: url(../../pix/t/collapsed.png);
+}
+.jsenabled .block_tree .tree_item.emptybranch {
+  background-image: url(../../pix/t/collapsed_empty.png);
+  background-position: center left;
+  background-repeat: no-repeat;
+}
+.block_tree_box .requiresjs {
+  display:none;
+}
+.jsenabled .block_tree_box .requiresjs {
+  display:inline;
+}
+/* Navigation and settings block */
+/* This CSS is for the side panel */
+body.has_navigation_bar {
+  margin-left:3em;
+}
+.navigation_bar {
+  width:30px;
+  position:fixed;
+  top:0px;
+  left:0px;
+  height:100%;
+  border-right:1px solid #bbb;
+  border-bottom:1px solid #bbb;
+  background-image:url(gradient_vertical.jpg);
+  background-position:100% 0%;
+  background-repeat:repeat-y;
+}
+.ie6 .navigation_bar {
+  position:absolute;
+}
+.ie6 .navigation_bar hr {
+  display:none;
+  margin:0px;
+  height:0px;
+  padding:0px;
+}
+.ie6 .navigation_bar li p {
+  background-color:inherit;
+}
+.navigation_bar .sideblock_tab {
+  
+}
+.navigation_bar .sideblock_tab .firsttab {
+  margin-top:1em;
+}
+.navigation_bar .sideblock_tab .title {
+  border-bottom:1px solid #eee;
+  border-top:1px solid #ddd;
+  cursor:pointer;
+}
+.navigation_bar .bd.oversized_content {
+  background-color:red;
+  margin:3px;
+  overflow-y:auto;
+  overflow-x:visible;
+  height:inherit;
+}
+.navigation_bar .block_tree .current_branch {
+  background-color:#ddd;
+}
+.ie6 .navigation_bar .bd.oversized_content {
+  width:100%;
+}
+.ie7 .navigation_bar .bd.oversized_content {
+  width:400px;
+}
+.navigation_bar .bd.oversized_content .content {
+  margin:6px 6px 6px 0px;
+  padding-bottom:6px;
+}
+.ie6 .navigation_bar .bd.oversized_content .content,
+.ie7 .navigation_bar .bd.oversized_content .content {
+  padding-bottom:0px;
+}
+.navigation_bar .sideblock_tab .title.active_tab {
+  background-color:#e9e9e9;
+  border-bottom-color:#ccc;
+  border-top-color:#ddd;
+}
+.navigation_bar .sideblock_tab .title h2 {
+  font-size:0.8em;
+  line-height:100%;
+  text-transform:uppercase;
+  text-align:center;
+}
+.navigation_bar .controls {
+  position:absolute;
+  bottom:1em;
+  width:100%;
+  text-align:center;
+}
+.navigation_bar .controls img {
+  cursor:pointer;
+}
+
+/* Navigation and settings block */
+/* Sideblock expansion code */
+.sideblock_js_expansion .block_tree {
+  overflow-x:scroll;
+}
+.sideblock_js_expansion.mouseover .content {
+  width:200%;
+  z-index:1000;
+  position:relative;
+}
+.sideblock_js_expansion.mouseover .content .block_tree {
+  width:100%;
+  background-color:#fcfcfc;
+  padding-bottom:0px;
+}
+.ie6 .sideblock_js_expansion.mouseover .content,
+.ie7 .sideblock_js_expansion.mouseover .content{
+  padding-bottom:2px;
+}
+
 /***
  *** Blogs
  ***/
@@ -5595,3 +5769,63 @@ wikiadminactions {
   margin: 4px;
 }
 
+/* CSS for shadows */
+.divshadow div {
+  position:absolute;
+  width:10px;
+  height:10px;
+  background-image:url(shadow_corners.png);
+  background-repeat:no-repeat;
+}
+.divshadow .shadow_top {
+  top:-10px;
+  right:0px;
+  width:100%;
+  background-image:url(shadow_top_bottom.png);
+  background-position:0px 0px;
+  background-repeat:repeat-x;
+}
+.divshadow .shadow_bottom {
+  bottom:-10px;
+  right:0px;
+  width:100%;
+  background-image:url(shadow_top_bottom.png);
+  background-position:0px 100%;
+  background-repeat:repeat-x;
+}
+.divshadow .shadow_left {
+  top:0px;
+  left:-10px;
+  height:100%;
+  background-image:url(shadow_left_right.png);
+  background-position:0px 0px;
+  background-repeat:repeat-y;
+}
+.divshadow .shadow_right {
+  bottom:0px;
+  right:-10px;
+  height:100%;
+  background-image:url(shadow_left_right.png);
+  background-position:100% 0px;
+  background-repeat:repeat-y;
+}
+.divshadow .shadow_top_right {
+  top:-10px;
+  right:-10px;
+  background-position:100% 0px;
+}
+.divshadow .shadow_bottom_right {
+  bottom:-10px;
+  right:-10px;
+  background-position:100% 100%;
+}
+.divshadow .shadow_top_left {
+  top:-10px;
+  left:-10px;
+  background-position:0px 0px;
+}
+.divshadow .shadow_bottom_left {
+  bottom:-10px;
+  left:-10px;
+  background-position:0px 100%;
+}
\ No newline at end of file
index 8d2da76cc8d1172b3f37cc8a0f490011353232ea..a2238b2af78ba22fd765bd21d70fa82834c5202b 100644 (file)
             $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
         }
 
-        $edittype = 'none';
-        if (isguestuser($user)) {
-            // guest account can not be edited
-
-        } else if (is_mnet_remote_user($user)) {
-            // cannot edit remote users
-
-        } else if (isguestuser() or !isloggedin()) {
-            // guests and not logged in can not edit own profile
-
-        } else if ($USER->id == $user->id) {
-            if (has_capability('moodle/user:update', $systemcontext)) {
-                $edittype = 'advanced';
-            } else if (has_capability('moodle/user:editownprofile', $systemcontext)) {
-                $edittype = 'normal';
-            }
-
-        } else {
-            if (has_capability('moodle/user:update', $systemcontext) and !is_primary_admin($user->id)){
-                $edittype = 'advanced';
-            } else if (has_capability('moodle/user:editprofile', $personalcontext) and !is_primary_admin($user->id)){
-                //teachers, parents, etc.
-                $edittype = 'normal';
-            }
-        }
-
-        if ($edittype == 'advanced') {
-            $toprow[] = new tabobject('editprofile', $wwwroot.'/user/editadvanced.php?id='.$user->id.'&amp;course='.$course->id, get_string('editmyprofile'));
-        } else if ($edittype == 'normal') {
-            $toprow[] = new tabobject('editprofile', $wwwroot.'/user/edit.php?id='.$user->id.'&amp;course='.$course->id, get_string('editmyprofile'));
-        }
-
     /// Everyone can see posts for this user
 
     /// add logic to see course read posts permission
         }
     }    //close last bracket (individual tags)
 
-
-    /// this needs permission checkings
-
-
-    if (!empty($showroles) and !empty($user)) { // this variable controls whether this roles is showed, or not, so only user/view page should set this flag
-        $usercontext = get_context_instance(CONTEXT_USER, $user->id);
-        if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride',
-                'moodle/role:override', 'moodle/role:manage'), $usercontext)) {
-            $toprow[] = new tabobject('roles', $CFG->wwwroot.'/'.$CFG->admin.'/roles/usersroles.php?userid='.$user->id.'&amp;courseid='.$course->id
-                    ,get_string('roles'));
-
-            if (in_array($currenttab, array('usersroles', 'assign', 'override', 'check'))) {
-                $inactive = array('roles');
-                $activetwo = array('roles');
-
-                $secondrow = array();
-                $secondrow[] = new tabobject('usersroles', $CFG->wwwroot.'/'.$CFG->admin.'/roles/usersroles.php?userid='.$user->id.'&amp;courseid='.$course->id
-                        ,get_string('thisusersroles', 'role'));
-                if (!empty($assignableroles) || $currenttab=='assign') {
-                    $secondrow[] = new tabobject('assign', $CFG->wwwroot.'/'.$CFG->admin.'/roles/assign.php?contextid='.$usercontext->id.'&amp;userid='.$user->id.'&amp;courseid='.$course->id
-                            ,get_string('assignrolesrelativetothisuser', 'role'), '', true);
-                }
-                if (!empty($overridableroles) || $currenttab=='override') {
-                    $secondrow[] = new tabobject('override', $CFG->wwwroot.'/'.$CFG->admin.'/roles/override.php?contextid='.$usercontext->id.'&amp;userid='.$user->id.'&amp;courseid='.$course->id
-                            ,get_string('overridepermissions', 'role'), '', true);
-                }
-                if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride',
-                        'moodle/role:override', 'moodle/role:assign'), $usercontext)) {
-                    $secondrow[] = new tabobject('check',
-                            $CFG->wwwroot.'/'.$CFG->admin.'/roles/check.php?contextid='.$usercontext->id.'&amp;userid='.$user->id.'&amp;courseid='.$course->id,
-                            get_string('checkpermissions', 'role'));
-                }
-            }
-        }
-    }
-
-    if (!empty($user) and empty($userindexpage) && $user->id == $USER->id && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', get_system_context())) {
-
-        /// Portfolio tab
-        if (portfolio_instances(true, false)) {
-            $toprow[] = new tabobject('portfolios', $CFG->wwwroot .'/user/portfolio.php', get_string('portfolios', 'portfolio'));
-            if (in_array($currenttab, array('portfolioconf', 'portfoliologs'))) {
-                $inactive = array('portfolios');
-                $activetwo = array('portfolios');
-                $secondrow = array();
-                $secondrow[] = new tabobject('portfolioconf', $CFG->wwwroot . '/user/portfolio.php', get_string('configure', 'portfolio'));
-                $secondrow[] = new tabobject('portfoliologs', $CFG->wwwroot . '/user/portfoliologs.php', get_string('logs', 'portfolio'));
-            }
-        }
-    }
-
     // Repository Tab
     if (!empty($user) and $user->id == $USER->id) {
         require_once($CFG->dirroot . '/repository/lib.php');
 
     }
 
-    /// Messaging tab
-    if (!empty($user) and empty($userindexpage) and has_capability('moodle/user:editownmessageprofile', $systemcontext)) {
-        $toprow[] = new tabobject('editmessage', $CFG->wwwroot.'/message/edit.php?id='.$user->id.'&amp;course='.$course->id, get_string('editmymessage', 'message'));
-    }
-
-
 /// Add second row to display if there is one
 
     if (!empty($secondrow)) {
index a48d4d8934d89539afedecd2e0ee6f1293ee8475..ca3f52d2d657c179e569081194d6f10e5ef594dc 100644 (file)
@@ -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 = 2009080700;  // YYYYMMDD   = date of the last version bump
+    $version = 2009082800;  // YYYYMMDD   = date of the last version bump
                             //         XX = daily increments
 
     $release = '2.0 dev (Build: 20090828)';  // Human-friendly version name