Merge branch 'MDL-25981'
authorPetr Skoda <commits@skodak.org>
Mon, 31 Jan 2011 21:44:17 +0000 (22:44 +0100)
committerPetr Skoda <commits@skodak.org>
Mon, 31 Jan 2011 21:44:17 +0000 (22:44 +0100)
14 files changed:
blocks/activity_modules/block_activity_modules.php
blocks/site_main_menu/block_site_main_menu.php
blocks/social_activities/block_social_activities.php
course/lib.php
course/report/outline/index.php
course/report/participation/index.php
course/resources.php
lib/modinfolib.php [new file with mode: 0644]
lib/moodlelib.php
lib/navigationlib.php
lib/setup.php
lib/weblib.php
mod/forum/lib.php
mod/label/lib.php

index adff582..c44ceb4 100644 (file)
@@ -27,7 +27,8 @@ class block_activity_modules extends block_list {
         $archetypes = array();
 
         foreach($modinfo->cms as $cm) {
-            if (!$cm->uservisible or $cm->modname === 'label') {
+            // Exclude activities which are not visible or have no link (=label)
+            if (!$cm->uservisible or !$cm->has_view()) {
                 continue;
             }
             if (array_key_exists($cm->modname, $modfullnames)) {
index 3e139ac..d3434f8 100644 (file)
@@ -40,21 +40,19 @@ class block_site_main_menu extends block_list {
                     if (!$cm->uservisible) {
                         continue;
                     }
-                    if ($cm->modname == 'label') {
-                        $this->content->items[] = format_text($cm->extra, FORMAT_HTML, $options);
+
+                    list($content, $instancename) =
+                            get_print_section_cm_text($cm, $course);
+
+                    if (!($url = $cm->get_url())) {
+                        $this->content->items[] = $content;
                         $this->content->icons[] = '';
                     } else {
                         $linkcss = $cm->visible ? '' : ' class="dimmed" ';
-                        $instancename = format_string($cm->name, true, $course->id);
                         //Accessibility: incidental image - should be empty Alt text
-                        if (!empty($cm->icon)) {
-                            $icon = $OUTPUT->pix_url($cm->icon);
-                        } else {
-                            $icon = $OUTPUT->pix_url('icon', $cm->modname);
-                        }
-                        $icon = '<img src="'.$icon.'" class="icon" alt="" />&nbsp;';
+                        $icon = '<img src="' . $cm->get_icon_url() . '" class="icon" alt="" />&nbsp;';
                         $this->content->items[] = '<a title="'.$cm->modplural.'" '.$linkcss.' '.$cm->extra.
-                            ' href="'.$CFG->wwwroot.'/mod/'.$cm->modname.'/view.php?id='.$cm->id.'">'.$icon.$instancename.'</a>';
+                                ' href="' . $url . '">' . $icon . $instancename . '</a>';
                     }
                 }
             }
@@ -114,28 +112,18 @@ class block_site_main_menu extends block_list {
                             '<img style="height:16px; width:80px; border:0px" src="'.$OUTPUT->pix_url('movehere') . '" alt="'.$strmovehere.'" /></a>';
                         $this->content->icons[] = '';
                     }
-                    $instancename = $modinfo->cms[$modnumber]->name;
-                    $instancename = format_string($instancename, true, $course->id);
+                    list($content, $instancename) =
+                            get_print_section_cm_text($modinfo->cms[$modnumber], $course);
                     $linkcss = $mod->visible ? '' : ' class="dimmed" ';
-                    if (!empty($modinfo->cms[$modnumber]->extra)) {
-                        $extra = $modinfo->cms[$modnumber]->extra;
-                    } else {
-                        $extra = '';
-                    }
-                    if (!empty($modinfo->cms[$modnumber]->icon)) {
-                        $icon = $OUTPUT->pix_url($modinfo->cms[$modnumber]->icon);
-                    } else {
-                        $icon = $OUTPUT->pix_url('icon', $mod->modname);
-                    }
 
-                    if ($mod->modname == 'label') {
-                        $this->content->items[] = format_text($extra, FORMAT_HTML,$options).$editbuttons;
+                    if (!($url = $mod->get_url())) {
+                        $this->content->items[] = $content . $editbuttons;
                         $this->content->icons[] = '';
                     } else {
                         //Accessibility: incidental image - should be empty Alt text
-                        $icon = '<img src="'.$icon.'" class="icon" alt="" />&nbsp;';
-                        $this->content->items[] = '<a title="'.$mod->modfullname.'" '.$linkcss.' '.$extra.
-                            ' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'.$icon.$instancename.'</a>'.$editbuttons;
+                        $icon = '<img src="' . $mod->get_icon_url() . '" class="icon" alt="" />&nbsp;';
+                        $this->content->items[] = '<a title="' . $mod->modfullname . '" ' . $linkcss . ' ' . $mod->extra .
+                            ' href="' . $url . '">' . $icon . $instancename . '</a>' . $editbuttons;
                     }
                 }
             }
index bcf7d7b..886e888 100644 (file)
@@ -42,21 +42,19 @@ class block_social_activities extends block_list {
                     if (!$cm->uservisible) {
                         continue;
                     }
-                    if ($cm->modname == 'label') {
-                        $this->content->items[] = format_text($cm->extra, FORMAT_HTML, $options);
+
+                    list($content, $instancename) =
+                            get_print_section_cm_text($cm, $course);
+
+                    if (!($url = $cm->get_url())) {
+                        $this->content->items[] = $content;
                         $this->content->icons[] = '';
                     } else {
                         $linkcss = $cm->visible ? '' : ' class="dimmed" ';
-                        $instancename = format_string($cm->name, true, $course->id);
                         //Accessibility: incidental image - should be empty Alt text
-                        if (!empty($cm->icon)) {
-                            $icon = $OUTPUT->pix_url($cm->icon);
-                        } else {
-                            $icon = $OUTPUT->pix_url('icon', $cm->modname);
-                        }
-                        $icon = '<img src="'.$icon.'" class="icon" alt="" />&nbsp;';
+                        $icon = '<img src="' . $cm->get_icon_url() . '" class="icon" alt="" />&nbsp;';
                         $this->content->items[] = '<a title="'.$cm->modplural.'" '.$linkcss.' '.$cm->extra.
-                            ' href="'.$CFG->wwwroot.'/mod/'.$cm->modname.'/view.php?id='.$cm->id.'">'.$icon.$instancename.'</a>';
+                                ' href="' . $url . '">' . $icon . $instancename . '</a>';
                     }
                 }
             }
@@ -123,28 +121,19 @@ class block_social_activities extends block_list {
                             '<img style="height:16px; width:80px; border:0px" src="'.$OUTPUT->pix_url('movehere') . '" alt="'.$strmovehere.'" /></a>';
                         $this->content->icons[] = '';
                     }
-                    $instancename = $modinfo->cms[$modnumber]->name;
-                    $instancename = format_string($instancename, true, $course->id);
+                    list($content, $instancename) =
+                                get_print_section_cm_text($modinfo->cms[$modnumber], $course);
+
                     $linkcss = $mod->visible ? '' : ' class="dimmed" ';
-                    if (!empty($modinfo->cms[$modnumber]->extra)) {
-                        $extra = $modinfo->cms[$modnumber]->extra;
-                    } else {
-                        $extra = '';
-                    }
-                    if (!empty($modinfo->cms[$modnumber]->icon)) {
-                        $icon = $OUTPUT->pix_url($modinfo->cms[$modnumber]->icon);
-                    } else {
-                        $icon = $OUTPUT->pix_url('icon', $mod->modname);
-                    }
 
-                    if ($mod->modname == 'label') {
-                        $this->content->items[] = format_text($extra, FORMAT_HTML, $options).$editbuttons;
+                    if (!($url = $mod->get_url())) {
+                        $this->content->items[] = $content . $editbuttons;
                         $this->content->icons[] = '';
                     } else {
                         //Accessibility: incidental image - should be empty Alt text
-                        $icon = '<img src="'.$icon.'" class="icon" alt="" />&nbsp;';
-                        $this->content->items[] = '<a title="'.$mod->modfullname.'" '.$linkcss.' '.$extra.
-                            ' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'.$icon.$instancename.'</a>'.$editbuttons;
+                        $icon = '<img src="' . $mod->get_icon_url() . '" class="icon" alt="" />&nbsp;';
+                        $this->content->items[] = '<a title="' . $mod->modfullname . '" ' . $linkcss . ' ' . $mod->extra .
+                            ' href="' . $url . '">' . $icon . $instancename . '</a>' . $editbuttons;
                     }
                 }
             }
index d5a5ba5..492275d 100644 (file)
@@ -48,10 +48,6 @@ define('FIRSTUSEDEXCELROW', 3);
 define('MOD_CLASS_ACTIVITY', 0);
 define('MOD_CLASS_RESOURCE', 1);
 
-if (!defined('MAX_MODINFO_CACHE_SIZE')) {
-    define('MAX_MODINFO_CACHE_SIZE', 10);
-}
-
 function make_log_url($module, $url) {
     switch ($module) {
         case 'course':
@@ -959,6 +955,9 @@ function print_recent_activity($course) {
             }
             $info = explode(' ', $log->info);
 
+            // note: in most cases I replaced hardcoding of label with use of
+            // $cm->has_view() but it was not possible to do this here because
+            // we don't necessarily have the $cm for it
             if ($info[0] == 'label') {     // Labels are ignored in recent activity
                 continue;
             }
@@ -1112,9 +1111,6 @@ function get_array_of_activities($courseid) {
 
                    if (function_exists($functionname)) {
                        if ($info = $functionname($rawmods[$seq])) {
-                           if (!empty($info->extra)) {
-                               $mod[$seq]->extra = $info->extra;
-                           }
                            if (!empty($info->icon)) {
                                $mod[$seq]->icon = $info->icon;
                            }
@@ -1124,11 +1120,45 @@ function get_array_of_activities($courseid) {
                            if (!empty($info->name)) {
                                $mod[$seq]->name = $info->name;
                            }
+                           if ($info instanceof cached_cm_info) {
+                               // When using cached_cm_info you can include three new fields
+                               // that aren't available for legacy code
+                               if (!empty($info->content)) {
+                                   $mod[$seq]->content = $info->content;
+                               }
+                               if (!empty($info->extraclasses)) {
+                                   $mod[$seq]->extraclasses = $info->extraclasses;
+                               }
+                               if (!empty($info->onclick)) {
+                                   $mod[$seq]->onclick = $info->onclick;
+                               }
+                               if (!empty($info->customdata)) {
+                                   $mod[$seq]->customdata = $info->customdata;
+                               }
+                           } else {
+                               // When using a stdclass, the (horrible) deprecated ->extra field
+                               // is available for BC
+                               if (!empty($info->extra)) {
+                                   $mod[$seq]->extra = $info->extra;
+                               }
+                           }
                        }
                    }
                    if (!isset($mod[$seq]->name)) {
                        $mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
                    }
+
+                   // Minimise the database size by unsetting default options when they are
+                   // 'empty'. This list corresponds to code in the cm_info constructor.
+                   foreach(array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
+                           'indent', 'completion', 'extra', 'extraclasses', 'onclick', 'content',
+                           'icon', 'iconcomponent', 'customdata', 'availablefrom', 'availableuntil',
+                           'conditionscompletion', 'conditionsgrade') as $property) {
+                       if (property_exists($mod[$seq], $property) &&
+                               empty($mod[$seq]->{$property})) {
+                           unset($mod[$seq]->{$property});
+                       }
+                   }
                }
             }
         }
@@ -1250,6 +1280,44 @@ function set_section_visible($courseid, $sectionnumber, $visibility) {
     }
 }
 
+/**
+ * Obtains shared data that is used in print_section when displaying a
+ * course-module entry.
+ *
+ * Calls format_text or format_string as appropriate, and obtains the correct icon.
+ *
+ * This data is also used in other areas of the code.
+ * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
+ * @param object $course Moodle course object
+ * @return array An array with the following values in this order:
+ *   $content (optional extra content for after link),
+ *   $instancename (text of link)
+ */
+function get_print_section_cm_text(cm_info $cm, $course) {
+    global $OUTPUT;
+
+    // Get course context
+    $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+
+    // Get content from modinfo if specified. Content displays either
+    // in addition to the standard link (below), or replaces it if
+    // the link is turned off by setting ->url to null.
+    if (($content = $cm->get_content()) !== '') {
+        $labelformatoptions = new stdClass();
+        $labelformatoptions->noclean = true;
+        $labelformatoptions->overflowdiv = true;
+        $labelformatoptions->context = $coursecontext;
+        $content = format_text($content, FORMAT_HTML, $labelformatoptions);
+    } else {
+        $content = '';
+    }
+
+    $stringoptions = new stdClass;
+    $stringoptions->context = $coursecontext;
+    $instancename = format_string($cm->name, true,  $stringoptions);
+    return array($content, $instancename);
+}
+
 /**
  * Prints a section full of activity modules
  */
@@ -1265,8 +1333,8 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
     static $strmovehere;
     static $strmovefull;
     static $strunreadpostsone;
-    static $usetracking;
     static $groupings;
+    static $modulenames;
 
     if (!isset($initialised)) {
         $groupbuttons     = ($course->groupmode or (!$course->groupmodeforce));
@@ -1277,18 +1345,12 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             $strmovehere  = get_string("movehere");
             $strmovefull  = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
         }
-        include_once($CFG->dirroot.'/mod/forum/lib.php');
-        if ($usetracking = forum_tp_can_track_forums()) {
-            $strunreadpostsone = get_string('unreadpostsone', 'forum');
-        }
+        $modulenames      = array();
         $initialised = true;
     }
 
-    $labelformatoptions = new stdClass();
-    $labelformatoptions->noclean = true;
-    $labelformatoptions->overflowdiv = true;
+    $tl = textlib_get_instance();
 
-/// Casting $course->modinfo to string prevents one notice when the field is null
     $modinfo = get_fast_modinfo($course);
     $completioninfo = new completion_info($course);
 
@@ -1304,6 +1366,9 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 continue;
             }
 
+            /**
+             * @var cm_info
+             */
             $mod = $mods[$modnumber];
 
             if ($ismoving and $mod->id == $USER->activitycopy) {
@@ -1341,6 +1406,11 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 }
             }
 
+            if (!isset($modulenames[$mod->modname])) {
+                $modulenames[$mod->modname] = get_string('modulename', $mod->modname);
+            }
+            $modulename = $modulenames[$mod->modname];
+
             // In some cases the activity is visible to user, but it is
             // dimmed. This is done if viewhiddenactivities is true and if:
             // 1. the activity is not visible, or
@@ -1366,6 +1436,10 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             $liclasses[] = 'activity';
             $liclasses[] = $mod->modname;
             $liclasses[] = 'modtype_'.$mod->modname;
+            $extraclasses = $mod->get_extra_classes();
+            if ($extraclasses) {
+                $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
+            }
             echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
             if ($ismoving) {
                 echo '<a title="'.$strmovefull.'"'.
@@ -1384,105 +1458,122 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             }
             echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
 
-            $extra = '';
-            if (!empty($modinfo->cms[$modnumber]->extra)) {
-                $extra = $modinfo->cms[$modnumber]->extra;
+            // Get data about this course-module
+            list($content, $instancename) =
+                    get_print_section_cm_text($modinfo->cms[$modnumber], $course);
+
+            //Accessibility: for files get description via icon, this is very ugly hack!
+            $altname = '';
+            $altname = $mod->modfullname;
+            if (!empty($customicon)) {
+                $archetype = plugin_supports('mod', $mod->modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
+                if ($archetype == MOD_ARCHETYPE_RESOURCE) {
+                    $mimetype = mimeinfo_from_icon('type', $customicon);
+                    $altname = get_mimetype_description($mimetype);
+                }
+            }
+            // Avoid unnecessary duplication: if e.g. a forum name already
+            // includes the word forum (or Forum, etc) then it is unhelpful
+            // to include that in the accessible description that is added.
+            if (false !== strpos($tl->strtolower($instancename),
+                    $tl->strtolower($altname))) {
+                $altname = '';
+            }
+            // File type after name, for alphabetic lists (screen reader).
+            if ($altname) {
+                $altname = get_accesshide(' '.$altname);
             }
 
-            if ($mod->modname == "label") {
-                if ($accessiblebutdim || !$mod->uservisible) {
-                    echo '<div class="dimmed_text"><span class="accesshide">'.
-                        get_string('hiddenfromstudents').'</span>';
+            // We may be displaying this just in order to show information
+            // about visibility, without the actual link
+            $contentpart = '';
+            if ($mod->uservisible) {
+                // Nope - in this case the link is fully working for user
+                $linkclasses = '';
+                $textclasses = '';
+                if ($accessiblebutdim) {
+                    $linkclasses .= ' dimmed';
+                    $textclasses .= ' dimmed_text';
+                    $accesstext = '<span class="accesshide">'.
+                        get_string('hiddenfromstudents').': </span>';
                 } else {
-                    echo '<div>';
+                    $accesstext = '';
                 }
-                echo format_text($extra, FORMAT_HTML, $labelformatoptions);
-                echo "</div>";
-                if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
-                    if (!isset($groupings)) {
-                        $groupings = groups_get_all_groupings($course->id);
-                    }
-                    echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
+                if ($linkclasses) {
+                    $linkcss = 'class="' . trim($linkclasses) . '" ';
+                } else {
+                    $linkcss = '';
+                }
+                if ($textclasses) {
+                    $textcss = 'class="' . trim($textclasses) . '" ';
+                } else {
+                    $textcss = '';
                 }
 
-            } else { // Normal activity
-                $instancename = format_string($modinfo->cms[$modnumber]->name, true,  $course->id);
+                // Get on-click attribute value if specified
+                $onclick = $mod->get_on_click();
+                if ($onclick) {
+                    $onclick = ' onclick="' . $onclick . '"';
+                }
 
-                $customicon = $modinfo->cms[$modnumber]->icon;
-                if (!empty($customicon)) {
-                    if (substr($customicon, 0, 4) === 'mod/') {
-                        list($modname, $iconname) = explode('/', substr($customicon, 4), 2);
-                        $icon = $OUTPUT->pix_url($iconname, $modname);
-                    } else {
-                        $icon = $OUTPUT->pix_url($customicon);
+                if ($url = $mod->get_url()) {
+                    // Display link itself
+                    echo '<a ' . $linkcss . $mod->extra . $onclick .
+                            ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
+                            '" class="activityicon" alt="' .
+                            $modulename . '" /> ' .
+                            $accesstext . '<span class="instancename">' .
+                            $instancename . $altname . '</span></a>';
+
+                    // If specified, display extra content after link
+                    if ($content) {
+                        $contentpart = '<div class="contentafterlink' .
+                                trim($textclasses) . '">' . $content . '</div>';
                     }
                 } else {
-                    $icon = $OUTPUT->pix_url('icon', $mod->modname);
+                    // No link, so display only content
+                    $contentpart = '<div ' . $textcss . $mod->extra . '>' .
+                            $accesstext . $content . '</div>';
                 }
 
-                //Accessibility: for files get description via icon, this is very ugly hack!
-                $altname = '';
-                $altname = $mod->modfullname;
-                if (!empty($customicon)) {
-                    $archetype = plugin_supports('mod', $mod->modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
-                    if ($archetype == MOD_ARCHETYPE_RESOURCE) {
-                        $mimetype = mimeinfo_from_icon('type', $customicon);
-                        $altname = get_mimetype_description($mimetype);
+                if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
+                    if (!isset($groupings)) {
+                        $groupings = groups_get_all_groupings($course->id);
                     }
+                    echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
                 }
-                // Avoid unnecessary duplication.
-                if (false !== stripos($instancename, $altname)) {
-                    $altname = '';
-                }
-                // File type after name, for alphabetic lists (screen reader).
-                if ($altname) {
-                    $altname = get_accesshide(' '.$altname);
+            } else {
+                $textclasses = $extraclasses;
+                $textclasses .= ' dimmed_text';
+                if ($textclasses) {
+                    $textcss = 'class="' . trim($textclasses) . '" ';
+                } else {
+                    $textcss = '';
                 }
+                $accesstext = '<span class="accesshide">' .
+                        get_string('notavailableyet', 'condition') .
+                        ': </span>';
 
-                // We may be displaying this just in order to show information
-                // about visibility, without the actual link
-                if ($mod->uservisible) {
-                    // Display normal module link
-                    if (!$accessiblebutdim) {
-                        $linkcss = '';
-                        $accesstext  ='';
-                    } else {
-                        $linkcss = ' class="dimmed" ';
-                        $accesstext = '<span class="accesshide">'.
-                            get_string('hiddenfromstudents').': </span>';
-                    }
-
-                    echo '<a '.$linkcss.' '.$extra.
-                         ' href="'.$CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id.'">'.
-                         '<img src="'.$icon.'" class="activityicon" alt="'.get_string('modulename',$mod->modname).'" /> '.
-                         $accesstext.'<span class="instancename">'.$instancename.$altname.'</span></a>';
-
-                    if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
-                        if (!isset($groupings)) {
-                            $groupings = groups_get_all_groupings($course->id);
-                        }
-                        echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
-                    }
-                } else {
+                if ($url = $mod->get_url()) {
                     // Display greyed-out text of link
-                    echo '<span class="dimmed_text" '.$extra.' ><span class="accesshide">'.
-                        get_string('notavailableyet','condition').': </span>'.
-                        '<img src="'.$icon.'" class="activityicon" alt="'.get_string('modulename', $mod->modname).'" /> <span>'.
-                        $instancename.$altname.'</span></span>';
-                }
-            }
-            if ($usetracking && $mod->modname == 'forum') {
-                if ($unread = forum_tp_count_forum_unread_posts($mod, $course)) {
-                    echo '<span class="unread"> <a href="'.$CFG->wwwroot.'/mod/forum/view.php?id='.$mod->id.'">';
-                    if ($unread == 1) {
-                        echo $strunreadpostsone;
-                    } else {
-                        print_string('unreadpostsnumber', 'forum', $unread);
-                    }
-                    echo '</a></span>';
+                    echo '<div ' . $textcss . $mod->extra .
+                            ' >' . '<img src="' . $mod->get_icon_url() .
+                            '" class="activityicon" alt="' .
+                            $modulename .
+                            '" /> <span>'. $instancename . $altname .
+                            '</span></div>';
+
+                    // Do not display content after link when it is greyed out like this.
+                } else {
+                    // No link, so display only content (also greyed)
+                    $contentpart = '<div ' . $textcss . $mod->extra . '>' .
+                            $accesstext . $content . '</div>';
                 }
             }
 
+            // Module can put text after the link (e.g. forum unread)
+            echo $mod->get_after_link();
+
             if ($isediting) {
                 if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
                     if (! $mod->groupmodelink = $groupbuttonslink) {
@@ -1494,6 +1585,7 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 }
                 echo '&nbsp;&nbsp;';
                 echo make_editing_buttons($mod, $absolute, true, $mod->indent, $section->section);
+                echo $mod->get_after_edit_icons();
             }
 
             // Completion
@@ -1566,6 +1658,9 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 }
             }
 
+            // Display the content (if any) at this part of the html
+            echo $contentpart;
+
             // Show availability information (for someone who isn't allowed to
             // see the activity itself, or for staff)
             if (!$mod->uservisible) {
index 5c151df..ad4ef64 100644 (file)
@@ -74,7 +74,7 @@
     foreach ($modinfo->sections as $sectionnum=>$section) {
         foreach ($section as $cmid) {
             $cm = $modinfo->cms[$cmid];
-            if ($cm->modname == 'label') {
+            if (!$cm->has_view()) {
                 continue;
             }
             if (!$cm->uservisible) {
index bb460b8..2ad66bb 100644 (file)
@@ -63,7 +63,7 @@
 
     $modinfo = get_fast_modinfo($course);
 
-    $modules = $DB->get_records_select('modules', "visible = 1 AND name <> 'label'", null, 'name ASC');
+    $modules = $DB->get_records_select('modules', "visible = 1", null, 'name ASC');
 
     $instanceoptions = array();
     foreach ($modules as $module) {
         }
         $instances = array();
         foreach ($modinfo->instances[$module->name] as $cm) {
+            // Skip modules such as label which do not actually have links;
+            // this means there's nothing to participate in
+            if (!$cm->has_view()) {
+                continue;
+            }
             $instances[$cm->id] = format_string($cm->name);
         }
+        if (count($instances) == 0) {
+            continue;
+        }
         $instanceoptions[] = array(get_string('modulenameplural', $module->name)=>$instances);
     }
 
index b98ced9..abf50b2 100644 (file)
@@ -37,9 +37,6 @@ $allmodules = $DB->get_records('modules', array('visible'=>1));
 $modules = array();
 foreach ($allmodules as $key=>$module) {
     $modname = $module->name;
-    if ($modname === 'label') {
-        continue;
-    }
     $libfile = "$CFG->dirroot/mod/$modname/lib.php";
     if (!file_exists($libfile)) {
         continue;
@@ -80,6 +77,10 @@ foreach ($modinfo->cms as $cm) {
     if (!array_key_exists($cm->modname, $modules)) {
         continue;
     }
+    if (!$cm->has_view()) {
+        // Exclude label and similar
+        continue;
+    }
     $cms[$cm->id] = $cm;
     $resources[$cm->modname][] = $cm->instance;
 }
diff --git a/lib/modinfolib.php b/lib/modinfolib.php
new file mode 100644 (file)
index 0000000..1265d0e
--- /dev/null
@@ -0,0 +1,1092 @@
+<?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/>.
+
+/**
+ * modinfolib.php - Functions/classes relating to cached information about module instances on
+ * a course.
+ * @package    core
+ * @subpackage lib
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     sam marshall
+ */
+
+
+// Maximum number of modinfo items to keep in memory cache. Do not increase this to a large
+// number because:
+// a) modinfo can be big (megabyte range) for some courses
+// b) performance of cache will deteriorate if there are very many items in it
+if (!defined('MAX_MODINFO_CACHE_SIZE')) {
+    define('MAX_MODINFO_CACHE_SIZE', 10);
+}
+
+
+/**
+ * Information about a course that is cached in the course table 'modinfo' field (and then in
+ * memory) in order to reduce the need for other database queries.
+ *
+ * This includes information about the course-modules and the sections on the course. It can also
+ * include dynamic data that has been updated for the current user.
+ */
+class course_modinfo {
+    // For convenience we store the course object here as it is needed in other parts of code
+    private $course;
+
+    // Existing data fields
+    ///////////////////////
+
+    // These are public for backward compatibility. Note: it is not possible to retain BC
+    // using PHP magic get methods because behaviour is different with regard to empty().
+
+    /**
+     * Course ID
+     * @var int
+     * @deprecated For new code, use get_course_id instead.
+     */
+    public $courseid;
+
+    /**
+     * User ID
+     * @var int
+     * @deprecated For new code, use get_user_id instead.
+     */
+    public $userid;
+
+    /**
+     * Array from int (section num, e.g. 0) => array of int (course-module id); this list only
+     * includes sections that actually contain at least one course-module
+     * @var array
+     * @deprecated For new code, use get_sections instead
+     */
+    public $sections;
+
+    /**
+     * Array from int (cm id) => cm_info object
+     * @var array
+     * @deprecated For new code, use get_cms or get_cm instead.
+     */
+    public $cms;
+
+    /**
+     * Array from string (modname) => int (instance id) => cm_info object
+     * @var array
+     * @deprecated For new code, use get_instances or get_instances_of instead.
+     */
+    public $instances;
+
+    /**
+     * Groups that the current user belongs to. This value is usually not available (set to null)
+     * unless the course has activities set to groupmembersonly. When set, it is an array of
+     * grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'.
+     * @var array
+     * @deprecated Don't use this! For new code, use get_groups.
+     */
+    public $groups;
+
+    // Get methods for data
+    ///////////////////////
+
+    /**
+     * @return object Moodle course object that was used to construct this data
+     */
+    public function get_course() {
+        return $this->course;
+    }
+
+    /**
+     * @return int Course ID
+     */
+    public function get_course_id() {
+        return $this->courseid;
+    }
+
+    /**
+     * @return int User ID
+     */
+    public function get_user_id() {
+        return $this->userid;
+    }
+
+    /**
+     * @return array Array from section number (e.g. 0) to array of course-module IDs in that
+     *   section; this only includes sections that contain at least one course-module
+     */
+    public function get_sections() {
+        return $this->sections;
+    }
+
+    /**
+     * @return array Array from course-module instance to cm_info object within this course, in
+     *   order of appearance
+     */
+    public function get_cms() {
+        return $this->cms;
+    }
+
+    /**
+     * Obtains a single course-module object (for a course-module that is on this course).
+     * @param int $cmid Course-module ID
+     * @return cm_info Information about that course-module
+     * @throws moodle_exception If the course-module does not exist
+     */
+    public function get_cm($cmid) {
+        if (empty($this->cms[$cmid])) {
+            throw new moodle_exception('invalidcoursemodule', 'error');
+        }
+        return $this->cms[$cmid];
+    }
+
+    /**
+     * Obtains all module instances on this course.
+     * @return array Array from module name => array from instance id => cm_info
+     */
+    public function get_instances() {
+        return $this->instances;
+    }
+
+    /**
+     * Obtains all instances of a particular module on this course.
+     * @param $modname Name of module (not full frankenstyle) e.g. 'label'
+     * @return array Array from instance id => cm_info for modules on this course; empty if none
+     */
+    public function get_instances_of($modname) {
+        if (empty($this->instances[$modname])) {
+            return array();
+        }
+        return $this->instances[$modname];
+    }
+
+    /**
+     * Returns groups that the current user belongs to on the course. Note: If not already
+     * available, this may make a database query.
+     * @param int $groupingid Grouping ID or 0 (default) for all groups
+     * @return array Array of int (group id) => int (same group id again); empty array if none
+     */
+    public function get_groups($groupingid=0) {
+        if (is_null($this->groups)) {
+            // NOTE: Performance could be improved here. The system caches user groups
+            // in $USER->groupmember[$courseid] => array of groupid=>groupid. Unfortunately this
+            // structure does not include grouping information. It probably could be changed to
+            // do so, without a significant performance hit on login, thus saving this one query
+            // each request.
+            $this->groups = groups_get_user_groups($this->courseid, $this->userid);
+        }
+        if (!isset($this->groups[$groupingid])) {
+            return array();
+        }
+        return $this->groups[$groupingid];
+    }
+
+    /**
+     * Constructs based on course.
+     * Note: This constructor should not usually be called directly.
+     * Use get_fast_modinfo($course) instead as this maintains a cache.
+     * @param object $course Moodle course object, which may include modinfo
+     * @param int $userid User ID
+     */
+    public function __construct($course, $userid) {
+        global $CFG, $DB;
+
+        // Set initial values
+        $this->courseid = $course->id;
+        $this->userid = $userid;
+        $this->sections = array();
+        $this->cms = array();
+        $this->instances = array();
+        $this->groups = null;
+        $this->course = $course;
+
+        // Check modinfo field is set. If not, build and load it.
+        if (empty($course->modinfo)) {
+            rebuild_course_cache($course->id);
+            $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
+        }
+
+        // Load modinfo field into memory as PHP object and check it's valid
+        $info = unserialize($course->modinfo);
+        if (!is_array($info)) {
+            // hmm, something is wrong - lets try to fix it
+            rebuild_course_cache($course->id);
+            $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
+            $info = unserialize($course->modinfo);
+            if (!is_array($info)) {
+                // If it still fails, abort
+                debugging('Problem with "modinfo" data for this course');
+                return;
+            }
+        }
+
+        // If we haven't already preloaded contexts for the course, do it now
+        preload_course_contexts($course->id);
+
+        // Loop through each piece of module data, constructing it
+        $modexists = array();
+        foreach ($info as $mod) {
+            if (empty($mod->name)) {
+                // something is wrong here
+                continue;
+            }
+
+            // Skip modules which don't exist
+            if (empty($modexists[$mod->mod])) {
+                if (!file_exists("$CFG->dirroot/mod/$mod->mod/lib.php")) {
+                    continue;
+                }
+                $modexists[$mod->mod] = true;
+            }
+
+            // Construct info for this module
+            $cm = new cm_info($this, $course, $mod, $info);
+
+            // Store module in instances and cms array
+            if (!isset($this->instances[$cm->modname])) {
+                $this->instances[$cm->modname] = array();
+            }
+            $this->instances[$cm->modname][$cm->instance] = $cm;
+            $this->cms[$cm->id] = $cm;
+
+            // Reconstruct sections. This works because modules are stored in order
+            if (!isset($this->sections[$cm->sectionnum])) {
+                $this->sections[$cm->sectionnum] = array();
+            }
+            $this->sections[$cm->sectionnum][] = $cm->id;
+        }
+
+        // We need at least 'dynamic' data from each course-module (this is basically the remaining
+        // data which was always present in previous version of get_fast_modinfo, so it's required
+        // for BC). Creating it in a second pass is necessary because obtain_dynamic_data sometimes
+        // needs to be able to refer to a 'complete' (with basic data) modinfo.
+        foreach ($this->cms as $cm) {
+            $cm->obtain_dynamic_data();
+        }
+    }
+}
+
+
+/**
+ * Data about a single module on a course. This contains most of the fields in the course_modules
+ * table, plus additional data when required.
+ *
+ * This object has many public fields; code should treat all these fields as read-only and set
+ * data only using the supplied set functions. Setting the fields directly is not supported
+ * and may cause problems later.
+ */
+class cm_info {
+    /**
+     * State: Only basic data from modinfo cache is available.
+     */
+    const STATE_BASIC = 0;
+
+    /**
+     * State: Dynamic data is available too.
+     */
+    const STATE_DYNAMIC = 1;
+
+    /**
+     * State: View data (for course page) is available.
+     */
+    const STATE_VIEW = 2;
+
+    /**
+     * Parent object
+     * @var course_modinfo
+     */
+    private $modinfo;
+
+    /**
+     * Level of information stored inside this object (STATE_xx constant)
+     * @var int
+     */
+    private $state;
+
+    // Existing data fields
+    ///////////////////////
+
+    /**
+     * Course-module ID - from course_modules table
+     * @var int
+     */
+    public $id;
+
+    /**
+     * Module instance (ID within module table) - from course_modules table
+     * @var int
+     */
+    public $instance;
+
+    /**
+     * Course ID - from course_modules table
+     * @var int
+     */
+    public $course;
+
+    /**
+     * 'ID number' from course-modules table (arbitrary text set by user) - from
+     * course_modules table
+     * @var string
+     */
+    public $idnumber;
+
+    /**
+     * Visible setting (0 or 1; if this is 0, students cannot see/access the activity)  - from
+     * course_modules table
+     * @var int
+     */
+    public $visible;
+
+    /**
+     * Group mode (one of the constants NONE, SEPARATEGROUPS, or VISIBLEGROUPS) - from
+     * course_modules table
+     * @var int
+     */
+    public $groupmode;
+
+    /**
+     * Grouping ID (0 = all groupings)
+     * @var int
+     */
+    public $groupingid;
+
+    /**
+     * Group members only (if set to 1, only members of a suitable group see this link on the
+     * course page; 0 = everyone sees it even if they don't belong to a suitable group)  - from
+     * course_modules table
+     * @var int
+     */
+    public $groupmembersonly;
+
+    /**
+     * Indent level on course page (0 = no indent) - from course_modules table
+     * @var int
+     */
+    public $indent;
+
+    /**
+     * Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from
+     * course_modules table
+     * @var int
+     */
+    public $completion;
+
+    /**
+     * Available date for this activity (0 if not set, or set to seconds since epoch; before this
+     * date, activity does not display to students) - from course_modules table
+     * @var int
+     */
+    public $availablefrom;
+
+    /**
+     * Available until date for this activity (0 if not set, or set to seconds since epoch; from
+     * this date, activity does not display to students) - from course_modules table
+     * @var int
+     */
+    public $availableuntil;
+
+    /**
+     * When activity is unavailable, this field controls whether it is shown to students (0 =
+     * hide completely, 1 = show greyed out with information about when it will be available) -
+     * from course_modules table
+     * @var int
+     */
+    public $showavailability;
+
+    /**
+     * Extra HTML that is put in an unhelpful part of the HTML when displaying this module in
+     * course page - from cached data in modinfo field
+     * @deprecated This is crazy, don't use it. Replaced by ->extraclasses and ->onclick
+     * @var string
+     */
+    public $extra;
+
+    /**
+     * Name of icon to use - from cached data in modinfo field
+     * @var string
+     */
+    public $icon;
+
+    /**
+     * Component that contains icon - from cached data in modinfo field
+     * @var string
+     */
+    public $iconcomponent;
+
+    /**
+     * Name of module e.g. 'forum' (this is the same name as the module's main database
+     * table) - from cached data in modinfo field
+     * @var string
+     */
+    public $modname;
+
+    /**
+     * Name of module instance for display on page e.g. 'General discussion forum' - from cached
+     * data in modinfo field
+     * @var string
+     */
+    public $name;
+
+    /**
+     * Section number that this course-module is in (section 0 = above the calendar, section 1
+     * = week/topic 1, etc) - from cached data in modinfo field
+     * @var string
+     */
+    public $sectionnum;
+
+    /**
+     * Availability conditions for this course-module based on the completion of other
+     * course-modules (array from other course-module id to required completion state for that
+     * module) - from cached data in modinfo field
+     * @var array
+     */
+    public $conditionscompletion;
+
+    /**
+     * Availability conditions for this course-module based on course grades (array from
+     * grade item id to object with ->min, ->max fields) - from cached data in modinfo field
+     * @var array
+     */
+    public $conditionsgrade;
+
+    /**
+     * Plural name of module type, e.g. 'Forums' - from lang file
+     * @deprecated Do not use this value (you can obtain it by calling get_string instead); it
+     *   will be removed in a future version (see later TODO in this file)
+     * @var string
+     */
+    public $modplural;
+
+    /**
+     * True if this course-module is available to students i.e. if all availability conditions
+     * are met - obtained dynamically
+     * @var bool
+     */
+    public $available;
+
+    /**
+     * If course-module is not available to students, this string gives information about
+     * availability which can be displayed to students and/or staff (e.g. 'Available from 3
+     * January 2010') for display on main page - obtained dynamically
+     * @var string
+     */
+    public $availableinfo;
+
+    /**
+     * True if this course-module is available to the CURRENT user (for example, if current user
+     * has viewhiddenactivities capability, they can access the course-module even if it is not
+     * visible or not available, so this would be true in that case)
+     * @var bool
+     */
+    public $uservisible;
+
+    // New data available only via functions
+    ////////////////////////////////////////
+
+    /**
+     * @var moodle_url
+     */
+    private $url;
+
+    /**
+     * @var string
+     */
+    private $content;
+
+    /**
+     * @var string
+     */
+    private $extraclasses;
+
+    /**
+     * @var string
+     */
+    private $onclick;
+
+    /**
+     * @var mixed
+     */
+    private $customdata;
+
+    /**
+     * @var string
+     */
+    private $afterlink;
+
+    /**
+     * @var string
+     */
+    private $afterediticons;
+
+    /**
+     * @return bool True if this module has a 'view' page that should be linked to in navigation
+     *   etc (note: modules may still have a view.php file, but return false if this is not
+     *   intended to be linked to from 'normal' parts of the interface; this is what label does).
+     */
+    public function has_view() {
+        return !is_null($this->url);
+    }
+
+    /**
+     * @return moodle_url URL to link to for this module, or null if it doesn't have a view page
+     */
+    public function get_url() {
+        return $this->url;
+    }
+
+    /**
+     * Obtains content to display on main (view) page.
+     * Note: Will collect view data, if not already obtained.
+     * @return string Content to display on main page below link, or empty string if none
+     */
+    public function get_content() {
+        $this->obtain_view_data();
+        return $this->content;
+    }
+
+    /**
+     * Note: Will collect view data, if not already obtained.
+     * @return string Extra CSS classes to add to html output for this activity on main page
+     */
+    public function get_extra_classes() {
+        $this->obtain_view_data();
+        return $this->extraclasses;
+    }
+
+    /**
+     * @return string Content of HTML on-click attribute. This string will be used literally
+     * as a string so should be pre-escaped.
+     */
+    public function get_on_click() {
+        // Does not need view data; may be used by navigation
+        return $this->onclick;
+    }
+    /**
+     * @return mixed Optional custom data stored in modinfo cache for this activity, or null if none
+     */
+    public function get_custom_data() {
+        return $this->customdata;
+    }
+
+    /**
+     * Note: Will collect view data, if not already obtained.
+                * @return string Extra HTML code to display after link
+     */
+    public function get_after_link() {
+        $this->obtain_view_data();
+        return $this->afterlink;
+    }
+
+    /**
+     * Note: Will collect view data, if not already obtained.
+     * @return string Extra HTML code to display after editing icons (e.g. more icons)
+     */
+    public function get_after_edit_icons() {
+        $this->obtain_view_data();
+        return $this->afterediticons;
+    }
+
+    /**
+     * @param moodle_core_renderer $output Output render to use, or null for default (global)
+     * @return moodle_url Icon URL for a suitable icon to put beside this cm
+     */
+    public function get_icon_url($output = null) {
+        global $OUTPUT;
+        if (!$output) {
+            $output = $OUTPUT;
+        }
+        if (!empty($this->icon)) {
+            if (substr($this->icon, 0, 4) === 'mod/') {
+                list($modname, $iconname) = explode('/', substr($this->icon, 4), 2);
+                $icon = $output->pix_url($iconname, $modname);
+            } else {
+                if (!empty($this->iconcomponent)) {
+                    // Icon  has specified component
+                    $icon = $output->pix_url($this->icon, $this->iconcomponent);
+                } else {
+                    // Icon does not have specified component, use default
+                    $icon = $output->pix_url($this->icon);
+                }
+            }
+        } else {
+            $icon = $output->pix_url('icon', $this->modname);
+        }
+        return $icon;
+    }
+
+    /**
+     * @return course_modinfo Modinfo object that this came from
+     */
+    public function get_modinfo() {
+        return $this->modinfo;
+    }
+
+    /**
+     * @return object Moodle course object that was used to construct this data
+     */
+    public function get_course() {
+        return $this->modinfo->get_course();
+    }
+
+    // Set functions
+    ////////////////
+
+    /**
+     * Sets content to display on course view page below link (if present).
+     * @param string $content New content as HTML string (empty string if none)
+     * @return void
+     */
+    public function set_content($content) {
+        $this->content = $content;
+    }
+
+    /**
+     * Sets extra classes to include in CSS.
+     * @param string $extraclasses Extra classes (empty string if none)
+     * @return void
+     */
+    public function set_extra_classes($extraclasses) {
+        $this->extraclasses = $extraclasses;
+    }
+
+    /**
+     * Sets value of on-click attribute for JavaScript.
+     * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
+     * @param string $onclick New onclick attribute which should be HTML-escaped
+     *   (empty string if none)
+     * @return void
+     */
+    public function set_on_click($onclick) {
+        $this->check_not_view_only();
+        $this->onclick = $onclick;
+    }
+
+    /**
+     * Sets HTML that displays after link on course view page.
+     * @param string $afterlink HTML string (empty string if none)
+     * @return void
+     */
+    public function set_after_link($afterlink) {
+        $this->afterlink = $afterlink;
+    }
+
+    /**
+     * Sets HTML that displays after edit icons on course view page.
+     * @param string $afterediticons HTML string (empty string if none)
+     * @return void
+     */
+    public function set_after_edit_icons($afterediticons) {
+        $this->afterediticons = $afterediticons;
+    }
+
+    /**
+     * Changes the name (text of link) for this module instance.
+     * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
+     * @param string $name Name of activity / link text
+     * @return void
+     */
+    public function set_name($name) {
+        $this->update_user_visible();
+        $this->name = $name;
+    }
+
+    /**
+     * Turns off the view link for this module instance.
+     * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
+     * @return void
+     */
+    public function set_no_view_link() {
+        $this->check_not_view_only();
+        $url = null;
+    }
+
+    /**
+     * Sets the 'uservisible' flag. This can be used (by setting false) to prevent access and
+     * display of this module link for the current user.
+     * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
+     * @param bool $uservisible
+     * @return void
+     */
+    public function set_user_visible($uservisible) {
+        $this->check_not_view_only();
+        $this->uservisible = $uservisible;
+    }
+
+    /**
+     * Sets the 'available' flag and related details. This flag is normally used to make
+     * course modules unavailable until a certain date or condition is met. (When a course
+     * module is unavailable, it is still visible to users who have viewhiddenactivities
+     * permission.)
+     *
+     * When this is function is called, user-visible status is recalculated automatically.
+     *
+     * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
+     * @param bool $available False if this item is not 'available'
+     * @param int $showavailability 0 = do not show this item at all if it's not available,
+     *   1 = show this item greyed out with the following message
+     * @param string $availableinfo Information about why this is not available which displays
+     *   to those who have viewhiddenactivities, and to everyone if showavailability is set;
+     *   note that this function replaces the existing data (if any)
+     * @return void
+     */
+    public function set_available($available, $showavailability=0, $availableinfo='') {
+        $this->check_not_view_only();
+        $this->available = $available;
+        $this->showavailability = $showavailability;
+        $this->availableinfo = $availableinfo;
+        $this->update_user_visible();
+    }
+
+    /**
+     * Some set functions can only be called from _cm_info_dynamic and not _cm_info_view.
+     * This is because they may affect parts of this object which are used on pages other
+     * than the view page (e.g. in the navigation block, or when checking access on
+     * module pages).
+     * @return void
+     */
+    private function check_not_view_only() {
+        if ($this->state >= self::STATE_DYNAMIC) {
+            throw new coding_exception('Cannot set this data from _cm_info_view because it may ' .
+                    'affect other pages as well as view');
+        }
+    }
+
+    /**
+     * Constructor should not be called directly; use get_fast_modinfo.
+     * @param course_modinfo $modinfo Parent object
+     * @param object $course Course row
+     * @param object $mod Module object from the modinfo field of course table
+     * @param object $info Entire object from modinfo field of course table
+     */
+    public function __construct(course_modinfo $modinfo, $course, $mod, $info) {
+        global $CFG;
+        $this->modinfo = $modinfo;
+
+        $this->id               = $mod->cm;
+        $this->instance         = $mod->id;
+        $this->course           = $course->id;
+        $this->modname          = $mod->mod;
+        $this->idnumber         = isset($mod->idnumber) ? $mod->idnumber : '';
+        $this->name             = $mod->name;
+        $this->visible          = $mod->visible;
+        $this->sectionnum       = $mod->section;
+        $this->groupmode        = isset($mod->groupmode) ? $mod->groupmode : 0;
+        $this->groupingid       = isset($mod->groupingid) ? $mod->groupingid : 0;
+        $this->groupmembersonly = isset($mod->groupmembersonly) ? $mod->groupmembersonly : 0;
+        $this->indent           = isset($mod->indent) ? $mod->indent : 0;
+        $this->completion       = isset($mod->completion) ? $mod->completion : 0;
+        $this->extra            = isset($mod->extra) ? $mod->extra : '';
+        $this->extraclasses     = isset($mod->extraclasses) ? $mod->extraclasses : '';
+        $this->onclick          = isset($mod->onclick) ? $mod->onclick : '';
+        $this->content          = isset($mod->content) ? $mod->content : '';
+        $this->icon             = isset($mod->icon) ? $mod->icon : '';
+        $this->iconcomponent    = isset($mod->iconcomponent) ? $mod->iconcomponent : '';
+        $this->customdata       = isset($mod->customdata) ? $mod->customdata : '';
+        $this->state = self::STATE_BASIC;
+
+        // This special case handles old label data. Labels used to use the 'name' field for
+        // content
+        if ($this->modname === 'label' && $this->content === '') {
+            $this->content = $this->extra;
+            $this->extra = '';
+        }
+
+        if (!empty($CFG->enableavailability)) {
+            // We must have completion information from modinfo. If it's not
+            // there, cache needs rebuilding
+            if (!isset($mod->showavailability)) {
+                throw new modinfo_rebuild_cache_exception(
+                        'enableavailability option was changed; rebuilding '.
+                        'cache for course ' . $course->id);
+            }
+            $this->showavailability = $mod->showavailability;
+            $this->availablefrom = isset($mod->availablefrom) ? $mod->availablefrom : 0;
+            $this->availableuntil = isset($mod->availableuntil) ? $mod->availableuntil : 0;
+            $this->conditionscompletion = isset($mod->conditionscompletion)
+                    ? $mod->conditionscompletion : array();
+            $this->conditionsgrade = isset($mod->conditionsgrade)
+                    ? $mod->conditionsgrade : array();
+        }
+
+        // Get module plural name.
+        // TODO This was a very old performance hack and should now be removed as the information
+        // certainly doesn't belong in modinfo. On a 'normal' page this is only used in the
+        // activity_modules block, so if it needs caching, it should be cached there.
+        static $modplurals;
+        if (!isset($modplurals[$this->modname])) {
+            $modplurals[$this->modname] = get_string('modulenameplural', $this->modname);
+        }
+        $this->modplural = $modplurals[$this->modname];
+
+        static $modviews;
+        if (!isset($modviews[$this->modname])) {
+            $modviews[$this->modname] = !plugin_supports('mod', $this->modname,
+                    FEATURE_NO_VIEW_LINK);
+        }
+        $this->url = $modviews[$this->modname]
+                ? new moodle_url('/mod/' . $this->modname . '/view.php', array('id'=>$this->id))
+                : null;
+    }
+
+    /**
+     * If dynamic data for this course-module is not yet available, gets it.
+     *
+     * This function is automatically called when constructing course_modinfo, so users don't
+     * need to call it.
+     *
+     * Dynamic data is data which does not come directly from the cache but is calculated at
+     * runtime based on the current user. Primarily this concerns whether the user can access
+     * the module or not.
+     *
+     * As part of this function, the module's _cm_info_dynamic function from its lib.php will
+     * be called (if it exists).
+     * @return void
+     */
+    public function obtain_dynamic_data() {
+        global $CFG;
+        if ($this->state >= self::STATE_DYNAMIC) {
+            return;
+        }
+        $userid = $this->modinfo->get_user_id();
+
+        if (!empty($CFG->enableavailability)) {
+            // Get availability information
+            $ci = new condition_info($this);
+            // Note that the modinfo currently available only includes minimal details (basic data)
+            // so passing it to this function is a bit dangerous as it would cause infinite
+            // recursion if it tried to get dynamic data, however we know that this function only
+            // uses basic data.
+            $this->available = $ci->is_available($this->availableinfo, true,
+                    $userid, $this->modinfo);
+        } else {
+            $this->available = true;
+        }
+
+        // Update visible state for current user
+        $this->update_user_visible();
+
+        // Let module make dynamic changes at this point
+        $this->call_mod_function('cm_info_dynamic');
+        $this->state = self::STATE_DYNAMIC;
+    }
+
+    /**
+     * Works out whether activity is visible *for current user* - if this is false, they
+     * aren't allowed to access it.
+     * @return void
+     */
+    private function update_user_visible() {
+        global $CFG;
+        $modcontext = get_context_instance(CONTEXT_MODULE, $this->id);
+        $userid = $this->modinfo->get_user_id();
+        $this->uservisible = true;
+        if ((!$this->visible or !$this->available) and
+                !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
+            // If the activity is hidden or unavailable, and you don't have viewhiddenactivities,
+            // set it so that user can't see or access it
+            $this->uservisible = false;
+        } else if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly)
+                and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
+            // If the activity has 'group members only' and you don't have accessallgroups...
+            $groups = $this->modinfo->get_groups();
+            if (empty($this->groups[$this->groupingid])) {
+                // ...and you don't belong to a group, then set it so you can't see/access it
+                $this->uservisible = false;
+            }
+        }
+    }
+
+    /**
+     * Calls a module function (if exists), passing in one parameter: this object.
+     * @param string $type Name of function e.g. if this is 'grooblezorb' and the modname is
+     *   'forum' then it will try to call 'mod_forum_grooblezorb' or 'forum_grooblezorb'
+     * @return void
+     */
+    private function call_mod_function($type) {
+        global $CFG;
+        $libfile = $CFG->dirroot . '/mod/' . $this->modname . '/lib.php';
+        if (file_exists($libfile)) {
+            include_once($libfile);
+            $function = 'mod_' . $this->modname . '_' . $type;
+            if (function_exists($function)) {
+                $function($this);
+            } else {
+                $function = $this->modname . '_' . $type;
+                if (function_exists($function)) {
+                    $function($this);
+                }
+            }
+        }
+    }
+
+    /**
+     * If view data for this course-module is not yet available, obtains it.
+     *
+     * This function is automatically called if any of the functions (marked) which require
+     * view data are called.
+     *
+     * View data is data which is needed only for displaying the course main page (& any similar
+     * functionality on other pages) but is not needed in general. Obtaining view data may have
+     * a performance cost.
+     *
+     * As part of this function, the module's _cm_info_view function from its lib.php will
+     * be called (if it exists).
+     * @return void
+     */
+    private function obtain_view_data() {
+        if ($this->state >= self::STATE_VIEW) {
+            return;
+        }
+
+        // Let module make changes at this point
+        $this->call_mod_function('cm_info_view');
+        $this->state = self::STATE_VIEW;
+    }
+}
+
+
+/**
+ * Special exception that may only be thrown within the constructor for course_modinfo to
+ * indicate that the cache needs to be rebuilt. Not for use anywhere else.
+ */
+class modinfo_rebuild_cache_exception extends coding_exception {
+    function __construct($why) {
+        // If it ever escapes, that's a code bug
+        parent::__construct('This exception should be caught by code', $why);
+    }
+}
+
+
+/**
+ * Returns reference to full info about modules in course (including visibility).
+ * Cached and as fast as possible (0 or 1 db query).
+ *
+ * @global object
+ * @global object
+ * @global moodle_database
+ * @uses MAX_MODINFO_CACHE_SIZE
+ * @param mixed $course object or 'reset' string to reset caches, modinfo may be updated in db
+ * @param int $userid Defaults to current user id
+ * @return course_modinfo Module information for course, or null if resetting
+ */
+function get_fast_modinfo(&$course, $userid=0) {
+    global $CFG, $USER, $DB;
+    require_once($CFG->dirroot.'/course/lib.php');
+
+    if (!empty($CFG->enableavailability)) {
+        require_once($CFG->libdir.'/conditionlib.php');
+    }
+
+    static $cache = array();
+
+    if ($course === 'reset') {
+        $cache = array();
+        return null;
+    }
+
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) {
+        return $cache[$course->id];
+    }
+
+    if (!property_exists($course, 'modinfo')) {
+        debugging('Coding problem - missing course modinfo property in get_fast_modinfo() call');
+    }
+
+    unset($cache[$course->id]); // prevent potential reference problems when switching users
+
+    try {
+        $cache[$course->id] = new course_modinfo($course, $userid);
+    } catch (modinfo_rebuild_cache_exception $e) {
+        debugging($e->debuginfo);
+        rebuild_course_cache($course->id, true);
+        $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
+        // This second time we don't catch the exception - if you request cache rebuild twice
+        // in a row, that's a bug => coding_exception
+        $cache[$course->id] = new course_modinfo($course, $userid);
+    }
+
+    // Ensure cache does not use too much RAM
+    if (count($cache) > MAX_MODINFO_CACHE_SIZE) {
+        reset($cache);
+        $key = key($cache);
+        unset($cache[$key]);
+    }
+
+    return $cache[$course->id];
+}
+
+
+/**
+ * Class that is the return value for the _get_coursemodule_info module API function.
+ *
+ * Note: For backward compatibility, you can also return a stdclass object from that function.
+ * The difference is that the stdclass object may contain an 'extra' field (deprecated because
+ * it was crazy, except for label which uses it differently). The stdclass object may not contain
+ * the new fields defined here (content, extraclasses, customdata).
+ */
+class cached_cm_info {
+    /**
+     * Name (text of link) for this activity; Leave unset to accept default name
+     * @var string
+     */
+    public $name;
+
+    /**
+     * Name of icon for this activity. Normally, this should be used together with $iconcomponent
+     * to define the icon, as per pix_url function.
+     * For backward compatibility, if this value is of the form 'mod/forum/icon' then an icon
+     * within that module will be used.
+     * @see cm_info::get_icon_url()
+     * @see renderer_base::pix_url()
+     * @var string
+     */
+    public $icon;
+
+    /**
+     * Component for icon for this activity, as per pix_url; leave blank to use default 'moodle'
+     * component
+     * @see renderer_base::pix_url()
+     * @var string
+     */
+    public $iconcomponent;
+
+    /**
+     * HTML content to be displayed on the main page below the link (if any) for this course-module
+     * @var string
+     */
+    public $content;
+
+    /**
+     * Custom data to be stored in modinfo for this activity; useful if there are cases when
+     * internal information for this activity type needs to be accessible from elsewhere on the
+     * course without making database queries. May be of any type but should be short.
+     * @var mixed
+     */
+    public $customdata;
+
+    /**
+     * Extra CSS class or classes to be added when this activity is displayed on the main page;
+     * space-separated string
+     * @var string
+     */
+    public $extraclasses;
+
+    /**
+     * Content of onclick JavaScript; escaped HTML to be inserted as attribute value
+     * @var string
+     */
+    public $onclick;
+}
\ No newline at end of file
index e7fe450..c59449a 100644 (file)
@@ -352,6 +352,8 @@ define('FEATURE_COMPLETION_TRACKS_VIEWS', 'completion_tracks_views');
 /** True if module has custom completion rules */
 define('FEATURE_COMPLETION_HAS_RULES', 'completion_has_rules');
 
+/** True if module has no 'view' page (like label) */
+define('FEATURE_NO_VIEW_LINK', 'viewlink');
 /** True if module supports outcomes */
 define('FEATURE_IDNUMBER', 'idnumber');
 /** True if module supports groups */
@@ -2312,6 +2314,14 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
             if ($cm->course != $course->id) {
                 throw new coding_exception('course and cm parameters in require_login() call do not match!!');
             }
+            // make sure we have a $cm from get_fast_modinfo as this contains activity access details
+            if (!($cm instanceof cm_info)) {
+                // note: nearly all pages call get_fast_modinfo anyway and it does not make any
+                // db queries so this is not really a performance concern, however it is obviously
+                // better if you use get_fast_modinfo to get the cm before calling this.
+                $modinfo = get_fast_modinfo($course);
+                $cm = $modinfo->get_cm($cm->id);
+            }
             $PAGE->set_cm($cm, $course); // set's up global $COURSE
             $PAGE->set_pagelayout('incourse');
         } else {
@@ -2583,47 +2593,15 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
         }
     }
 
-    // test visibility
-    if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
+    // Check visibility of activity to current user; includes visible flag, groupmembersonly,
+    // conditional availability, etc
+    if ($cm && !$cm->uservisible) {
         if ($preventredirect) {
             throw new require_login_exception('Activity is hidden');
         }
         redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
     }
 
-    // groupmembersonly access control
-    if (!empty($CFG->enablegroupmembersonly) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-        if (isguestuser() or !groups_has_membership($cm)) {
-            if ($preventredirect) {
-                throw new require_login_exception('Not member of a group');
-            }
-            print_error('groupmembersonlyerror', 'group', $CFG->wwwroot.'/course/view.php?id='.$cm->course);
-        }
-    }
-
-    // Conditional activity access control
-    if (!empty($CFG->enableavailability) and $cm) {
-        // TODO: this is going to work with login-as-user, sorry!
-        // We cache conditional access in session
-        if (!isset($SESSION->conditionaccessok)) {
-            $SESSION->conditionaccessok = array();
-        }
-        // If you have been allowed into the module once then you are allowed
-        // in for rest of session, no need to do conditional checks
-        if (!array_key_exists($cm->id, $SESSION->conditionaccessok)) {
-            // Get condition info (does a query for the availability table)
-            require_once($CFG->libdir.'/conditionlib.php');
-            $ci = new condition_info($cm, CONDITION_MISSING_EXTRATABLE);
-            // Check condition for user (this will do a query if the availability
-            // information depends on grade or completion information)
-            if ($ci->is_available($junk) || has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
-                $SESSION->conditionaccessok[$cm->id] = true;
-            } else {
-                print_error('activityiscurrentlyhidden');
-            }
-        }
-    }
-
     // Finally access granted, update lastaccess times
     user_accesstime_log($course->id);
 }
@@ -2674,16 +2652,29 @@ function require_logout() {
  */
 function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
     global $CFG, $PAGE, $SITE;
+    $issite = (is_object($courseorid) and $courseorid->id == SITEID)
+          or (!is_object($courseorid) and $courseorid == SITEID);
+    if ($issite && !empty($cm) && !($cm instanceof cm_info)) {
+        // note: nearly all pages call get_fast_modinfo anyway and it does not make any
+        // db queries so this is not really a performance concern, however it is obviously
+        // better if you use get_fast_modinfo to get the cm before calling this.
+        if (is_object($courseorid)) {
+            $course = $courseorid;
+        } else {
+            $course = clone($SITE);
+        }
+        $modinfo = get_fast_modinfo($course);
+        $cm = $modinfo->get_cm($cm->id);
+    }
     if (!empty($CFG->forcelogin)) {
         // login required for both SITE and courses
         require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
 
-    } else if (!empty($cm) and !$cm->visible) {
+    } else if ($issite && !empty($cm) and !$cm->uservisible) {
         // always login for hidden activities
         require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
 
-    } else if ((is_object($courseorid) and $courseorid->id == SITEID)
-          or (!is_object($courseorid) and $courseorid == SITEID)) {
+    } else if ($issite) {
               //login for SITE not required
         if ($cm and empty($cm->visible)) {
             // hidden activities are not accessible without login
@@ -3006,207 +2997,6 @@ function reset_login_count() {
     $SESSION->logincount = 0;
 }
 
-/**
- * Returns reference to full info about modules in course (including visibility).
- * Cached and as fast as possible (0 or 1 db query).
- *
- * @global object
- * @global object
- * @global object
- * @uses CONTEXT_MODULE
- * @uses MAX_MODINFO_CACHE_SIZE
- * @param mixed $course object or 'reset' string to reset caches, modinfo may be updated in db
- * @param int $userid Defaults to current user id
- * @return mixed courseinfo object or nothing if resetting
- */
-function &get_fast_modinfo(&$course, $userid=0) {
-    global $CFG, $USER, $DB;
-    require_once($CFG->dirroot.'/course/lib.php');
-
-    if (!empty($CFG->enableavailability)) {
-        require_once($CFG->libdir.'/conditionlib.php');
-    }
-
-    static $cache = array();
-
-    if ($course === 'reset') {
-        $cache = array();
-        $nothing = null;
-        return $nothing; // we must return some reference
-    }
-
-    if (empty($userid)) {
-        $userid = $USER->id;
-    }
-
-    if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) {
-        return $cache[$course->id];
-    }
-
-    if (!property_exists($course, 'modinfo')) {
-        debugging('Coding problem - missing course modinfo property in get_fast_modinfo() call');
-    }
-
-    if (empty($course->modinfo)) {
-        // no modinfo yet - load it
-        rebuild_course_cache($course->id);
-        $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-    }
-
-    $modinfo = new stdClass();
-    $modinfo->courseid  = $course->id;
-    $modinfo->userid    = $userid;
-    $modinfo->sections  = array();
-    $modinfo->cms       = array();
-    $modinfo->instances = array();
-    $modinfo->groups    = null; // loaded only when really needed - the only one db query
-
-    $info = unserialize($course->modinfo);
-    if (!is_array($info)) {
-        // hmm, something is wrong - lets try to fix it
-        rebuild_course_cache($course->id);
-        $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-        $info = unserialize($course->modinfo);
-        if (!is_array($info)) {
-            return $modinfo;
-        }
-    }
-
-    if ($info) {
-        // detect if upgrade required
-        $first = reset($info);
-        if (!isset($first->id)) {
-            rebuild_course_cache($course->id);
-            $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-            $info = unserialize($course->modinfo);
-            if (!is_array($info)) {
-                return $modinfo;
-            }
-        }
-    }
-
-    $modlurals = array();
-
-    // If we haven't already preloaded contexts for the course, do it now
-    preload_course_contexts($course->id);
-
-    foreach ($info as $mod) {
-        if (empty($mod->name)) {
-            // something is wrong here
-            continue;
-        }
-        // reconstruct minimalistic $cm
-        $cm = new stdClass();
-        $cm->id               = $mod->cm;
-        $cm->instance         = $mod->id;
-        $cm->course           = $course->id;
-        $cm->modname          = $mod->mod;
-        $cm->idnumber         = $mod->idnumber;
-        $cm->name             = $mod->name;
-        $cm->visible          = $mod->visible;
-        $cm->sectionnum       = $mod->section;
-        $cm->groupmode        = $mod->groupmode;
-        $cm->groupingid       = $mod->groupingid;
-        $cm->groupmembersonly = $mod->groupmembersonly;
-        $cm->indent           = $mod->indent;
-        $cm->completion       = $mod->completion;
-        $cm->extra            = isset($mod->extra) ? $mod->extra : '';
-        $cm->icon             = isset($mod->icon) ? $mod->icon : '';
-        $cm->iconcomponent    = isset($mod->iconcomponent) ? $mod->iconcomponent : '';
-        $cm->uservisible      = true;
-        if (!empty($CFG->enableavailability)) {
-            // We must have completion information from modinfo. If it's not
-            // there, cache needs rebuilding
-            if(!isset($mod->availablefrom)) {
-                debugging('enableavailability option was changed; rebuilding '.
-                    'cache for course '.$course->id);
-                rebuild_course_cache($course->id,true);
-                // Re-enter this routine to do it all properly
-                return get_fast_modinfo($course, $userid);
-            }
-            $cm->availablefrom    = $mod->availablefrom;
-            $cm->availableuntil   = $mod->availableuntil;
-            $cm->showavailability = $mod->showavailability;
-            $cm->conditionscompletion = $mod->conditionscompletion;
-            $cm->conditionsgrade  = $mod->conditionsgrade;
-        }
-
-        // preload long names plurals and also check module is installed properly
-        if (!isset($modlurals[$cm->modname])) {
-            if (!file_exists("$CFG->dirroot/mod/$cm->modname/lib.php")) {
-                continue;
-            }
-            $modlurals[$cm->modname] = get_string('modulenameplural', $cm->modname);
-        }
-        $cm->modplural = $modlurals[$cm->modname];
-        $modcontext = get_context_instance(CONTEXT_MODULE,$cm->id);
-
-        if (!empty($CFG->enableavailability)) {
-            // Unfortunately the next call really wants to call
-            // get_fast_modinfo, but that would be recursive, so we fake up a
-            // modinfo for it already
-            if (empty($minimalmodinfo)) { //TODO: this is suspicious (skodak)
-                $minimalmodinfo = new stdClass();
-                $minimalmodinfo->cms = array();
-                foreach($info as $mod) {
-                    if (empty($mod->name)) {
-                        // something is wrong here
-                        continue;
-                    }
-                    $minimalcm = new stdClass();
-                    $minimalcm->id = $mod->cm;
-                    $minimalcm->name = $mod->name;
-                    $minimalmodinfo->cms[$minimalcm->id]=$minimalcm;
-                }
-            }
-
-            // Get availability information
-            $ci = new condition_info($cm);
-            $cm->available = $ci->is_available($cm->availableinfo, true, $userid, $minimalmodinfo);
-        } else {
-            $cm->available = true;
-        }
-        if ((!$cm->visible or !$cm->available) and !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
-            $cm->uservisible = false;
-
-        } else if (!empty($CFG->enablegroupmembersonly) and !empty($cm->groupmembersonly)
-                and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
-            if (is_null($modinfo->groups)) {
-                $modinfo->groups = groups_get_user_groups($course->id, $userid);
-            }
-            if (empty($modinfo->groups[$cm->groupingid])) {
-                $cm->uservisible = false;
-            }
-        }
-
-        if (!isset($modinfo->instances[$cm->modname])) {
-            $modinfo->instances[$cm->modname] = array();
-        }
-        $modinfo->instances[$cm->modname][$cm->instance] =& $cm;
-        $modinfo->cms[$cm->id] =& $cm;
-
-        // reconstruct sections
-        if (!isset($modinfo->sections[$cm->sectionnum])) {
-            $modinfo->sections[$cm->sectionnum] = array();
-        }
-        $modinfo->sections[$cm->sectionnum][] = $cm->id;
-
-        unset($cm);
-    }
-
-    unset($cache[$course->id]); // prevent potential reference problems when switching users
-    $cache[$course->id] = $modinfo;
-
-    // Ensure cache does not use too much RAM
-    if (count($cache) > MAX_MODINFO_CACHE_SIZE) {
-        reset($cache);
-        $key = key($cache);
-        unset($cache[$key]);
-    }
-
-    return $cache[$course->id];
-}
-
 /**
  * Determines if the currently logged in user is in editing mode.
  * Note: originally this function had $userid parameter - it was not usable anyway
index 795246f..1a9f007 100644 (file)
@@ -1026,10 +1026,14 @@ class global_navigation extends navigation_node {
                 if ($course->id !== SITEID) {
                     // Find the section for the $CM associated with the page and collect
                     // its section number.
-                    foreach ($sections as $section) {
-                        if ($section->id == $cm->section) {
-                            $cm->sectionnumber = $section->section;
-                            break;
+                    if (isset($cm->sectionnum)) {
+                        $cm->sectionnumber = $cm->sectionnum;
+                    } else {
+                        foreach ($sections as $section) {
+                            if ($section->id == $cm->section) {
+                                $cm->sectionnumber = $section->section;
+                                break;
+                            }
                         }
                     }
 
@@ -1429,10 +1433,10 @@ class global_navigation extends navigation_node {
      *
      * @param navigation_node $sectionnode
      * @param int $sectionnumber
-     * @param stdClass $modinfo Object returned from {@see get_fast_modinfo()}
+     * @param course_modinfo $modinfo Object returned from {@see get_fast_modinfo()}
      * @return array Array of activity nodes
      */
-    protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, $modinfo) {
+    protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, course_modinfo $modinfo) {
         if (!array_key_exists($sectionnumber, $modinfo->sections)) {
             return true;
         }
@@ -1449,11 +1453,12 @@ class global_navigation extends navigation_node {
             } else {
                 $icon = new pix_icon('icon', get_string('modulename', $cm->modname), $cm->modname);
             }
-            $url = new moodle_url('/mod/'.$cm->modname.'/view.php', array('id'=>$cm->id));
+            $url = $cm->get_url();
             $activitynode = $sectionnode->add(format_string($cm->name), $url, navigation_node::TYPE_ACTIVITY, null, $cm->id, $icon);
             $activitynode->title(get_string('modulename', $cm->modname));
             $activitynode->hidden = (!$cm->visible);
-            if ($cm->modname == 'label') {
+            if (!$url) {
+                // Do not show activities that don't have links!
                 $activitynode->display = false;
             } else if ($this->module_extends_navigation($cm->modname)) {
                 $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
@@ -1482,11 +1487,12 @@ class global_navigation extends navigation_node {
         } else {
             $icon = new pix_icon('icon', get_string('modulename', $cm->modname), $cm->modname);
         }
-        $url = new moodle_url('/mod/'.$cm->modname.'/view.php', array('id'=>$cm->id));
+        $url = $cm->get_url();
         $activitynode = $coursenode->add(format_string($cm->name), $url, navigation_node::TYPE_ACTIVITY, null, $cm->id, $icon);
         $activitynode->title(get_string('modulename', $cm->modname));
         $activitynode->hidden = (!$cm->visible);
-        if ($cm->modname == 'label') {
+        if (!$url) {
+            // Don't show activities that don't have links!
             $activitynode->display = false;
         } else if ($this->module_extends_navigation($cm->modname)) {
             $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
@@ -1509,7 +1515,7 @@ class global_navigation extends navigation_node {
      * @param navigation_node $activity
      * @return bool
      */
-    protected function load_activity(stdClass $cm, stdClass $course, navigation_node $activity) {
+    protected function load_activity($cm, stdClass $course, navigation_node $activity) {
         global $CFG, $DB;
 
         $activity->make_active();
index e677754..de6f9ff 100644 (file)
@@ -414,6 +414,7 @@ require_once($CFG->libdir .'/grouplib.php');        // Groups functions
 require_once($CFG->libdir .'/sessionlib.php');      // All session and cookie related stuff
 require_once($CFG->libdir .'/editorlib.php');       // All text editor related functions and classes
 require_once($CFG->libdir .'/messagelib.php');      // Messagelib functions
+require_once($CFG->libdir .'/modinfolib.php');      // Cached information on course-module instances
 
 // make sure PHP is not severly misconfigured
 setup_validate_php_configuration();
index 7d2ddc3..69b13a4 100644 (file)
@@ -2181,7 +2181,8 @@ function navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $wid
 
     $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>';
     foreach ($modinfo->cms as $mod) {
-        if ($mod->modname == 'label') {
+        if (!$mod->has_view()) {
+            // Don't show modules which you can't link to!
             continue;
         }
 
index 8d74f63..df7c7cd 100644 (file)
@@ -7792,3 +7792,35 @@ class forum_existing_subscriber_selector extends forum_subscriber_selector_base
     }
 
 }
+
+/**
+ * Adds information about unread messages, that is only required for the course view page (and
+ * similar), to the course-module object.
+ * @param cm_info $cm Course-module object
+ */
+function forum_cm_info_view(cm_info $cm) {
+    global $CFG;
+
+    // Get tracking status (once per request)
+    static $initialised;
+    static $usetracking, $strunreadpostsone;
+    if (!isset($initialised)) {
+        if ($usetracking = forum_tp_can_track_forums()) {
+            $strunreadpostsone = get_string('unreadpostsone', 'forum');
+        }
+        $initialised = true;
+    }
+
+    if ($usetracking) {
+        if ($unread = forum_tp_count_forum_unread_posts($cm, $cm->get_course())) {
+            $out = '<span class="unread"> <a href="' . $cm->get_url() . '">';
+            if ($unread == 1) {
+                $out .= $strunreadpostsone;
+            } else {
+                $out .= get_string('unreadpostsnumber', 'forum', $unread);
+            }
+            $out .= '</a></span>';
+            $cm->set_after_link($out);
+        }
+    }
+}
index 1f77822..2e21e13 100644 (file)
@@ -209,6 +209,7 @@ function label_supports($feature) {
         case FEATURE_GRADE_OUTCOMES:          return false;
         case FEATURE_MOD_ARCHETYPE:           return MOD_ARCHETYPE_RESOURCE;
         case FEATURE_BACKUP_MOODLE2:          return true;
+        case FEATURE_NO_VIEW_LINK:            return true;
 
         default: return null;
     }