MDL-35769 Give more flexibility for course formats to extend course navigation
authorMarina Glancy <marina@moodle.com>
Wed, 31 Oct 2012 04:52:32 +0000 (12:52 +0800)
committerMarina Glancy <marina@moodle.com>
Fri, 2 Nov 2012 02:58:21 +0000 (10:58 +0800)
global_navigation::load_course_sections() now has more parameters and ensures
that current section and activity is loaded. This will allow course formats
to display hierarchical sections in navigation and have one callback for
overwriting whole course navigation

course/format/formatlegacy.php
course/format/lib.php
lib/navigationlib.php

index e6141a3..838c6fc 100644 (file)
@@ -175,27 +175,37 @@ class format_legacy extends format_base {
      * First this function calls callback_FORMATNAME_display_content() if it exists to check
      * if the navigation should be extended at all
      *
-     * Then it calls function callback_FORMATNAME_load_content() if it exist to actually extend
+     * Then it calls function callback_FORMATNAME_load_content() if it exists to actually extend
      * navigation
      *
      * By default the parent method is called
      *
      * @param global_navigation $navigation
      * @param navigation_node $node The course node within the navigation
-     * @return array Array of sections where each element also contains the element 'sectionnode'
-     *     referring to the corresponding section node
      */
-    public function extend_course_navigation(&$navigation, navigation_node $node) {
+    public function extend_course_navigation($navigation, navigation_node $node) {
+        global $PAGE;
+        // if course format displays section on separate pages and we are on course/view.php page
+        // and the section parameter is specified, make sure this section is expanded in
+        // navigation
+        if ($navigation->includesectionnum === false) {
+            $selectedsection = optional_param('section', null, PARAM_INT);
+            if ($selectedsection !== null && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0') &&
+                    $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+                $navigation->includesectionnum = $selectedsection;
+            }
+        }
+
         // check if there are callbacks to extend course navigation
         $displayfunc = 'callback_'.$this->format.'_display_content';
         if (function_exists($displayfunc) && !$displayfunc()) {
-            return array();
+            return;
         }
         $featurefunction = 'callback_'.$this->format.'_load_content';
         if (function_exists($featurefunction) && ($course = $this->get_course())) {
-            return $featurefunction($navigation, $course, $node);
+            $featurefunction($navigation, $course, $node);
         } else {
-            return parent::extend_navigation($navigation, $node);
+            parent::extend_course_navigation($navigation, $node);
         }
     }
 
index 0b2cbd5..d7ebe40 100644 (file)
@@ -376,16 +376,34 @@ abstract class format_base {
     /**
      * Loads all of the course sections into the navigation
      *
+     * This method is called from {@link global_navigation::load_course_sections()}
+     *
      * By default the method {@link global_navigation::load_generic_course_sections()} is called
      *
+     * When overwriting please note that navigationlib relies on using the correct values for
+     * arguments $type and $key in {@link navigation_node::add()}
+     *
+     * Example of code creating a section node:
+     * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
+     * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
+     *
+     * Example of code creating an activity node:
+     * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
+     * if (global_navigation::module_extends_navigation($activity->modname)) {
+     *     $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
+     * } else {
+     *     $activitynode->nodetype = navigation_node::NODETYPE_LEAF;
+     * }
+     *
+     * Also note that if $navigation->includesectionnum is not null, the section with this relative
+     * number needs is expected to be loaded
+     *
      * @param global_navigation $navigation
      * @param navigation_node $node The course node within the navigation
-     * @return array Array of sections where each element also contains the element 'sectionnode'
-     *     referring to the corresponding section node
      */
-    public function extend_course_navigation(&$navigation, navigation_node $node) {
+    public function extend_course_navigation($navigation, navigation_node $node) {
         if ($course = $this->get_course()) {
-            return $navigation->load_generic_course_sections($course, $node);
+            $navigation->load_generic_course_sections($course, $node);
         }
         return array();
     }
index ba6a828..dda123b 100644 (file)
@@ -1264,7 +1264,7 @@ class global_navigation extends navigation_node {
                     // already been loaded.
                     // However we need to check if there is more content that can
                     // yet be loaded for the specific module instance.
-                    $activitynode = $this->rootnodes['site']->get($this->page->cm->id, navigation_node::TYPE_ACTIVITY);
+                    $activitynode = $this->rootnodes['site']->find($this->page->cm->id, navigation_node::TYPE_ACTIVITY);
                     if ($activitynode) {
                         $this->load_activity($this->page->cm, $this->page->course, $activitynode);
                     }
@@ -1297,62 +1297,15 @@ class global_navigation extends navigation_node {
 
                 $this->add_course_essentials($coursenode, $course);
 
-                // Get section number from $cm (if provided) - we need this
-                // before loading sections in order to tell it to load this section
-                // even if it would not normally display (=> it contains only
-                // a label, which we are now editing)
-                $sectionnum = isset($cm->sectionnum) ? $cm->sectionnum : 0;
-                if ($sectionnum) {
-                    // This value has to be stored in a member variable because
-                    // otherwise we would have to pass it through a public API
-                    // to course formats and they would need to change their
-                    // functions to pass it along again...
-                    $this->includesectionnum = $sectionnum;
-                } else {
-                    $this->includesectionnum = false;
-                }
-
                 // Load the course sections into the page
-                $sections = $this->load_course_sections($course, $coursenode);
-                if ($course->id != $SITE->id) {
-                    // Find the section for the $CM associated with the page and collect
-                    // its section number.
-                    if ($sectionnum) {
-                        $cm->sectionnumber = $sectionnum;
-                    } else {
-                        foreach ($sections as $section) {
-                            if ($section->id == $cm->section) {
-                                $cm->sectionnumber = $section->section;
-                                break;
-                            }
-                        }
-                    }
-
-                    // Load all of the section activities for the section the cm belongs to.
-                    if (isset($cm->sectionnumber) and !empty($sections[$cm->sectionnumber])) {
-                        list($sectionarray, $activityarray) = $this->generate_sections_and_activities($course);
-                        $activities = $this->load_section_activities($sections[$cm->sectionnumber]->sectionnode, $cm->sectionnumber, $activityarray);
-                    } else {
-                        $activities = array();
-                        if ($activity = $this->load_stealth_activity($coursenode, get_fast_modinfo($course))) {
-                            // "stealth" activity from unavailable section
-                            $activities[$cm->id] = $activity;
-                        }
-                    }
-                } else {
-                    $activities = array();
-                    $activities[$cm->id] = $coursenode->get($cm->id, navigation_node::TYPE_ACTIVITY);
-                }
-                if (!empty($activities[$cm->id])) {
-                    // Finally load the cm specific navigaton information
-                    $this->load_activity($cm, $course, $activities[$cm->id]);
-                    // Check if we have an active ndoe
-                    if (!$activities[$cm->id]->contains_active_node() && !$activities[$cm->id]->search_for_active_node()) {
-                        // And make the activity node active.
-                        $activities[$cm->id]->make_active();
-                    }
-                } else {
-                    //TODO: something is wrong, what to do? (Skodak)
+                $this->load_course_sections($course, $coursenode, null, $cm);
+                $activity = $coursenode->find($cm->id, navigation_node::TYPE_ACTIVITY);
+                // Finally load the cm specific navigaton information
+                $this->load_activity($cm, $course, $activity);
+                // Check if we have an active ndoe
+                if (!$activity->contains_active_node() && !$activity->search_for_active_node()) {
+                    // And make the activity node active.
+                    $activity->make_active();
                 }
                 break;
             case CONTEXT_USER :
@@ -1857,22 +1810,36 @@ class global_navigation extends navigation_node {
     /**
      * Loads all of the courses section into the navigation.
      *
-     * This function utilisies a callback that can be implemented within the course
-     * formats lib.php file to customise the navigation that is generated at this
-     * point for the course.
+     * This function calls method from current course format, see
+     * {@link format_base::extend_course_navigation()}
+     * If course module ($cm) is specified but course format failed to create the node,
+     * the activity node is created anyway.
      *
-     * By default (if not defined) the method {@link global_navigation::load_generic_course_sections()} is
-     * called instead.
+     * By default course formats call the method {@link global_navigation::load_generic_course_sections()}
      *
      * @param stdClass $course Database record for the course
      * @param navigation_node $coursenode The course node within the navigation
-     * @return array Array of navigation nodes for the section with key = section id
+     * @param null|int $sectionnum If specified load the contents of section with this relative number
+     * @param null|cm_info $cm If specified make sure that activity node is created (either
+     *    in containg section or by calling load_stealth_activity() )
      */
-    protected function load_course_sections(stdClass $course, navigation_node $coursenode) {
-        global $CFG;
+    protected function load_course_sections(stdClass $course, navigation_node $coursenode, $sectionnum = null, $cm = null) {
+        global $CFG, $SITE;
         require_once($CFG->dirroot.'/course/lib.php');
-        return course_get_format($course)->extend_course_navigation($this, $coursenode);
-    }
+        if (isset($cm->sectionnum)) {
+            $sectionnum = $cm->sectionnum;
+        }
+        if ($sectionnum !== null) {
+            $this->includesectionnum = $sectionnum;
+        }
+        course_get_format($course)->extend_course_navigation($this, $coursenode, $sectionnum, $cm);
+        if (isset($cm->id)) {
+            $activity = $coursenode->find($cm->id, self::TYPE_ACTIVITY);
+            if (empty($activity)) {
+                $activity = $this->load_stealth_activity($coursenode, get_fast_modinfo($course));
+            }
+        }
+   }
 
     /**
      * Generates an array of sections and an array of activities for the given course.
@@ -1952,11 +1919,6 @@ class global_navigation extends navigation_node {
 
         list($sections, $activities) = $this->generate_sections_and_activities($course);
 
-        $key = 0;
-        if (defined('AJAX_SCRIPT') && AJAX_SCRIPT == '0' && $this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
-            $key = optional_param('section', $key, PARAM_INT);
-        }
-
         $navigationsections = array();
         foreach ($sections as $sectionid => $section) {
             $section = clone($section);
@@ -1974,8 +1936,7 @@ class global_navigation extends navigation_node {
                 $sectionnode = $coursenode->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
                 $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
                 $sectionnode->hidden = (!$section->visible || !$section->available);
-                if ($key != '0' && $section->section != '0' && $section->section == $key && $this->page->context->contextlevel != CONTEXT_MODULE && $section->hasactivites) {
-                    $sectionnode->make_active();
+                if ($this->includesectionnum !== false && $this->includesectionnum == $section->section) {
                     $this->load_section_activities($sectionnode, $section->section, $activities);
                 }
                 $section->sectionnode = $sectionnode;
@@ -1984,6 +1945,7 @@ class global_navigation extends navigation_node {
         }
         return $navigationsections;
     }
+
     /**
      * Loads all of the activities for a section into the navigation structure.
      *
@@ -2112,7 +2074,6 @@ class global_navigation extends navigation_node {
             $modinfo = get_fast_modinfo($course);
             $cm = $modinfo->get_cm($cm->id);
         }
-
         $activity->nodetype = navigation_node::NODETYPE_LEAF;
         $activity->make_active();
         $file = $CFG->dirroot.'/mod/'.$cm->modname.'/lib.php';
@@ -2396,7 +2357,7 @@ class global_navigation extends navigation_node {
      * @param string $modname
      * @return bool
      */
-    protected static function module_extends_navigation($modname) {
+    public static function module_extends_navigation($modname) {
         global $CFG;
         static $extendingmodules = array();
         if (!array_key_exists($modname, $extendingmodules)) {
@@ -2817,9 +2778,7 @@ class global_navigation_for_ajax extends global_navigation {
                 $this->page->set_context(context_course::instance($course->id));
                 $coursenode = $this->add_course($course);
                 $this->add_course_essentials($coursenode, $course);
-                $sections = $this->load_course_sections($course, $coursenode);
-                list($sectionarray, $activities) = $this->generate_sections_and_activities($course);
-                $this->load_section_activities($sections[$course->sectionnumber]->sectionnode, $course->sectionnumber, $activities);
+                $this->load_course_sections($course, $coursenode, $course->sectionnumber);
                 break;
             case self::TYPE_ACTIVITY :
                 $sql = "SELECT c.*
@@ -2833,14 +2792,10 @@ class global_navigation_for_ajax extends global_navigation {
                 require_course_login($course, true, $cm, false, true);
                 $this->page->set_context(context_module::instance($cm->id));
                 $coursenode = $this->load_course($course);
-                if ($course->id == $SITE->id) {
-                    $modulenode = $this->load_activity($cm, $course, $coursenode->find($cm->id, self::TYPE_ACTIVITY));
-                } else {
-                    $sections   = $this->load_course_sections($course, $coursenode);
-                    list($sectionarray, $activities) = $this->generate_sections_and_activities($course);
-                    $activities = $this->load_section_activities($sections[$cm->sectionnum]->sectionnode, $cm->sectionnum, $activities);
-                    $modulenode = $this->load_activity($cm, $course, $activities[$cm->id]);
+                if ($course->id != $SITE->id) {
+                    $this->load_course_sections($course, $coursenode, null, $cm);
                 }
+                $modulenode = $this->load_activity($cm, $course, $coursenode->find($cm->id, self::TYPE_ACTIVITY));
                 break;
             default:
                 throw new Exception('Unknown type');