0d8b6a69 |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // |
4 | // Moodle is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // Moodle is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | /** |
18 | * modinfolib.php - Functions/classes relating to cached information about module instances on |
19 | * a course. |
20 | * @package core |
21 | * @subpackage lib |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
23 | * @author sam marshall |
24 | */ |
25 | |
26 | |
27 | // Maximum number of modinfo items to keep in memory cache. Do not increase this to a large |
28 | // number because: |
29 | // a) modinfo can be big (megabyte range) for some courses |
30 | // b) performance of cache will deteriorate if there are very many items in it |
31 | if (!defined('MAX_MODINFO_CACHE_SIZE')) { |
32 | define('MAX_MODINFO_CACHE_SIZE', 10); |
33 | } |
34 | |
35 | |
36 | /** |
37 | * Information about a course that is cached in the course table 'modinfo' field (and then in |
38 | * memory) in order to reduce the need for other database queries. |
39 | * |
40 | * This includes information about the course-modules and the sections on the course. It can also |
41 | * include dynamic data that has been updated for the current user. |
42 | */ |
43 | class course_modinfo { |
44 | // For convenience we store the course object here as it is needed in other parts of code |
45 | private $course; |
46 | |
47 | // Existing data fields |
48 | /////////////////////// |
49 | |
50 | // These are public for backward compatibility. Note: it is not possible to retain BC |
51 | // using PHP magic get methods because behaviour is different with regard to empty(). |
52 | |
53 | /** |
54 | * Course ID |
55 | * @var int |
56 | * @deprecated For new code, use get_course_id instead. |
57 | */ |
58 | public $courseid; |
59 | |
60 | /** |
61 | * User ID |
62 | * @var int |
63 | * @deprecated For new code, use get_user_id instead. |
64 | */ |
65 | public $userid; |
66 | |
67 | /** |
68 | * Array from int (section num, e.g. 0) => array of int (course-module id); this list only |
69 | * includes sections that actually contain at least one course-module |
70 | * @var array |
71 | * @deprecated For new code, use get_sections instead |
72 | */ |
73 | public $sections; |
74 | |
75 | /** |
76 | * Array from int (cm id) => cm_info object |
77 | * @var array |
78 | * @deprecated For new code, use get_cms or get_cm instead. |
79 | */ |
80 | public $cms; |
81 | |
82 | /** |
83 | * Array from string (modname) => int (instance id) => cm_info object |
84 | * @var array |
85 | * @deprecated For new code, use get_instances or get_instances_of instead. |
86 | */ |
87 | public $instances; |
88 | |
89 | /** |
90 | * Groups that the current user belongs to. This value is usually not available (set to null) |
91 | * unless the course has activities set to groupmembersonly. When set, it is an array of |
92 | * grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'. |
93 | * @var array |
94 | * @deprecated Don't use this! For new code, use get_groups. |
95 | */ |
96 | public $groups; |
97 | |
98 | // Get methods for data |
99 | /////////////////////// |
100 | |
101 | /** |
102 | * @return object Moodle course object that was used to construct this data |
103 | */ |
104 | public function get_course() { |
105 | return $this->course; |
106 | } |
107 | |
108 | /** |
109 | * @return int Course ID |
110 | */ |
111 | public function get_course_id() { |
112 | return $this->courseid; |
113 | } |
114 | |
115 | /** |
116 | * @return int User ID |
117 | */ |
118 | public function get_user_id() { |
119 | return $this->userid; |
120 | } |
121 | |
122 | /** |
123 | * @return array Array from section number (e.g. 0) to array of course-module IDs in that |
124 | * section; this only includes sections that contain at least one course-module |
125 | */ |
126 | public function get_sections() { |
127 | return $this->sections; |
128 | } |
129 | |
130 | /** |
131 | * @return array Array from course-module instance to cm_info object within this course, in |
132 | * order of appearance |
133 | */ |
134 | public function get_cms() { |
135 | return $this->cms; |
136 | } |
137 | |
138 | /** |
139 | * Obtains a single course-module object (for a course-module that is on this course). |
140 | * @param int $cmid Course-module ID |
141 | * @return cm_info Information about that course-module |
142 | * @throws moodle_exception If the course-module does not exist |
143 | */ |
144 | public function get_cm($cmid) { |
145 | if (empty($this->cms[$cmid])) { |
146 | throw new moodle_exception('invalidcoursemodule', 'error'); |
147 | } |
148 | return $this->cms[$cmid]; |
149 | } |
150 | |
151 | /** |
152 | * Obtains all module instances on this course. |
153 | * @return array Array from module name => array from instance id => cm_info |
154 | */ |
155 | public function get_instances() { |
156 | return $this->instances; |
157 | } |
158 | |
159 | /** |
160 | * Obtains all instances of a particular module on this course. |
161 | * @param $modname Name of module (not full frankenstyle) e.g. 'label' |
162 | * @return array Array from instance id => cm_info for modules on this course; empty if none |
163 | */ |
164 | public function get_instances_of($modname) { |
165 | if (empty($this->instances[$modname])) { |
166 | return array(); |
167 | } |
168 | return $this->instances[$modname]; |
169 | } |
170 | |
171 | /** |
172 | * Returns groups that the current user belongs to on the course. Note: If not already |
173 | * available, this may make a database query. |
174 | * @param int $groupingid Grouping ID or 0 (default) for all groups |
175 | * @return array Array of int (group id) => int (same group id again); empty array if none |
176 | */ |
177 | public function get_groups($groupingid=0) { |
178 | if (is_null($this->groups)) { |
179 | // NOTE: Performance could be improved here. The system caches user groups |
180 | // in $USER->groupmember[$courseid] => array of groupid=>groupid. Unfortunately this |
181 | // structure does not include grouping information. It probably could be changed to |
182 | // do so, without a significant performance hit on login, thus saving this one query |
183 | // each request. |
184 | $this->groups = groups_get_user_groups($this->courseid, $this->userid); |
185 | } |
186 | if (!isset($this->groups[$groupingid])) { |
187 | return array(); |
188 | } |
189 | return $this->groups[$groupingid]; |
190 | } |
191 | |
192 | /** |
193 | * Constructs based on course. |
194 | * Note: This constructor should not usually be called directly. |
195 | * Use get_fast_modinfo($course) instead as this maintains a cache. |
196 | * @param object $course Moodle course object, which may include modinfo |
197 | * @param int $userid User ID |
198 | */ |
199 | public function __construct($course, $userid) { |
d6f4508c |
200 | global $CFG, $DB; |
0d8b6a69 |
201 | |
202 | // Set initial values |
203 | $this->courseid = $course->id; |
204 | $this->userid = $userid; |
205 | $this->sections = array(); |
206 | $this->cms = array(); |
207 | $this->instances = array(); |
208 | $this->groups = null; |
209 | $this->course = $course; |
210 | |
211 | // Check modinfo field is set. If not, build and load it. |
212 | if (empty($course->modinfo)) { |
213 | rebuild_course_cache($course->id); |
214 | $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id)); |
215 | } |
216 | |
217 | // Load modinfo field into memory as PHP object and check it's valid |
218 | $info = unserialize($course->modinfo); |
219 | if (!is_array($info)) { |
220 | // hmm, something is wrong - lets try to fix it |
221 | rebuild_course_cache($course->id); |
222 | $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id)); |
223 | $info = unserialize($course->modinfo); |
224 | if (!is_array($info)) { |
225 | // If it still fails, abort |
226 | debugging('Problem with "modinfo" data for this course'); |
227 | return; |
228 | } |
229 | } |
230 | |
231 | // If we haven't already preloaded contexts for the course, do it now |
232 | preload_course_contexts($course->id); |
233 | |
234 | // Loop through each piece of module data, constructing it |
235 | $modexists = array(); |
236 | foreach ($info as $mod) { |
237 | if (empty($mod->name)) { |
238 | // something is wrong here |
239 | continue; |
240 | } |
241 | |
242 | // Skip modules which don't exist |
243 | if (empty($modexists[$mod->mod])) { |
244 | if (!file_exists("$CFG->dirroot/mod/$mod->mod/lib.php")) { |
245 | continue; |
246 | } |
247 | $modexists[$mod->mod] = true; |
248 | } |
249 | |
250 | // Construct info for this module |
251 | $cm = new cm_info($this, $course, $mod, $info); |
252 | |
253 | // Store module in instances and cms array |
254 | if (!isset($this->instances[$cm->modname])) { |
255 | $this->instances[$cm->modname] = array(); |
256 | } |
257 | $this->instances[$cm->modname][$cm->instance] = $cm; |
258 | $this->cms[$cm->id] = $cm; |
259 | |
260 | // Reconstruct sections. This works because modules are stored in order |
261 | if (!isset($this->sections[$cm->sectionnum])) { |
262 | $this->sections[$cm->sectionnum] = array(); |
263 | } |
264 | $this->sections[$cm->sectionnum][] = $cm->id; |
265 | } |
266 | |
267 | // We need at least 'dynamic' data from each course-module (this is basically the remaining |
268 | // data which was always present in previous version of get_fast_modinfo, so it's required |
269 | // for BC). Creating it in a second pass is necessary because obtain_dynamic_data sometimes |
270 | // needs to be able to refer to a 'complete' (with basic data) modinfo. |
271 | foreach ($this->cms as $cm) { |
272 | $cm->obtain_dynamic_data(); |
273 | } |
274 | } |
275 | } |
276 | |
277 | |
278 | /** |
279 | * Data about a single module on a course. This contains most of the fields in the course_modules |
280 | * table, plus additional data when required. |
281 | * |
282 | * This object has many public fields; code should treat all these fields as read-only and set |
283 | * data only using the supplied set functions. Setting the fields directly is not supported |
284 | * and may cause problems later. |
285 | */ |
286 | class cm_info { |
287 | /** |
288 | * State: Only basic data from modinfo cache is available. |
289 | */ |
290 | const STATE_BASIC = 0; |
291 | |
292 | /** |
293 | * State: Dynamic data is available too. |
294 | */ |
295 | const STATE_DYNAMIC = 1; |
296 | |
297 | /** |
298 | * State: View data (for course page) is available. |
299 | */ |
300 | const STATE_VIEW = 2; |
301 | |
302 | /** |
303 | * Parent object |
304 | * @var course_modinfo |
305 | */ |
306 | private $modinfo; |
307 | |
308 | /** |
309 | * Level of information stored inside this object (STATE_xx constant) |
310 | * @var int |
311 | */ |
312 | private $state; |
313 | |
314 | // Existing data fields |
315 | /////////////////////// |
316 | |
317 | /** |
318 | * Course-module ID - from course_modules table |
319 | * @var int |
320 | */ |
321 | public $id; |
322 | |
323 | /** |
324 | * Module instance (ID within module table) - from course_modules table |
325 | * @var int |
326 | */ |
327 | public $instance; |
328 | |
329 | /** |
330 | * Course ID - from course_modules table |
331 | * @var int |
332 | */ |
333 | public $course; |
334 | |
335 | /** |
336 | * 'ID number' from course-modules table (arbitrary text set by user) - from |
337 | * course_modules table |
338 | * @var string |
339 | */ |
340 | public $idnumber; |
341 | |
342 | /** |
343 | * Visible setting (0 or 1; if this is 0, students cannot see/access the activity) - from |
344 | * course_modules table |
345 | * @var int |
346 | */ |
347 | public $visible; |
348 | |
349 | /** |
350 | * Group mode (one of the constants NONE, SEPARATEGROUPS, or VISIBLEGROUPS) - from |
351 | * course_modules table |
352 | * @var int |
353 | */ |
354 | public $groupmode; |
355 | |
356 | /** |
357 | * Grouping ID (0 = all groupings) |
358 | * @var int |
359 | */ |
360 | public $groupingid; |
361 | |
362 | /** |
363 | * Group members only (if set to 1, only members of a suitable group see this link on the |
364 | * course page; 0 = everyone sees it even if they don't belong to a suitable group) - from |
365 | * course_modules table |
366 | * @var int |
367 | */ |
368 | public $groupmembersonly; |
369 | |
370 | /** |
371 | * Indent level on course page (0 = no indent) - from course_modules table |
372 | * @var int |
373 | */ |
374 | public $indent; |
375 | |
376 | /** |
377 | * Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from |
378 | * course_modules table |
379 | * @var int |
380 | */ |
381 | public $completion; |
382 | |
383 | /** |
384 | * Available date for this activity (0 if not set, or set to seconds since epoch; before this |
385 | * date, activity does not display to students) - from course_modules table |
386 | * @var int |
387 | */ |
388 | public $availablefrom; |
389 | |
390 | /** |
391 | * Available until date for this activity (0 if not set, or set to seconds since epoch; from |
392 | * this date, activity does not display to students) - from course_modules table |
393 | * @var int |
394 | */ |
395 | public $availableuntil; |
396 | |
397 | /** |
398 | * When activity is unavailable, this field controls whether it is shown to students (0 = |
399 | * hide completely, 1 = show greyed out with information about when it will be available) - |
400 | * from course_modules table |
401 | * @var int |
402 | */ |
403 | public $showavailability; |
404 | |
405 | /** |
406 | * Extra HTML that is put in an unhelpful part of the HTML when displaying this module in |
407 | * course page - from cached data in modinfo field |
408 | * @deprecated This is crazy, don't use it. Replaced by ->extraclasses and ->onclick |
409 | * @var string |
410 | */ |
411 | public $extra; |
412 | |
413 | /** |
414 | * Name of icon to use - from cached data in modinfo field |
415 | * @var string |
416 | */ |
417 | public $icon; |
418 | |
419 | /** |
420 | * Component that contains icon - from cached data in modinfo field |
421 | * @var string |
422 | */ |
423 | public $iconcomponent; |
424 | |
425 | /** |
426 | * Name of module e.g. 'forum' (this is the same name as the module's main database |
427 | * table) - from cached data in modinfo field |
428 | * @var string |
429 | */ |
430 | public $modname; |
431 | |
432 | /** |
433 | * Name of module instance for display on page e.g. 'General discussion forum' - from cached |
434 | * data in modinfo field |
435 | * @var string |
436 | */ |
437 | public $name; |
438 | |
439 | /** |
440 | * Section number that this course-module is in (section 0 = above the calendar, section 1 |
441 | * = week/topic 1, etc) - from cached data in modinfo field |
442 | * @var string |
443 | */ |
444 | public $sectionnum; |
445 | |
446 | /** |
447 | * Availability conditions for this course-module based on the completion of other |
448 | * course-modules (array from other course-module id to required completion state for that |
449 | * module) - from cached data in modinfo field |
450 | * @var array |
451 | */ |
452 | public $conditionscompletion; |
453 | |
454 | /** |
455 | * Availability conditions for this course-module based on course grades (array from |
456 | * grade item id to object with ->min, ->max fields) - from cached data in modinfo field |
457 | * @var array |
458 | */ |
459 | public $conditionsgrade; |
460 | |
461 | /** |
462 | * Plural name of module type, e.g. 'Forums' - from lang file |
463 | * @deprecated Do not use this value (you can obtain it by calling get_string instead); it |
464 | * will be removed in a future version (see later TODO in this file) |
465 | * @var string |
466 | */ |
467 | public $modplural; |
468 | |
469 | /** |
470 | * True if this course-module is available to students i.e. if all availability conditions |
471 | * are met - obtained dynamically |
472 | * @var bool |
473 | */ |
474 | public $available; |
475 | |
476 | /** |
477 | * If course-module is not available to students, this string gives information about |
478 | * availability which can be displayed to students and/or staff (e.g. 'Available from 3 |
479 | * January 2010') for display on main page - obtained dynamically |
480 | * @var string |
481 | */ |
482 | public $availableinfo; |
483 | |
484 | /** |
485 | * True if this course-module is available to the CURRENT user (for example, if current user |
486 | * has viewhiddenactivities capability, they can access the course-module even if it is not |
487 | * visible or not available, so this would be true in that case) |
488 | * @var bool |
489 | */ |
490 | public $uservisible; |
491 | |
492 | // New data available only via functions |
493 | //////////////////////////////////////// |
494 | |
495 | /** |
496 | * @var moodle_url |
497 | */ |
498 | private $url; |
499 | |
500 | /** |
501 | * @var string |
502 | */ |
503 | private $content; |
504 | |
505 | /** |
506 | * @var string |
507 | */ |
508 | private $extraclasses; |
509 | |
510 | /** |
511 | * @var string |
512 | */ |
513 | private $onclick; |
514 | |
515 | /** |
516 | * @var mixed |
517 | */ |
518 | private $customdata; |
519 | |
520 | /** |
521 | * @var string |
522 | */ |
523 | private $afterlink; |
524 | |
525 | /** |
526 | * @var string |
527 | */ |
528 | private $afterediticons; |
529 | |
530 | /** |
531 | * @return bool True if this module has a 'view' page that should be linked to in navigation |
532 | * etc (note: modules may still have a view.php file, but return false if this is not |
533 | * intended to be linked to from 'normal' parts of the interface; this is what label does). |
534 | */ |
535 | public function has_view() { |
536 | return !is_null($this->url); |
537 | } |
538 | |
539 | /** |
540 | * @return moodle_url URL to link to for this module, or null if it doesn't have a view page |
541 | */ |
542 | public function get_url() { |
543 | return $this->url; |
544 | } |
545 | |
546 | /** |
547 | * Obtains content to display on main (view) page. |
548 | * Note: Will collect view data, if not already obtained. |
549 | * @return string Content to display on main page below link, or empty string if none |
550 | */ |
551 | public function get_content() { |
552 | $this->obtain_view_data(); |
553 | return $this->content; |
554 | } |
555 | |
556 | /** |
557 | * Note: Will collect view data, if not already obtained. |
558 | * @return string Extra CSS classes to add to html output for this activity on main page |
559 | */ |
560 | public function get_extra_classes() { |
561 | $this->obtain_view_data(); |
562 | return $this->extraclasses; |
563 | } |
564 | |
565 | /** |
566 | * @return string Content of HTML on-click attribute. This string will be used literally |
567 | * as a string so should be pre-escaped. |
568 | */ |
569 | public function get_on_click() { |
570 | // Does not need view data; may be used by navigation |
571 | return $this->onclick; |
572 | } |
573 | /** |
574 | * @return mixed Optional custom data stored in modinfo cache for this activity, or null if none |
575 | */ |
576 | public function get_custom_data() { |
577 | return $this->customdata; |
578 | } |
579 | |
580 | /** |
581 | * Note: Will collect view data, if not already obtained. |
582 | * @return string Extra HTML code to display after link |
583 | */ |
584 | public function get_after_link() { |
585 | $this->obtain_view_data(); |
586 | return $this->afterlink; |
587 | } |
588 | |
589 | /** |
590 | * Note: Will collect view data, if not already obtained. |
591 | * @return string Extra HTML code to display after editing icons (e.g. more icons) |
592 | */ |
593 | public function get_after_edit_icons() { |
594 | $this->obtain_view_data(); |
595 | return $this->afterediticons; |
596 | } |
597 | |
598 | /** |
599 | * @param moodle_core_renderer $output Output render to use, or null for default (global) |
600 | * @return moodle_url Icon URL for a suitable icon to put beside this cm |
601 | */ |
602 | public function get_icon_url($output = null) { |
603 | global $OUTPUT; |
604 | if (!$output) { |
605 | $output = $OUTPUT; |
606 | } |
607 | if (!empty($this->icon)) { |
608 | if (substr($this->icon, 0, 4) === 'mod/') { |
609 | list($modname, $iconname) = explode('/', substr($this->icon, 4), 2); |
610 | $icon = $output->pix_url($iconname, $modname); |
611 | } else { |
612 | if (!empty($this->iconcomponent)) { |
613 | // Icon has specified component |
614 | $icon = $output->pix_url($this->icon, $this->iconcomponent); |
615 | } else { |
616 | // Icon does not have specified component, use default |
617 | $icon = $output->pix_url($this->icon); |
618 | } |
619 | } |
620 | } else { |
621 | $icon = $output->pix_url('icon', $this->modname); |
622 | } |
623 | return $icon; |
624 | } |
625 | |
626 | /** |
627 | * @return course_modinfo Modinfo object that this came from |
628 | */ |
629 | public function get_modinfo() { |
630 | return $this->modinfo; |
631 | } |
632 | |
633 | /** |
634 | * @return object Moodle course object that was used to construct this data |
635 | */ |
636 | public function get_course() { |
637 | return $this->modinfo->get_course(); |
638 | } |
639 | |
640 | // Set functions |
641 | //////////////// |
642 | |
643 | /** |
644 | * Sets content to display on course view page below link (if present). |
645 | * @param string $content New content as HTML string (empty string if none) |
646 | * @return void |
647 | */ |
648 | public function set_content($content) { |
649 | $this->content = $content; |
650 | } |
651 | |
652 | /** |
653 | * Sets extra classes to include in CSS. |
654 | * @param string $extraclasses Extra classes (empty string if none) |
655 | * @return void |
656 | */ |
657 | public function set_extra_classes($extraclasses) { |
658 | $this->extraclasses = $extraclasses; |
659 | } |
660 | |
661 | /** |
662 | * Sets value of on-click attribute for JavaScript. |
663 | * Note: May not be called from _cm_info_view (only _cm_info_dynamic). |
664 | * @param string $onclick New onclick attribute which should be HTML-escaped |
665 | * (empty string if none) |
666 | * @return void |
667 | */ |
668 | public function set_on_click($onclick) { |
669 | $this->check_not_view_only(); |
670 | $this->onclick = $onclick; |
671 | } |
672 | |
673 | /** |
674 | * Sets HTML that displays after link on course view page. |
675 | * @param string $afterlink HTML string (empty string if none) |
676 | * @return void |
677 | */ |
678 | public function set_after_link($afterlink) { |
679 | $this->afterlink = $afterlink; |
680 | } |
681 | |
682 | /** |
683 | * Sets HTML that displays after edit icons on course view page. |
684 | * @param string $afterediticons HTML string (empty string if none) |
685 | * @return void |
686 | */ |
687 | public function set_after_edit_icons($afterediticons) { |
688 | $this->afterediticons = $afterediticons; |
689 | } |
690 | |
691 | /** |
692 | * Changes the name (text of link) for this module instance. |
693 | * Note: May not be called from _cm_info_view (only _cm_info_dynamic). |
694 | * @param string $name Name of activity / link text |
695 | * @return void |
696 | */ |
697 | public function set_name($name) { |
698 | $this->update_user_visible(); |
699 | $this->name = $name; |
700 | } |
701 | |
702 | /** |
703 | * Turns off the view link for this module instance. |
704 | * Note: May not be called from _cm_info_view (only _cm_info_dynamic). |
705 | * @return void |
706 | */ |
707 | public function set_no_view_link() { |
708 | $this->check_not_view_only(); |
709 | $url = null; |
710 | } |
711 | |
712 | /** |
713 | * Sets the 'uservisible' flag. This can be used (by setting false) to prevent access and |
714 | * display of this module link for the current user. |
715 | * Note: May not be called from _cm_info_view (only _cm_info_dynamic). |
716 | * @param bool $uservisible |
717 | * @return void |
718 | */ |
719 | public function set_user_visible($uservisible) { |
720 | $this->check_not_view_only(); |
721 | $this->uservisible = $uservisible; |
722 | } |
723 | |
724 | /** |
725 | * Sets the 'available' flag and related details. This flag is normally used to make |
726 | * course modules unavailable until a certain date or condition is met. (When a course |
727 | * module is unavailable, it is still visible to users who have viewhiddenactivities |
728 | * permission.) |
729 | * |
730 | * When this is function is called, user-visible status is recalculated automatically. |
731 | * |
732 | * Note: May not be called from _cm_info_view (only _cm_info_dynamic). |
733 | * @param bool $available False if this item is not 'available' |
734 | * @param int $showavailability 0 = do not show this item at all if it's not available, |
735 | * 1 = show this item greyed out with the following message |
736 | * @param string $availableinfo Information about why this is not available which displays |
737 | * to those who have viewhiddenactivities, and to everyone if showavailability is set; |
738 | * note that this function replaces the existing data (if any) |
739 | * @return void |
740 | */ |
741 | public function set_available($available, $showavailability=0, $availableinfo='') { |
742 | $this->check_not_view_only(); |
743 | $this->available = $available; |
744 | $this->showavailability = $showavailability; |
745 | $this->availableinfo = $availableinfo; |
746 | $this->update_user_visible(); |
747 | } |
748 | |
749 | /** |
750 | * Some set functions can only be called from _cm_info_dynamic and not _cm_info_view. |
751 | * This is because they may affect parts of this object which are used on pages other |
752 | * than the view page (e.g. in the navigation block, or when checking access on |
753 | * module pages). |
754 | * @return void |
755 | */ |
756 | private function check_not_view_only() { |
757 | if ($this->state >= self::STATE_DYNAMIC) { |
758 | throw new coding_exception('Cannot set this data from _cm_info_view because it may ' . |
759 | 'affect other pages as well as view'); |
760 | } |
761 | } |
762 | |
763 | /** |
764 | * Constructor should not be called directly; use get_fast_modinfo. |
765 | * @param course_modinfo $modinfo Parent object |
766 | * @param object $course Course row |
767 | * @param object $mod Module object from the modinfo field of course table |
768 | * @param object $info Entire object from modinfo field of course table |
769 | */ |
770 | public function __construct(course_modinfo $modinfo, $course, $mod, $info) { |
771 | global $CFG; |
772 | $this->modinfo = $modinfo; |
773 | |
774 | $this->id = $mod->cm; |
775 | $this->instance = $mod->id; |
776 | $this->course = $course->id; |
777 | $this->modname = $mod->mod; |
778 | $this->idnumber = isset($mod->idnumber) ? $mod->idnumber : ''; |
779 | $this->name = $mod->name; |
780 | $this->visible = $mod->visible; |
781 | $this->sectionnum = $mod->section; |
782 | $this->groupmode = isset($mod->groupmode) ? $mod->groupmode : 0; |
783 | $this->groupingid = isset($mod->groupingid) ? $mod->groupingid : 0; |
784 | $this->groupmembersonly = isset($mod->groupmembersonly) ? $mod->groupmembersonly : 0; |
785 | $this->indent = isset($mod->indent) ? $mod->indent : 0; |
786 | $this->completion = isset($mod->completion) ? $mod->completion : 0; |
787 | $this->extra = isset($mod->extra) ? $mod->extra : ''; |
788 | $this->extraclasses = isset($mod->extraclasses) ? $mod->extraclasses : ''; |
789 | $this->onclick = isset($mod->onclick) ? $mod->onclick : ''; |
790 | $this->content = isset($mod->content) ? $mod->content : ''; |
791 | $this->icon = isset($mod->icon) ? $mod->icon : ''; |
792 | $this->iconcomponent = isset($mod->iconcomponent) ? $mod->iconcomponent : ''; |
793 | $this->customdata = isset($mod->customdata) ? $mod->customdata : ''; |
794 | $this->state = self::STATE_BASIC; |
795 | |
796 | // This special case handles old label data. Labels used to use the 'name' field for |
797 | // content |
798 | if ($this->modname === 'label' && $this->content === '') { |
799 | $this->content = $this->extra; |
800 | $this->extra = ''; |
801 | } |
802 | |
803 | if (!empty($CFG->enableavailability)) { |
804 | // We must have completion information from modinfo. If it's not |
805 | // there, cache needs rebuilding |
806 | if (!isset($mod->showavailability)) { |
807 | throw new modinfo_rebuild_cache_exception( |
808 | 'enableavailability option was changed; rebuilding '. |
809 | 'cache for course ' . $course->id); |
810 | } |
811 | $this->showavailability = $mod->showavailability; |
812 | $this->availablefrom = isset($mod->availablefrom) ? $mod->availablefrom : 0; |
813 | $this->availableuntil = isset($mod->availableuntil) ? $mod->availableuntil : 0; |
814 | $this->conditionscompletion = isset($mod->conditionscompletion) |
815 | ? $mod->conditionscompletion : array(); |
816 | $this->conditionsgrade = isset($mod->conditionsgrade) |
817 | ? $mod->conditionsgrade : array(); |
818 | } |
819 | |
820 | // Get module plural name. |
821 | // TODO This was a very old performance hack and should now be removed as the information |
822 | // certainly doesn't belong in modinfo. On a 'normal' page this is only used in the |
823 | // activity_modules block, so if it needs caching, it should be cached there. |
824 | static $modplurals; |
825 | if (!isset($modplurals[$this->modname])) { |
826 | $modplurals[$this->modname] = get_string('modulenameplural', $this->modname); |
827 | } |
828 | $this->modplural = $modplurals[$this->modname]; |
829 | |
830 | static $modviews; |
831 | if (!isset($modviews[$this->modname])) { |
832 | $modviews[$this->modname] = !plugin_supports('mod', $this->modname, |
833 | FEATURE_NO_VIEW_LINK); |
834 | } |
835 | $this->url = $modviews[$this->modname] |
836 | ? new moodle_url('/mod/' . $this->modname . '/view.php', array('id'=>$this->id)) |
837 | : null; |
838 | } |
839 | |
840 | /** |
841 | * If dynamic data for this course-module is not yet available, gets it. |
842 | * |
843 | * This function is automatically called when constructing course_modinfo, so users don't |
844 | * need to call it. |
845 | * |
846 | * Dynamic data is data which does not come directly from the cache but is calculated at |
847 | * runtime based on the current user. Primarily this concerns whether the user can access |
848 | * the module or not. |
849 | * |
850 | * As part of this function, the module's _cm_info_dynamic function from its lib.php will |
851 | * be called (if it exists). |
852 | * @return void |
853 | */ |
854 | public function obtain_dynamic_data() { |
855 | global $CFG; |
856 | if ($this->state >= self::STATE_DYNAMIC) { |
857 | return; |
858 | } |
859 | $userid = $this->modinfo->get_user_id(); |
860 | |
861 | if (!empty($CFG->enableavailability)) { |
862 | // Get availability information |
863 | $ci = new condition_info($this); |
864 | // Note that the modinfo currently available only includes minimal details (basic data) |
865 | // so passing it to this function is a bit dangerous as it would cause infinite |
866 | // recursion if it tried to get dynamic data, however we know that this function only |
867 | // uses basic data. |
868 | $this->available = $ci->is_available($this->availableinfo, true, |
869 | $userid, $this->modinfo); |
870 | } else { |
871 | $this->available = true; |
872 | } |
873 | |
874 | // Update visible state for current user |
875 | $this->update_user_visible(); |
876 | |
877 | // Let module make dynamic changes at this point |
878 | $this->call_mod_function('cm_info_dynamic'); |
879 | $this->state = self::STATE_DYNAMIC; |
880 | } |
881 | |
882 | /** |
883 | * Works out whether activity is visible *for current user* - if this is false, they |
884 | * aren't allowed to access it. |
885 | * @return void |
886 | */ |
887 | private function update_user_visible() { |
888 | global $CFG; |
889 | $modcontext = get_context_instance(CONTEXT_MODULE, $this->id); |
890 | $userid = $this->modinfo->get_user_id(); |
891 | $this->uservisible = true; |
892 | if ((!$this->visible or !$this->available) and |
893 | !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) { |
894 | // If the activity is hidden or unavailable, and you don't have viewhiddenactivities, |
895 | // set it so that user can't see or access it |
896 | $this->uservisible = false; |
897 | } else if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly) |
898 | and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) { |
899 | // If the activity has 'group members only' and you don't have accessallgroups... |
900 | $groups = $this->modinfo->get_groups(); |
901 | if (empty($this->groups[$this->groupingid])) { |
902 | // ...and you don't belong to a group, then set it so you can't see/access it |
903 | $this->uservisible = false; |
904 | } |
905 | } |
906 | } |
907 | |
908 | /** |
909 | * Calls a module function (if exists), passing in one parameter: this object. |
910 | * @param string $type Name of function e.g. if this is 'grooblezorb' and the modname is |
911 | * 'forum' then it will try to call 'mod_forum_grooblezorb' or 'forum_grooblezorb' |
912 | * @return void |
913 | */ |
914 | private function call_mod_function($type) { |
915 | global $CFG; |
916 | $libfile = $CFG->dirroot . '/mod/' . $this->modname . '/lib.php'; |
917 | if (file_exists($libfile)) { |
918 | include_once($libfile); |
919 | $function = 'mod_' . $this->modname . '_' . $type; |
920 | if (function_exists($function)) { |
921 | $function($this); |
922 | } else { |
923 | $function = $this->modname . '_' . $type; |
924 | if (function_exists($function)) { |
925 | $function($this); |
926 | } |
927 | } |
928 | } |
929 | } |
930 | |
931 | /** |
932 | * If view data for this course-module is not yet available, obtains it. |
933 | * |
934 | * This function is automatically called if any of the functions (marked) which require |
935 | * view data are called. |
936 | * |
937 | * View data is data which is needed only for displaying the course main page (& any similar |
938 | * functionality on other pages) but is not needed in general. Obtaining view data may have |
939 | * a performance cost. |
940 | * |
941 | * As part of this function, the module's _cm_info_view function from its lib.php will |
942 | * be called (if it exists). |
943 | * @return void |
944 | */ |
945 | private function obtain_view_data() { |
946 | if ($this->state >= self::STATE_VIEW) { |
947 | return; |
948 | } |
949 | |
950 | // Let module make changes at this point |
951 | $this->call_mod_function('cm_info_view'); |
952 | $this->state = self::STATE_VIEW; |
953 | } |
954 | } |
955 | |
956 | |
957 | /** |
958 | * Special exception that may only be thrown within the constructor for course_modinfo to |
959 | * indicate that the cache needs to be rebuilt. Not for use anywhere else. |
960 | */ |
961 | class modinfo_rebuild_cache_exception extends coding_exception { |
962 | function __construct($why) { |
963 | // If it ever escapes, that's a code bug |
964 | parent::__construct('This exception should be caught by code', $why); |
965 | } |
966 | } |
967 | |
968 | |
969 | /** |
970 | * Returns reference to full info about modules in course (including visibility). |
971 | * Cached and as fast as possible (0 or 1 db query). |
972 | * |
973 | * @global object |
974 | * @global object |
975 | * @global moodle_database |
976 | * @uses MAX_MODINFO_CACHE_SIZE |
977 | * @param mixed $course object or 'reset' string to reset caches, modinfo may be updated in db |
978 | * @param int $userid Defaults to current user id |
979 | * @return course_modinfo Module information for course, or null if resetting |
980 | */ |
981 | function get_fast_modinfo(&$course, $userid=0) { |
982 | global $CFG, $USER, $DB; |
983 | require_once($CFG->dirroot.'/course/lib.php'); |
984 | |
985 | if (!empty($CFG->enableavailability)) { |
986 | require_once($CFG->libdir.'/conditionlib.php'); |
987 | } |
988 | |
989 | static $cache = array(); |
990 | |
991 | if ($course === 'reset') { |
992 | $cache = array(); |
993 | return null; |
994 | } |
995 | |
996 | if (empty($userid)) { |
997 | $userid = $USER->id; |
998 | } |
999 | |
1000 | if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) { |
1001 | return $cache[$course->id]; |
1002 | } |
1003 | |
1004 | if (!property_exists($course, 'modinfo')) { |
1005 | debugging('Coding problem - missing course modinfo property in get_fast_modinfo() call'); |
1006 | } |
1007 | |
1008 | unset($cache[$course->id]); // prevent potential reference problems when switching users |
1009 | |
1010 | try { |
1011 | $cache[$course->id] = new course_modinfo($course, $userid); |
1012 | } catch (modinfo_rebuild_cache_exception $e) { |
1013 | debugging($e->debuginfo); |
1014 | rebuild_course_cache($course->id, true); |
1015 | $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); |
1016 | // This second time we don't catch the exception - if you request cache rebuild twice |
1017 | // in a row, that's a bug => coding_exception |
1018 | $cache[$course->id] = new course_modinfo($course, $userid); |
1019 | } |
1020 | |
1021 | // Ensure cache does not use too much RAM |
1022 | if (count($cache) > MAX_MODINFO_CACHE_SIZE) { |
1023 | reset($cache); |
1024 | $key = key($cache); |
1025 | unset($cache[$key]); |
1026 | } |
1027 | |
1028 | return $cache[$course->id]; |
1029 | } |
1030 | |
1031 | |
1032 | /** |
1033 | * Class that is the return value for the _get_coursemodule_info module API function. |
1034 | * |
1035 | * Note: For backward compatibility, you can also return a stdclass object from that function. |
1036 | * The difference is that the stdclass object may contain an 'extra' field (deprecated because |
1037 | * it was crazy, except for label which uses it differently). The stdclass object may not contain |
1038 | * the new fields defined here (content, extraclasses, customdata). |
1039 | */ |
1040 | class cached_cm_info { |
1041 | /** |
1042 | * Name (text of link) for this activity; Leave unset to accept default name |
1043 | * @var string |
1044 | */ |
1045 | public $name; |
1046 | |
1047 | /** |
1048 | * Name of icon for this activity. Normally, this should be used together with $iconcomponent |
1049 | * to define the icon, as per pix_url function. |
1050 | * For backward compatibility, if this value is of the form 'mod/forum/icon' then an icon |
1051 | * within that module will be used. |
1052 | * @see cm_info::get_icon_url() |
1053 | * @see renderer_base::pix_url() |
1054 | * @var string |
1055 | */ |
1056 | public $icon; |
1057 | |
1058 | /** |
1059 | * Component for icon for this activity, as per pix_url; leave blank to use default 'moodle' |
1060 | * component |
1061 | * @see renderer_base::pix_url() |
1062 | * @var string |
1063 | */ |
1064 | public $iconcomponent; |
1065 | |
1066 | /** |
1067 | * HTML content to be displayed on the main page below the link (if any) for this course-module |
1068 | * @var string |
1069 | */ |
1070 | public $content; |
1071 | |
1072 | /** |
1073 | * Custom data to be stored in modinfo for this activity; useful if there are cases when |
1074 | * internal information for this activity type needs to be accessible from elsewhere on the |
1075 | * course without making database queries. May be of any type but should be short. |
1076 | * @var mixed |
1077 | */ |
1078 | public $customdata; |
1079 | |
1080 | /** |
1081 | * Extra CSS class or classes to be added when this activity is displayed on the main page; |
1082 | * space-separated string |
1083 | * @var string |
1084 | */ |
1085 | public $extraclasses; |
1086 | |
1087 | /** |
1088 | * Content of onclick JavaScript; escaped HTML to be inserted as attribute value |
1089 | * @var string |
1090 | */ |
1091 | public $onclick; |
1092 | } |