|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00027 // Maximum number of modinfo items to keep in memory cache. Do not increase this to a large 00028 // number because: 00029 // a) modinfo can be big (megabyte range) for some courses 00030 // b) performance of cache will deteriorate if there are very many items in it 00031 if (!defined('MAX_MODINFO_CACHE_SIZE')) { 00032 define('MAX_MODINFO_CACHE_SIZE', 10); 00033 } 00034 00035 00043 class course_modinfo extends stdClass { 00044 // For convenience we store the course object here as it is needed in other parts of code 00045 private $course; 00046 00047 // Existing data fields 00049 00050 // These are public for backward compatibility. Note: it is not possible to retain BC 00051 // using PHP magic get methods because behaviour is different with regard to empty(). 00052 00058 public $courseid; 00059 00065 public $userid; 00066 00073 public $sections; 00074 00080 public $cms; 00081 00087 public $instances; 00088 00096 public $groups; 00097 00098 // Get methods for data 00100 00104 public function get_course() { 00105 return $this->course; 00106 } 00107 00111 public function get_course_id() { 00112 return $this->courseid; 00113 } 00114 00118 public function get_user_id() { 00119 return $this->userid; 00120 } 00121 00126 public function get_sections() { 00127 return $this->sections; 00128 } 00129 00134 public function get_cms() { 00135 return $this->cms; 00136 } 00137 00144 public function get_cm($cmid) { 00145 if (empty($this->cms[$cmid])) { 00146 throw new moodle_exception('invalidcoursemodule', 'error'); 00147 } 00148 return $this->cms[$cmid]; 00149 } 00150 00155 public function get_instances() { 00156 return $this->instances; 00157 } 00158 00164 public function get_instances_of($modname) { 00165 if (empty($this->instances[$modname])) { 00166 return array(); 00167 } 00168 return $this->instances[$modname]; 00169 } 00170 00177 public function get_groups($groupingid=0) { 00178 if (is_null($this->groups)) { 00179 // NOTE: Performance could be improved here. The system caches user groups 00180 // in $USER->groupmember[$courseid] => array of groupid=>groupid. Unfortunately this 00181 // structure does not include grouping information. It probably could be changed to 00182 // do so, without a significant performance hit on login, thus saving this one query 00183 // each request. 00184 $this->groups = groups_get_user_groups($this->courseid, $this->userid); 00185 } 00186 if (!isset($this->groups[$groupingid])) { 00187 return array(); 00188 } 00189 return $this->groups[$groupingid]; 00190 } 00191 00199 public function __construct($course, $userid) { 00200 global $CFG, $DB; 00201 00202 // Set initial values 00203 $this->courseid = $course->id; 00204 $this->userid = $userid; 00205 $this->sections = array(); 00206 $this->cms = array(); 00207 $this->instances = array(); 00208 $this->groups = null; 00209 $this->course = $course; 00210 00211 // Check modinfo field is set. If not, build and load it. 00212 if (empty($course->modinfo)) { 00213 rebuild_course_cache($course->id); 00214 $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id)); 00215 } 00216 00217 // Load modinfo field into memory as PHP object and check it's valid 00218 $info = unserialize($course->modinfo); 00219 if (!is_array($info)) { 00220 // hmm, something is wrong - lets try to fix it 00221 rebuild_course_cache($course->id); 00222 $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id)); 00223 $info = unserialize($course->modinfo); 00224 if (!is_array($info)) { 00225 // If it still fails, abort 00226 debugging('Problem with "modinfo" data for this course'); 00227 return; 00228 } 00229 } 00230 00231 // If we haven't already preloaded contexts for the course, do it now 00232 preload_course_contexts($course->id); 00233 00234 // Loop through each piece of module data, constructing it 00235 $modexists = array(); 00236 foreach ($info as $mod) { 00237 if (empty($mod->name)) { 00238 // something is wrong here 00239 continue; 00240 } 00241 00242 // Skip modules which don't exist 00243 if (empty($modexists[$mod->mod])) { 00244 if (!file_exists("$CFG->dirroot/mod/$mod->mod/lib.php")) { 00245 continue; 00246 } 00247 $modexists[$mod->mod] = true; 00248 } 00249 00250 // Construct info for this module 00251 $cm = new cm_info($this, $course, $mod, $info); 00252 00253 // Store module in instances and cms array 00254 if (!isset($this->instances[$cm->modname])) { 00255 $this->instances[$cm->modname] = array(); 00256 } 00257 $this->instances[$cm->modname][$cm->instance] = $cm; 00258 $this->cms[$cm->id] = $cm; 00259 00260 // Reconstruct sections. This works because modules are stored in order 00261 if (!isset($this->sections[$cm->sectionnum])) { 00262 $this->sections[$cm->sectionnum] = array(); 00263 } 00264 $this->sections[$cm->sectionnum][] = $cm->id; 00265 } 00266 00267 // We need at least 'dynamic' data from each course-module (this is basically the remaining 00268 // data which was always present in previous version of get_fast_modinfo, so it's required 00269 // for BC). Creating it in a second pass is necessary because obtain_dynamic_data sometimes 00270 // needs to be able to refer to a 'complete' (with basic data) modinfo. 00271 foreach ($this->cms as $cm) { 00272 $cm->obtain_dynamic_data(); 00273 } 00274 } 00275 } 00276 00277 00286 class cm_info extends stdClass { 00290 const STATE_BASIC = 0; 00291 00295 const STATE_DYNAMIC = 1; 00296 00300 const STATE_VIEW = 2; 00301 00306 private $modinfo; 00307 00312 private $state; 00313 00314 // Existing data fields 00316 00321 public $id; 00322 00327 public $instance; 00328 00333 public $course; 00334 00340 public $idnumber; 00341 00346 public $added; 00347 00355 public $score; 00356 00362 public $visible; 00363 00369 public $visibleold; 00370 00376 public $groupmode; 00377 00382 public $groupingid; 00383 00390 public $groupmembersonly; 00391 00396 public $indent; 00397 00403 public $completion; 00404 00411 public $completiongradeitemnumber; 00412 00417 public $completionview; 00418 00424 public $completionexpected; 00425 00431 public $availablefrom; 00432 00438 public $availableuntil; 00439 00446 public $showavailability; 00447 00454 public $showdescription; 00455 00462 public $extra; 00463 00468 public $icon; 00469 00474 public $iconcomponent; 00475 00481 public $modname; 00482 00487 public $module; 00488 00494 public $name; 00495 00501 public $sectionnum; 00502 00507 public $section; 00508 00515 public $conditionscompletion; 00516 00522 public $conditionsgrade; 00523 00530 public $modplural; 00531 00537 public $available; 00538 00545 public $availableinfo; 00546 00553 public $uservisible; 00554 00560 public $context; 00561 00562 00563 // New data available only via functions 00565 00569 private $url; 00570 00574 private $content; 00575 00579 private $extraclasses; 00580 00584 private $iconurl; 00585 00589 private $onclick; 00590 00594 private $customdata; 00595 00599 private $afterlink; 00600 00604 private $afterediticons; 00605 00611 public function has_view() { 00612 return !is_null($this->url); 00613 } 00614 00618 public function get_url() { 00619 return $this->url; 00620 } 00621 00627 public function get_content() { 00628 $this->obtain_view_data(); 00629 return $this->content; 00630 } 00631 00636 public function get_extra_classes() { 00637 $this->obtain_view_data(); 00638 return $this->extraclasses; 00639 } 00640 00645 public function get_on_click() { 00646 // Does not need view data; may be used by navigation 00647 return $this->onclick; 00648 } 00652 public function get_custom_data() { 00653 return $this->customdata; 00654 } 00655 00660 public function get_after_link() { 00661 $this->obtain_view_data(); 00662 return $this->afterlink; 00663 } 00664 00669 public function get_after_edit_icons() { 00670 $this->obtain_view_data(); 00671 return $this->afterediticons; 00672 } 00673 00678 public function get_icon_url($output = null) { 00679 global $OUTPUT; 00680 if (!$output) { 00681 $output = $OUTPUT; 00682 } 00683 // Support modules setting their own, external, icon image 00684 if (!empty($this->iconurl)) { 00685 $icon = $this->iconurl; 00686 00687 // Fallback to normal local icon + component procesing 00688 } else if (!empty($this->icon)) { 00689 if (substr($this->icon, 0, 4) === 'mod/') { 00690 list($modname, $iconname) = explode('/', substr($this->icon, 4), 2); 00691 $icon = $output->pix_url($iconname, $modname); 00692 } else { 00693 if (!empty($this->iconcomponent)) { 00694 // Icon has specified component 00695 $icon = $output->pix_url($this->icon, $this->iconcomponent); 00696 } else { 00697 // Icon does not have specified component, use default 00698 $icon = $output->pix_url($this->icon); 00699 } 00700 } 00701 } else { 00702 $icon = $output->pix_url('icon', $this->modname); 00703 } 00704 return $icon; 00705 } 00706 00710 public function get_modinfo() { 00711 return $this->modinfo; 00712 } 00713 00717 public function get_course() { 00718 return $this->modinfo->get_course(); 00719 } 00720 00721 // Set functions 00723 00729 public function set_content($content) { 00730 $this->content = $content; 00731 } 00732 00738 public function set_extra_classes($extraclasses) { 00739 $this->extraclasses = $extraclasses; 00740 } 00741 00750 public function set_icon_url(moodle_url $iconurl) { 00751 $this->iconurl = $iconurl; 00752 } 00753 00761 public function set_on_click($onclick) { 00762 $this->check_not_view_only(); 00763 $this->onclick = $onclick; 00764 } 00765 00771 public function set_after_link($afterlink) { 00772 $this->afterlink = $afterlink; 00773 } 00774 00780 public function set_after_edit_icons($afterediticons) { 00781 $this->afterediticons = $afterediticons; 00782 } 00783 00790 public function set_name($name) { 00791 $this->update_user_visible(); 00792 $this->name = $name; 00793 } 00794 00800 public function set_no_view_link() { 00801 $this->check_not_view_only(); 00802 $this->url = null; 00803 } 00804 00812 public function set_user_visible($uservisible) { 00813 $this->check_not_view_only(); 00814 $this->uservisible = $uservisible; 00815 } 00816 00834 public function set_available($available, $showavailability=0, $availableinfo='') { 00835 $this->check_not_view_only(); 00836 $this->available = $available; 00837 $this->showavailability = $showavailability; 00838 $this->availableinfo = $availableinfo; 00839 $this->update_user_visible(); 00840 } 00841 00849 private function check_not_view_only() { 00850 if ($this->state >= self::STATE_DYNAMIC) { 00851 throw new coding_exception('Cannot set this data from _cm_info_view because it may ' . 00852 'affect other pages as well as view'); 00853 } 00854 } 00855 00863 public function __construct(course_modinfo $modinfo, $course, $mod, $info) { 00864 global $CFG; 00865 $this->modinfo = $modinfo; 00866 00867 $this->id = $mod->cm; 00868 $this->instance = $mod->id; 00869 $this->course = $course->id; 00870 $this->modname = $mod->mod; 00871 $this->idnumber = isset($mod->idnumber) ? $mod->idnumber : ''; 00872 $this->name = $mod->name; 00873 $this->visible = $mod->visible; 00874 $this->sectionnum = $mod->section; // Note weirdness with name here 00875 $this->groupmode = isset($mod->groupmode) ? $mod->groupmode : 0; 00876 $this->groupingid = isset($mod->groupingid) ? $mod->groupingid : 0; 00877 $this->groupmembersonly = isset($mod->groupmembersonly) ? $mod->groupmembersonly : 0; 00878 $this->indent = isset($mod->indent) ? $mod->indent : 0; 00879 $this->extra = isset($mod->extra) ? $mod->extra : ''; 00880 $this->extraclasses = isset($mod->extraclasses) ? $mod->extraclasses : ''; 00881 $this->iconurl = isset($mod->iconurl) ? $mod->iconurl : ''; 00882 $this->onclick = isset($mod->onclick) ? $mod->onclick : ''; 00883 $this->content = isset($mod->content) ? $mod->content : ''; 00884 $this->icon = isset($mod->icon) ? $mod->icon : ''; 00885 $this->iconcomponent = isset($mod->iconcomponent) ? $mod->iconcomponent : ''; 00886 $this->customdata = isset($mod->customdata) ? $mod->customdata : ''; 00887 $this->context = get_context_instance(CONTEXT_MODULE, $mod->cm); 00888 $this->showdescription = isset($mod->showdescription) ? $mod->showdescription : 0; 00889 $this->state = self::STATE_BASIC; 00890 00891 // This special case handles old label data. Labels used to use the 'name' field for 00892 // content 00893 if ($this->modname === 'label' && $this->content === '') { 00894 $this->content = $this->extra; 00895 $this->extra = ''; 00896 } 00897 00898 // Note: These fields from $cm were not present in cm_info in Moodle 00899 // 2.0.2 and prior. They may not be available if course cache hasn't 00900 // been rebuilt since then. 00901 $this->section = isset($mod->sectionid) ? $mod->sectionid : 0; 00902 $this->module = isset($mod->module) ? $mod->module : 0; 00903 $this->added = isset($mod->added) ? $mod->added : 0; 00904 $this->score = isset($mod->score) ? $mod->score : 0; 00905 $this->visibleold = isset($mod->visibleold) ? $mod->visibleold : 0; 00906 00907 // Note: it saves effort and database space to always include the 00908 // availability and completion fields, even if availability or completion 00909 // are actually disabled 00910 $this->completion = isset($mod->completion) ? $mod->completion : 0; 00911 $this->completiongradeitemnumber = isset($mod->completiongradeitemnumber) 00912 ? $mod->completiongradeitemnumber : null; 00913 $this->completionview = isset($mod->completionview) 00914 ? $mod->completionview : 0; 00915 $this->completionexpected = isset($mod->completionexpected) 00916 ? $mod->completionexpected : 0; 00917 $this->showavailability = isset($mod->showavailability) ? $mod->showavailability : 0; 00918 $this->availablefrom = isset($mod->availablefrom) ? $mod->availablefrom : 0; 00919 $this->availableuntil = isset($mod->availableuntil) ? $mod->availableuntil : 0; 00920 $this->conditionscompletion = isset($mod->conditionscompletion) 00921 ? $mod->conditionscompletion : array(); 00922 $this->conditionsgrade = isset($mod->conditionsgrade) 00923 ? $mod->conditionsgrade : array(); 00924 00925 // Get module plural name. 00926 // TODO This was a very old performance hack and should now be removed as the information 00927 // certainly doesn't belong in modinfo. On a 'normal' page this is only used in the 00928 // activity_modules block, so if it needs caching, it should be cached there. 00929 static $modplurals; 00930 if (!isset($modplurals[$this->modname])) { 00931 $modplurals[$this->modname] = get_string('modulenameplural', $this->modname); 00932 } 00933 $this->modplural = $modplurals[$this->modname]; 00934 00935 static $modviews; 00936 if (!isset($modviews[$this->modname])) { 00937 $modviews[$this->modname] = !plugin_supports('mod', $this->modname, 00938 FEATURE_NO_VIEW_LINK); 00939 } 00940 $this->url = $modviews[$this->modname] 00941 ? new moodle_url('/mod/' . $this->modname . '/view.php', array('id'=>$this->id)) 00942 : null; 00943 } 00944 00959 public function obtain_dynamic_data() { 00960 global $CFG; 00961 if ($this->state >= self::STATE_DYNAMIC) { 00962 return; 00963 } 00964 $userid = $this->modinfo->get_user_id(); 00965 00966 if (!empty($CFG->enableavailability)) { 00967 // Get availability information 00968 $ci = new condition_info($this); 00969 // Note that the modinfo currently available only includes minimal details (basic data) 00970 // so passing it to this function is a bit dangerous as it would cause infinite 00971 // recursion if it tried to get dynamic data, however we know that this function only 00972 // uses basic data. 00973 $this->available = $ci->is_available($this->availableinfo, true, 00974 $userid, $this->modinfo); 00975 } else { 00976 $this->available = true; 00977 } 00978 00979 // Update visible state for current user 00980 $this->update_user_visible(); 00981 00982 // Let module make dynamic changes at this point 00983 $this->call_mod_function('cm_info_dynamic'); 00984 $this->state = self::STATE_DYNAMIC; 00985 } 00986 00992 private function update_user_visible() { 00993 global $CFG; 00994 $modcontext = get_context_instance(CONTEXT_MODULE, $this->id); 00995 $userid = $this->modinfo->get_user_id(); 00996 $this->uservisible = true; 00997 if ((!$this->visible or !$this->available) and 00998 !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) { 00999 // If the activity is hidden or unavailable, and you don't have viewhiddenactivities, 01000 // set it so that user can't see or access it 01001 $this->uservisible = false; 01002 } else if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly) 01003 and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) { 01004 // If the activity has 'group members only' and you don't have accessallgroups... 01005 $groups = $this->modinfo->get_groups($this->groupingid); 01006 if (empty($groups)) { 01007 // ...and you don't belong to a group, then set it so you can't see/access it 01008 $this->uservisible = false; 01009 } 01010 } 01011 } 01012 01019 private function call_mod_function($type) { 01020 global $CFG; 01021 $libfile = $CFG->dirroot . '/mod/' . $this->modname . '/lib.php'; 01022 if (file_exists($libfile)) { 01023 include_once($libfile); 01024 $function = 'mod_' . $this->modname . '_' . $type; 01025 if (function_exists($function)) { 01026 $function($this); 01027 } else { 01028 $function = $this->modname . '_' . $type; 01029 if (function_exists($function)) { 01030 $function($this); 01031 } 01032 } 01033 } 01034 } 01035 01050 private function obtain_view_data() { 01051 if ($this->state >= self::STATE_VIEW) { 01052 return; 01053 } 01054 01055 // Let module make changes at this point 01056 $this->call_mod_function('cm_info_view'); 01057 $this->state = self::STATE_VIEW; 01058 } 01059 } 01060 01061 01074 function get_fast_modinfo(&$course, $userid=0) { 01075 global $CFG, $USER, $DB; 01076 require_once($CFG->dirroot.'/course/lib.php'); 01077 01078 if (!empty($CFG->enableavailability)) { 01079 require_once($CFG->libdir.'/conditionlib.php'); 01080 } 01081 01082 static $cache = array(); 01083 01084 if ($course === 'reset') { 01085 $cache = array(); 01086 return null; 01087 } 01088 01089 if (empty($userid)) { 01090 $userid = $USER->id; 01091 } 01092 01093 if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) { 01094 return $cache[$course->id]; 01095 } 01096 01097 if (!property_exists($course, 'modinfo')) { 01098 debugging('Coding problem - missing course modinfo property in get_fast_modinfo() call'); 01099 } 01100 01101 unset($cache[$course->id]); // prevent potential reference problems when switching users 01102 01103 $cache[$course->id] = new course_modinfo($course, $userid); 01104 01105 // Ensure cache does not use too much RAM 01106 if (count($cache) > MAX_MODINFO_CACHE_SIZE) { 01107 reset($cache); 01108 $key = key($cache); 01109 unset($cache[$key]->instances); 01110 unset($cache[$key]->cms); 01111 unset($cache[$key]); 01112 } 01113 01114 return $cache[$course->id]; 01115 } 01116 01122 function rebuild_course_cache($courseid=0, $clearonly=false) { 01123 global $COURSE, $DB, $CFG; 01124 01125 // Destroy navigation caches 01126 navigation_cache::destroy_volatile_caches(); 01127 01128 if ($clearonly) { 01129 if (empty($courseid)) { 01130 $courseselect = array(); 01131 } else { 01132 $courseselect = array('id'=>$courseid); 01133 } 01134 $DB->set_field('course', 'modinfo', null, $courseselect); 01135 // update cached global COURSE too ;-) 01136 if ($courseid == $COURSE->id or empty($courseid)) { 01137 $COURSE->modinfo = null; 01138 } 01139 // reset the fast modinfo cache 01140 $reset = 'reset'; 01141 get_fast_modinfo($reset); 01142 return; 01143 } 01144 01145 require_once("$CFG->dirroot/course/lib.php"); 01146 01147 if ($courseid) { 01148 $select = array('id'=>$courseid); 01149 } else { 01150 $select = array(); 01151 @set_time_limit(0); // this could take a while! MDL-10954 01152 } 01153 01154 $rs = $DB->get_recordset("course", $select,'','id,fullname'); 01155 foreach ($rs as $course) { 01156 $modinfo = serialize(get_array_of_activities($course->id)); 01157 $DB->set_field("course", "modinfo", $modinfo, array("id"=>$course->id)); 01158 // update cached global COURSE too ;-) 01159 if ($course->id == $COURSE->id) { 01160 $COURSE->modinfo = $modinfo; 01161 } 01162 } 01163 $rs->close(); 01164 // reset the fast modinfo cache 01165 $reset = 'reset'; 01166 get_fast_modinfo($reset); 01167 } 01168 01169 01178 class cached_cm_info { 01183 public $name; 01184 01194 public $icon; 01195 01202 public $iconcomponent; 01203 01208 public $content; 01209 01216 public $customdata; 01217 01223 public $extraclasses; 01224 01230 public $iconurl; 01231 01236 public $onclick; 01237 }