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)
1  2 
course/lib.php
lib/moodlelib.php
lib/navigationlib.php
lib/weblib.php
mod/forum/lib.php

diff --combined course/lib.php
@@@ -48,10 -48,6 +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 +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 +1111,6 @@@ function get_array_of_activities($cours
  
                     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;
                             }
                             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 +1280,44 @@@ function set_section_visible($courseid
      }
  }
  
+ /**
+  * 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 +1333,8 @@@ function print_section($course, $sectio
      static $strmovehere;
      static $strmovefull;
      static $strunreadpostsone;
-     static $usetracking;
      static $groupings;
+     static $modulenames;
  
      if (!isset($initialised)) {
          $groupbuttons     = ($course->groupmode or (!$course->groupmodeforce));
              $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);
  
                  continue;
              }
  
+             /**
+              * @var cm_info
+              */
              $mod = $mods[$modnumber];
  
              if ($ismoving and $mod->id == $USER->activitycopy) {
                  }
              }
  
+             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
              $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.'"'.
              }
              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) {
                  }
                  echo '&nbsp;&nbsp;';
                  echo make_editing_buttons($mod, $absolute, true, $mod->indent, $section->section);
+                 echo $mod->get_after_edit_icons();
              }
  
              // Completion
                  }
              }
  
+             // 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) {
@@@ -1757,16 -1852,17 +1852,16 @@@ function rebuild_course_cache($courseid
          @set_time_limit(0);  // this could take a while!   MDL-10954
      }
  
 -    if ($rs = $DB->get_recordset("course", $select,'','id,fullname')) {
 -        foreach ($rs as $course) {
 -            $modinfo = serialize(get_array_of_activities($course->id));
 -            $DB->set_field("course", "modinfo", $modinfo, array("id"=>$course->id));
 -            // update cached global COURSE too ;-)
 -            if ($course->id == $COURSE->id) {
 -                $COURSE->modinfo = $modinfo;
 -            }
 +    $rs = $DB->get_recordset("course", $select,'','id,fullname');
 +    foreach ($rs as $course) {
 +        $modinfo = serialize(get_array_of_activities($course->id));
 +        $DB->set_field("course", "modinfo", $modinfo, array("id"=>$course->id));
 +        // update cached global COURSE too ;-)
 +        if ($course->id == $COURSE->id) {
 +            $COURSE->modinfo = $modinfo;
          }
 -        $rs->close();
      }
 +    $rs->close();
      // reset the fast modinfo cache
      $reset = 'reset';
      get_fast_modinfo($reset);
diff --combined lib/moodlelib.php
@@@ -352,6 -352,8 +352,8 @@@ define('FEATURE_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 +2314,14 @@@ function require_login($courseorid = NU
              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 {
          }
      }
  
-     // 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 +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 +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
@@@ -8012,13 -7802,14 +7802,13 @@@ function notify_login_failures() 
            GROUP BY ip
              HAVING COUNT(*) >= ?";
      $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
 -    if ($rs = $DB->get_recordset_sql($sql, $params)) {
 -        foreach ($rs as $iprec) {
 -            if (!empty($iprec->ip)) {
 -                set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
 -            }
 +    $rs = $DB->get_recordset_sql($sql, $params);
 +    foreach ($rs as $iprec) {
 +        if (!empty($iprec->ip)) {
 +            set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
          }
 -        $rs->close();
      }
 +    $rs->close();
  
  /// Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
  /// and insert them into the cache_flags temp table
            GROUP BY info
              HAVING count(*) >= ?";
      $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
 -    if ($rs = $DB->get_recordset_sql($sql, $params)) {
 -        foreach ($rs as $inforec) {
 -            if (!empty($inforec->info)) {
 -                set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
 -            }
 +    $rs = $DB->get_recordset_sql($sql, $params);
 +    foreach ($rs as $inforec) {
 +        if (!empty($inforec->info)) {
 +            set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
          }
 -        $rs->close();
      }
 +    $rs->close();
  
  /// Now, select all the login error logged records belonging to the ips and infos
  /// since lastnotifyfailure, that we have stored in the cache_flags table
      $count = 0;
      $messages = '';
  /// Iterate over the logs recordset
 -    if ($rs = $DB->get_recordset_sql($sql, $params)) {
 -        foreach ($rs as $log) {
 -            $log->time = userdate($log->time);
 -            $messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
 -            $count++;
 -        }
 -        $rs->close();
 +    $rs = $DB->get_recordset_sql($sql, $params);
 +    foreach ($rs as $log) {
 +        $log->time = userdate($log->time);
 +        $messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
 +        $count++;
      }
 +    $rs->close();
  
  /// If we haven't run in the last hour and
  /// we have something useful to report and we
diff --combined lib/navigationlib.php
@@@ -1026,10 -1026,14 +1026,14 @@@ class global_navigation extends navigat
                  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;
+                             }
                          }
                      }
  
       *
       * @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;
          }
              } 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;
          } 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;
       * @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();
@@@ -2537,7 -2543,7 +2543,7 @@@ class settings_navigation extends navig
       *
       */
      public function initialise() {
 -        global $DB;
 +        global $DB, $SESSION;
  
          if (during_initial_install()) {
              return false;
          }
  
          $settings = $this->load_user_settings($this->page->course->id);
 -        $admin = $this->load_administration_settings();
 +
 +        if (isloggedin() && !isguestuser() && (!property_exists($SESSION, 'load_navigation_admin') || $SESSION->load_navigation_admin)) {
 +            $admin = $this->load_administration_settings();
 +            $SESSION->load_navigation_admin = ($admin->has_children());
 +        } else {
 +            $admin = false;
 +        }
  
          if ($context->contextlevel == CONTEXT_SYSTEM && $admin) {
              $admin->force_open();
              $this->add(get_string('returntooriginaluser', 'moodle', fullname($realuser, true)), $url, self::TYPE_SETTING, null, null, new pix_icon('t/left', ''));
          }
  
 -        // Make sure the first child doesnt have proceed with hr set to true
 -
          foreach ($this->children as $key=>$node) {
              if ($node->nodetype != self::NODETYPE_BRANCH || $node->children->count()===0) {
                  $node->remove();
diff --combined lib/weblib.php
@@@ -1106,13 -1106,6 +1106,13 @@@ function format_text($text, $format = F
              $text = $filtermanager->filter_text($text, $context, array('originalformat' => $format));
              break;
      }
 +    if ($options['filter']) {
 +        // at this point there should not be any draftfile links any more,
 +        // this happens when developers forget to post process the text.
 +        // The only potential problem is that somebody might try to format
 +        // the text before storing into database which would be itself big bug.
 +        $text = str_replace("\"$CFG->httpswwwroot/draftfile.php", "\"$CFG->httpswwwroot/brokenfile.php#", $text);
 +    }
  
      // Warn people that we have removed this old mechanism, just in case they
      // were stupid enough to rely on it.
@@@ -2181,7 -2174,8 +2181,8 @@@ function navmenulist($course, $sections
  
      $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;
          }
  
diff --combined mod/forum/lib.php
@@@ -1504,8 -1504,7 +1504,8 @@@ function forum_upgrade_grades() 
      $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid
                FROM {forum} f, {course_modules} cm, {modules} m
               WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
 -    if ($rs = $DB->get_recordset_sql($sql)) {
 +    $rs = $DB->get_recordset_sql($sql);
 +    if ($rs->valid()) {
          $pbar = new progress_bar('forumupgradegrades', 500, true);
          $i=0;
          foreach ($rs as $forum) {
              forum_update_grades($forum, 0, false);
              $pbar->update($i, $count, "Updating Forum grades ($i/$count).");
          }
 -        $rs->close();
      }
 +    $rs->close();
  }
  
  /**
@@@ -7792,3 -7791,35 +7792,35 @@@ class forum_existing_subscriber_selecto
      }
  
  }
+ /**
+  * 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);
+         }
+     }
+ }