~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to lib/modinfolib.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
 *
40
40
 * This includes information about the course-modules and the sections on the course. It can also
41
41
 * include dynamic data that has been updated for the current user.
 
42
 *
 
43
 * Use {@link get_fast_modinfo()} to retrieve the instance of the object for particular course
 
44
 * and particular user.
 
45
 *
 
46
 * @property-read int $courseid Course ID
 
47
 * @property-read int $userid User ID
 
48
 * @property-read array $sections Array from section number (e.g. 0) to array of course-module IDs in that
 
49
 *     section; this only includes sections that contain at least one course-module
 
50
 * @property-read cm_info[] $cms Array from course-module instance to cm_info object within this course, in
 
51
 *     order of appearance
 
52
 * @property-read cm_info[][] $instances Array from string (modname) => int (instance id) => cm_info object
 
53
 * @property-read array $groups Groups that the current user belongs to. Calculated on the first request.
 
54
 *     Is an array of grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'
42
55
 */
43
 
class course_modinfo extends stdClass {
44
 
    // For convenience we store the course object here as it is needed in other parts of code
 
56
class course_modinfo {
 
57
    /**
 
58
     * List of fields from DB table 'course' that are cached in MUC and are always present in course_modinfo::$course
 
59
     * @var array
 
60
     */
 
61
    public static $cachedfields = array('shortname', 'fullname', 'format',
 
62
            'enablecompletion', 'groupmode', 'groupmodeforce', 'cacherev');
 
63
 
 
64
    /**
 
65
     * For convenience we store the course object here as it is needed in other parts of code
 
66
     * @var stdClass
 
67
     */
45
68
    private $course;
46
 
    // Array of section data from cache
 
69
 
 
70
    /**
 
71
     * Array of section data from cache
 
72
     * @var section_info[]
 
73
     */
47
74
    private $sectioninfo;
48
75
 
49
 
    // Existing data fields
50
 
    ///////////////////////
51
 
 
52
 
    // These are public for backward compatibility. Note: it is not possible to retain BC
53
 
    // using PHP magic get methods because behaviour is different with regard to empty().
54
 
 
55
 
    /**
56
 
     * Course ID
57
 
     * @var int
58
 
     * @deprecated For new code, use get_course_id instead.
59
 
     */
60
 
    public $courseid;
61
 
 
62
76
    /**
63
77
     * User ID
64
78
     * @var int
65
 
     * @deprecated For new code, use get_user_id instead.
66
79
     */
67
 
    public $userid;
 
80
    private $userid;
68
81
 
69
82
    /**
70
83
     * Array from int (section num, e.g. 0) => array of int (course-module id); this list only
71
84
     * includes sections that actually contain at least one course-module
72
85
     * @var array
73
 
     * @deprecated For new code, use get_sections instead
74
86
     */
75
 
    public $sections;
 
87
    private $sections;
76
88
 
77
89
    /**
78
90
     * Array from int (cm id) => cm_info object
79
 
     * @var array
80
 
     * @deprecated For new code, use get_cms or get_cm instead.
 
91
     * @var cm_info[]
81
92
     */
82
 
    public $cms;
 
93
    private $cms;
83
94
 
84
95
    /**
85
96
     * Array from string (modname) => int (instance id) => cm_info object
86
 
     * @var array
87
 
     * @deprecated For new code, use get_instances or get_instances_of instead.
 
97
     * @var cm_info[][]
88
98
     */
89
 
    public $instances;
 
99
    private $instances;
90
100
 
91
101
    /**
92
102
     * Groups that the current user belongs to. This value is usually not available (set to null)
93
103
     * unless the course has activities set to groupmembersonly. When set, it is an array of
94
104
     * grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'.
 
105
     * @var int[][]
 
106
     */
 
107
    private $groups;
 
108
 
 
109
    /**
 
110
     * List of class read-only properties and their getter methods.
 
111
     * Used by magic functions __get(), __isset(), __empty()
95
112
     * @var array
96
 
     * @deprecated Don't use this! For new code, use get_groups.
97
 
     */
98
 
    public $groups;
99
 
 
100
 
    // Get methods for data
101
 
    ///////////////////////
102
 
 
103
 
    /**
104
 
     * @return object Moodle course object that was used to construct this data
 
113
     */
 
114
    private static $standardproperties = array(
 
115
        'courseid' => 'get_course_id',
 
116
        'userid' => 'get_user_id',
 
117
        'sections' => 'get_sections',
 
118
        'cms' => 'get_cms',
 
119
        'instances' => 'get_instances',
 
120
        'groups' => 'get_groups_all',
 
121
    );
 
122
 
 
123
    /**
 
124
     * Magic method getter
 
125
     *
 
126
     * @param string $name
 
127
     * @return mixed
 
128
     */
 
129
    public function __get($name) {
 
130
        if (isset(self::$standardproperties[$name])) {
 
131
            $method = self::$standardproperties[$name];
 
132
            return $this->$method();
 
133
        } else {
 
134
            debugging('Invalid course_modinfo property accessed: '.$name);
 
135
            return null;
 
136
        }
 
137
    }
 
138
 
 
139
    /**
 
140
     * Magic method for function isset()
 
141
     *
 
142
     * @param string $name
 
143
     * @return bool
 
144
     */
 
145
    public function __isset($name) {
 
146
        if (isset(self::$standardproperties[$name])) {
 
147
            $value = $this->__get($name);
 
148
            return isset($value);
 
149
        }
 
150
        return false;
 
151
    }
 
152
 
 
153
    /**
 
154
     * Magic method for function empty()
 
155
     *
 
156
     * @param string $name
 
157
     * @return bool
 
158
     */
 
159
    public function __empty($name) {
 
160
        if (isset(self::$standardproperties[$name])) {
 
161
            $value = $this->__get($name);
 
162
            return empty($value);
 
163
        }
 
164
        return true;
 
165
    }
 
166
 
 
167
    /**
 
168
     * Magic method setter
 
169
     *
 
170
     * Will display the developer warning when trying to set/overwrite existing property.
 
171
     *
 
172
     * @param string $name
 
173
     * @param mixed $value
 
174
     */
 
175
    public function __set($name, $value) {
 
176
        debugging("It is not allowed to set the property course_modinfo::\${$name}", DEBUG_DEVELOPER);
 
177
    }
 
178
 
 
179
    /**
 
180
     * Returns course object that was used in the first {@link get_fast_modinfo()} call.
 
181
     *
 
182
     * It may not contain all fields from DB table {course} but always has at least the following:
 
183
     * id,shortname,fullname,format,enablecompletion,groupmode,groupmodeforce,cacherev
 
184
     *
 
185
     * @return stdClass
105
186
     */
106
187
    public function get_course() {
107
188
        return $this->course;
111
192
     * @return int Course ID
112
193
     */
113
194
    public function get_course_id() {
114
 
        return $this->courseid;
 
195
        return $this->course->id;
115
196
    }
116
197
 
117
198
    /**
130
211
    }
131
212
 
132
213
    /**
133
 
     * @return array Array from course-module instance to cm_info object within this course, in
 
214
     * @return cm_info[] Array from course-module instance to cm_info object within this course, in
134
215
     *   order of appearance
135
216
     */
136
217
    public function get_cms() {
152
233
 
153
234
    /**
154
235
     * Obtains all module instances on this course.
155
 
     * @return array Array from module name => array from instance id => cm_info
 
236
     * @return cm_info[][] Array from module name => array from instance id => cm_info
156
237
     */
157
238
    public function get_instances() {
158
239
        return $this->instances;
172
253
                $modnamesused[$mod->modname] = $modnames[$mod->modname];
173
254
            }
174
255
        }
175
 
        collatorlib::asort($modnamesused);
 
256
        core_collator::asort($modnamesused);
176
257
        return $modnamesused;
177
258
    }
178
259
 
179
260
    /**
180
261
     * Obtains all instances of a particular module on this course.
181
262
     * @param $modname Name of module (not full frankenstyle) e.g. 'label'
182
 
     * @return array Array from instance id => cm_info for modules on this course; empty if none
 
263
     * @return cm_info[] Array from instance id => cm_info for modules on this course; empty if none
183
264
     */
184
265
    public function get_instances_of($modname) {
185
266
        if (empty($this->instances[$modname])) {
189
270
    }
190
271
 
191
272
    /**
192
 
     * Returns groups that the current user belongs to on the course. Note: If not already
193
 
     * available, this may make a database query.
194
 
     * @param int $groupingid Grouping ID or 0 (default) for all groups
195
 
     * @return array Array of int (group id) => int (same group id again); empty array if none
 
273
     * Groups that the current user belongs to organised by grouping id. Calculated on the first request.
 
274
     * @return int[][] array of grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'
196
275
     */
197
 
    public function get_groups($groupingid=0) {
 
276
    private function get_groups_all() {
198
277
        if (is_null($this->groups)) {
199
278
            // NOTE: Performance could be improved here. The system caches user groups
200
279
            // in $USER->groupmember[$courseid] => array of groupid=>groupid. Unfortunately this
201
280
            // structure does not include grouping information. It probably could be changed to
202
281
            // do so, without a significant performance hit on login, thus saving this one query
203
282
            // each request.
204
 
            $this->groups = groups_get_user_groups($this->courseid, $this->userid);
 
283
            $this->groups = groups_get_user_groups($this->course->id, $this->userid);
205
284
        }
206
 
        if (!isset($this->groups[$groupingid])) {
 
285
        return $this->groups;
 
286
    }
 
287
 
 
288
    /**
 
289
     * Returns groups that the current user belongs to on the course. Note: If not already
 
290
     * available, this may make a database query.
 
291
     * @param int $groupingid Grouping ID or 0 (default) for all groups
 
292
     * @return int[] Array of int (group id) => int (same group id again); empty array if none
 
293
     */
 
294
    public function get_groups($groupingid = 0) {
 
295
        $allgroups = $this->get_groups_all();
 
296
        if (!isset($allgroups[$groupingid])) {
207
297
            return array();
208
298
        }
209
 
        return $this->groups[$groupingid];
 
299
        return $allgroups[$groupingid];
210
300
    }
211
301
 
212
302
    /**
213
303
     * Gets all sections as array from section number => data about section.
214
 
     * @return array Array of section_info objects organised by section number
 
304
     * @return section_info[] Array of section_info objects organised by section number
215
305
     */
216
306
    public function get_section_info_all() {
217
307
        return $this->sectioninfo;
235
325
    }
236
326
 
237
327
    /**
 
328
     * Static cache for generated course_modinfo instances
 
329
     *
 
330
     * @see course_modinfo::instance()
 
331
     * @see course_modinfo::clear_instance_cache()
 
332
     * @var course_modinfo[]
 
333
     */
 
334
    protected static $instancecache = array();
 
335
 
 
336
    /**
 
337
     * Timestamps (microtime) when the course_modinfo instances were last accessed
 
338
     *
 
339
     * It is used to remove the least recent accessed instances when static cache is full
 
340
     *
 
341
     * @var float[]
 
342
     */
 
343
    protected static $cacheaccessed = array();
 
344
 
 
345
    /**
 
346
     * Clears the cache used in course_modinfo::instance()
 
347
     *
 
348
     * Used in {@link get_fast_modinfo()} when called with argument $reset = true
 
349
     * and in {@link rebuild_course_cache()}
 
350
     *
 
351
     * @param null|int|stdClass $courseorid if specified removes only cached value for this course
 
352
     */
 
353
    public static function clear_instance_cache($courseorid = null) {
 
354
        if (empty($courseorid)) {
 
355
            self::$instancecache = array();
 
356
            self::$cacheaccessed = array();
 
357
            return;
 
358
        }
 
359
        if (is_object($courseorid)) {
 
360
            $courseorid = $courseorid->id;
 
361
        }
 
362
        if (isset(self::$instancecache[$courseorid])) {
 
363
            // Unsetting static variable in PHP is peculiar, it removes the reference,
 
364
            // but data remain in memory. Prior to unsetting, the varable needs to be
 
365
            // set to empty to remove its remains from memory.
 
366
            self::$instancecache[$courseorid] = '';
 
367
            unset(self::$instancecache[$courseorid]);
 
368
            unset(self::$cacheaccessed[$courseorid]);
 
369
        }
 
370
    }
 
371
 
 
372
    /**
 
373
     * Returns the instance of course_modinfo for the specified course and specified user
 
374
     *
 
375
     * This function uses static cache for the retrieved instances. The cache
 
376
     * size is limited by MAX_MODINFO_CACHE_SIZE. If instance is not found in
 
377
     * the static cache or it was created for another user or the cacherev validation
 
378
     * failed - a new instance is constructed and returned.
 
379
     *
 
380
     * Used in {@link get_fast_modinfo()}
 
381
     *
 
382
     * @param int|stdClass $courseorid object from DB table 'course' (must have field 'id'
 
383
     *     and recommended to have field 'cacherev') or just a course id
 
384
     * @param int $userid User id to populate 'availble' and 'uservisible' attributes of modules and sections.
 
385
     *     Set to 0 for current user (default). Set to -1 to avoid calculation of dynamic user-depended data.
 
386
     * @return course_modinfo
 
387
     */
 
388
    public static function instance($courseorid, $userid = 0) {
 
389
        global $USER;
 
390
        if (is_object($courseorid)) {
 
391
            $course = $courseorid;
 
392
        } else {
 
393
            $course = (object)array('id' => $courseorid);
 
394
        }
 
395
        if (empty($userid)) {
 
396
            $userid = $USER->id;
 
397
        }
 
398
 
 
399
        if (!empty(self::$instancecache[$course->id])) {
 
400
            if (self::$instancecache[$course->id]->userid == $userid &&
 
401
                    (!isset($course->cacherev) ||
 
402
                    $course->cacherev == self::$instancecache[$course->id]->get_course()->cacherev)) {
 
403
                // This course's modinfo for the same user was recently retrieved, return cached.
 
404
                self::$cacheaccessed[$course->id] = microtime(true);
 
405
                return self::$instancecache[$course->id];
 
406
            } else {
 
407
                // Prevent potential reference problems when switching users.
 
408
                self::clear_instance_cache($course->id);
 
409
            }
 
410
        }
 
411
        $modinfo = new course_modinfo($course, $userid);
 
412
 
 
413
        // We have a limit of MAX_MODINFO_CACHE_SIZE entries to store in static variable.
 
414
        if (count(self::$instancecache) >= MAX_MODINFO_CACHE_SIZE) {
 
415
            // Find the course that was the least recently accessed.
 
416
            asort(self::$cacheaccessed, SORT_NUMERIC);
 
417
            $courseidtoremove = key(array_reverse(self::$cacheaccessed, true));
 
418
            self::clear_instance_cache($courseidtoremove);
 
419
        }
 
420
 
 
421
        // Add modinfo to the static cache.
 
422
        self::$instancecache[$course->id] = $modinfo;
 
423
        self::$cacheaccessed[$course->id] = microtime(true);
 
424
 
 
425
        return $modinfo;
 
426
    }
 
427
 
 
428
    /**
238
429
     * Constructs based on course.
239
430
     * Note: This constructor should not usually be called directly.
240
431
     * Use get_fast_modinfo($course) instead as this maintains a cache.
241
 
     * @param object $course Moodle course object, which may include modinfo
 
432
     * @param stdClass $course course object, only property id is required.
242
433
     * @param int $userid User ID
 
434
     * @throws moodle_exception if course is not found
243
435
     */
244
436
    public function __construct($course, $userid) {
245
 
        global $CFG, $DB, $COURSE, $SITE;
 
437
        global $CFG, $COURSE, $SITE, $DB;
246
438
 
247
 
        if (!isset($course->modinfo) || !isset($course->sectioncache)) {
 
439
        if (!isset($course->cacherev)) {
 
440
            // We require presence of property cacherev to validate the course cache.
 
441
            // No need to clone the $COURSE or $SITE object here because we clone it below anyway.
248
442
            $course = get_course($course->id, false);
249
443
        }
250
444
 
251
 
        // Check modinfo field is set. If not, build and load it.
252
 
        if (empty($course->modinfo) || empty($course->sectioncache)) {
253
 
            rebuild_course_cache($course->id);
254
 
            $course = $DB->get_record('course', array('id'=>$course->id), '*', MUST_EXIST);
 
445
        $cachecoursemodinfo = cache::make('core', 'coursemodinfo');
 
446
 
 
447
        // Retrieve modinfo from cache. If not present or cacherev mismatches, call rebuild and retrieve again.
 
448
        $coursemodinfo = $cachecoursemodinfo->get($course->id);
 
449
        if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) {
 
450
            $coursemodinfo = self::build_course_cache($course);
255
451
        }
256
452
 
257
453
        // Set initial values
258
 
        $this->courseid = $course->id;
259
454
        $this->userid = $userid;
260
455
        $this->sections = array();
261
456
        $this->cms = array();
262
457
        $this->instances = array();
263
458
        $this->groups = null;
264
 
        $this->course = $course;
265
 
 
266
 
        // Load modinfo field into memory as PHP object and check it's valid
267
 
        $info = unserialize($course->modinfo);
268
 
        if (!is_array($info)) {
269
 
            // hmm, something is wrong - lets try to fix it
270
 
            rebuild_course_cache($course->id);
271
 
            $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
272
 
            $info = unserialize($course->modinfo);
273
 
            if (!is_array($info)) {
274
 
                // If it still fails, abort
275
 
                debugging('Problem with "modinfo" data for this course');
276
 
                return;
277
 
            }
278
 
        }
279
 
 
280
 
        // Load sectioncache field into memory as PHP object and check it's valid
281
 
        $sectioncache = unserialize($course->sectioncache);
282
 
        if (!is_array($sectioncache)) {
283
 
            // hmm, something is wrong - let's fix it
284
 
            rebuild_course_cache($course->id);
285
 
            $course->sectioncache = $DB->get_field('course', 'sectioncache', array('id'=>$course->id));
286
 
            $sectioncache = unserialize($course->sectioncache);
287
 
            if (!is_array($sectioncache)) {
288
 
                // If it still fails, abort
289
 
                debugging('Problem with "sectioncache" data for this course');
290
 
                return;
291
 
            }
292
 
        }
293
 
 
294
 
        // If we haven't already preloaded contexts for the course, do it now.
 
459
 
 
460
        // If we haven't already preloaded contexts for the course, do it now
295
461
        // Modules are also cached here as long as it's the first time this course has been preloaded.
296
 
        preload_course_contexts($course->id);
 
462
        context_helper::preload_course($course->id);
297
463
 
298
464
        // Quick integrity check: as a result of race conditions modinfo may not be regenerated after the change.
299
465
        // It is especially dangerous if modinfo contains the deleted course module, as it results in fatal error.
301
467
        if ($course->id == $COURSE->id || $course->id == $SITE->id) {
302
468
            // Only verify current course (or frontpage) as pages with many courses may not have module contexts cached.
303
469
            // (Uncached modules will result in a very slow verification).
304
 
            foreach ($info as $mod) {
 
470
            foreach ($coursemodinfo->modinfo as $mod) {
305
471
                if (!context_module::instance($mod->cm, IGNORE_MISSING)) {
306
472
                    debugging('Course cache integrity check failed: course module with id '. $mod->cm.
307
473
                            ' does not have context. Rebuilding cache for course '. $course->id);
308
 
                    rebuild_course_cache($course->id);
309
 
                    $this->course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
310
 
                    $info = unserialize($this->course->modinfo);
311
 
                    $sectioncache = unserialize($this->course->sectioncache);
 
474
                    // Re-request the course record from DB as well, don't use get_course() here.
 
475
                    $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
476
                    $coursemodinfo = self::build_course_cache($course);
312
477
                    break;
313
478
                }
314
479
            }
315
480
        }
316
481
 
 
482
        // Overwrite unset fields in $course object with cached values, store the course object.
 
483
        $this->course = fullclone($course);
 
484
        foreach ($coursemodinfo as $key => $value) {
 
485
            if ($key !== 'modinfo' && $key !== 'sectioncache' &&
 
486
                    (!isset($this->course->$key) || $key === 'cacherev')) {
 
487
                $this->course->$key = $value;
 
488
            }
 
489
        }
 
490
 
317
491
        // Loop through each piece of module data, constructing it
318
 
        $modexists = array();
319
 
        foreach ($info as $mod) {
 
492
        static $modexists = array();
 
493
        foreach ($coursemodinfo->modinfo as $mod) {
320
494
            if (empty($mod->name)) {
321
495
                // something is wrong here
322
496
                continue;
323
497
            }
324
498
 
325
499
            // Skip modules which don't exist
326
 
            if (empty($modexists[$mod->mod])) {
327
 
                if (!file_exists("$CFG->dirroot/mod/$mod->mod/lib.php")) {
328
 
                    continue;
329
 
                }
330
 
                $modexists[$mod->mod] = true;
 
500
            if (!array_key_exists($mod->mod, $modexists)) {
 
501
                $modexists[$mod->mod] = file_exists("$CFG->dirroot/mod/$mod->mod/lib.php");
 
502
            }
 
503
            if (!$modexists[$mod->mod]) {
 
504
                continue;
331
505
            }
332
506
 
333
507
            // Construct info for this module
334
 
            $cm = new cm_info($this, $course, $mod, $info);
 
508
            $cm = new cm_info($this, null, $mod, null);
335
509
 
336
510
            // Store module in instances and cms array
337
511
            if (!isset($this->instances[$cm->modname])) {
349
523
 
350
524
        // Expand section objects
351
525
        $this->sectioninfo = array();
352
 
        foreach ($sectioncache as $number => $data) {
353
 
            // Calculate sequence
354
 
            if (isset($this->sections[$number])) {
355
 
                $sequence = implode(',', $this->sections[$number]);
356
 
            } else {
357
 
                $sequence = '';
358
 
            }
359
 
            // Expand
360
 
            $this->sectioninfo[$number] = new section_info($data, $number, $course->id, $sequence,
361
 
                    $this, $userid);
362
 
        }
363
 
 
364
 
        // We need at least 'dynamic' data from each course-module (this is basically the remaining
365
 
        // data which was always present in previous version of get_fast_modinfo, so it's required
366
 
        // for BC). Creating it in a second pass is necessary because obtain_dynamic_data sometimes
367
 
        // needs to be able to refer to a 'complete' (with basic data) modinfo.
368
 
        foreach ($this->cms as $cm) {
369
 
            $cm->obtain_dynamic_data();
 
526
        foreach ($coursemodinfo->sectioncache as $number => $data) {
 
527
            $this->sectioninfo[$number] = new section_info($data, $number, null, null,
 
528
                    $this, null);
370
529
        }
371
530
    }
372
531
 
375
534
     * the course cache. (Does not include information that is already cached
376
535
     * in some other way.)
377
536
     *
378
 
     * Used internally by rebuild_course_cache function; do not use otherwise.
 
537
     * This function will be removed in 2.7
 
538
     *
 
539
     * @deprecated since 2.6
379
540
     * @param int $courseid Course ID
380
541
     * @return array Information about sections, indexed by section number (not id)
381
542
     */
382
543
    public static function build_section_cache($courseid) {
383
544
        global $DB;
 
545
        debugging('Function course_modinfo::build_section_cache() is deprecated. It can only be used internally to build course cache.');
 
546
        $course = $DB->get_record('course', array('id' => $course->id),
 
547
                        array_merge(array('id'), self::$cachedfields), MUST_EXIST);
 
548
        return self::build_course_section_cache($course);
 
549
    }
 
550
 
 
551
    /**
 
552
     * Builds a list of information about sections on a course to be stored in
 
553
     * the course cache. (Does not include information that is already cached
 
554
     * in some other way.)
 
555
     *
 
556
     * @param stdClass $course Course object (must contain fields
 
557
     * @return array Information about sections, indexed by section number (not id)
 
558
     */
 
559
    protected static function build_course_section_cache($course) {
 
560
        global $DB;
384
561
 
385
562
        // Get section data
386
 
        $sections = $DB->get_records('course_sections', array('course' => $courseid), 'section',
 
563
        $sections = $DB->get_records('course_sections', array('course' => $course->id), 'section',
387
564
                'section, id, course, name, summary, summaryformat, sequence, visible, ' .
388
565
                'availablefrom, availableuntil, showavailability, groupingid');
389
566
        $compressedsections = array();
390
567
 
391
 
        $formatoptionsdef = course_get_format($courseid)->section_format_options();
 
568
        $formatoptionsdef = course_get_format($course)->section_format_options();
392
569
        // Remove unnecessary data and add availability
393
570
        foreach ($sections as $number => $section) {
394
571
            // Add cached options from course format to $section object
395
572
            foreach ($formatoptionsdef as $key => $option) {
396
573
                if (!empty($option['cache'])) {
397
 
                    $formatoptions = course_get_format($courseid)->get_format_options($section);
 
574
                    $formatoptions = course_get_format($course)->get_format_options($section);
398
575
                    if (!array_key_exists('cachedefault', $option) || $option['cachedefault'] !== $formatoptions[$key]) {
399
576
                        $section->$key = $formatoptions[$key];
400
577
                    }
407
584
 
408
585
        return $compressedsections;
409
586
    }
 
587
 
 
588
    /**
 
589
     * Builds and stores in MUC object containing information about course
 
590
     * modules and sections together with cached fields from table course.
 
591
     *
 
592
     * @param stdClass $course object from DB table course. Must have property 'id'
 
593
     *     but preferably should have all cached fields.
 
594
     * @return stdClass object with all cached keys of the course plus fields modinfo and sectioncache.
 
595
     *     The same object is stored in MUC
 
596
     * @throws moodle_exception if course is not found (if $course object misses some of the
 
597
     *     necessary fields it is re-requested from database)
 
598
     */
 
599
    public static function build_course_cache($course) {
 
600
        global $DB, $CFG;
 
601
        require_once("$CFG->dirroot/course/lib.php");
 
602
        if (empty($course->id)) {
 
603
            throw new coding_exception('Object $course is missing required property \id\'');
 
604
        }
 
605
        // Ensure object has all necessary fields.
 
606
        foreach (self::$cachedfields as $key) {
 
607
            if (!isset($course->$key)) {
 
608
                $course = $DB->get_record('course', array('id' => $course->id),
 
609
                        implode(',', array_merge(array('id'), self::$cachedfields)), MUST_EXIST);
 
610
                break;
 
611
            }
 
612
        }
 
613
        // Retrieve all information about activities and sections.
 
614
        // This may take time on large courses and it is possible that another user modifies the same course during this process.
 
615
        // Field cacherev stored in both DB and cache will ensure that cached data matches the current course state.
 
616
        $coursemodinfo = new stdClass();
 
617
        $coursemodinfo->modinfo = get_array_of_activities($course->id);
 
618
        $coursemodinfo->sectioncache = self::build_course_section_cache($course);
 
619
        foreach (self::$cachedfields as $key) {
 
620
            $coursemodinfo->$key = $course->$key;
 
621
        }
 
622
        // Set the accumulated activities and sections information in cache, together with cacherev.
 
623
        $cachecoursemodinfo = cache::make('core', 'coursemodinfo');
 
624
        $cachecoursemodinfo->set($course->id, $coursemodinfo);
 
625
        return $coursemodinfo;
 
626
    }
410
627
}
411
628
 
412
629
 
414
631
 * Data about a single module on a course. This contains most of the fields in the course_modules
415
632
 * table, plus additional data when required.
416
633
 *
417
 
 * This object has many public fields; code should treat all these fields as read-only and set
418
 
 * data only using the supplied set functions. Setting the fields directly is not supported
419
 
 * and may cause problems later.
 
634
 * The object can be accessed by core or any plugin (i.e. course format, block, filter, etc.) as
 
635
 * get_fast_modinfo($courseorid)->cms[$coursemoduleid]
 
636
 * or
 
637
 * get_fast_modinfo($courseorid)->instances[$moduletype][$instanceid]
 
638
 *
 
639
 * There are three stages when activity module can add/modify data in this object:
 
640
 *
 
641
 * <b>Stage 1 - during building the cache.</b>
 
642
 * Allows to add to the course cache static user-independent information about the module.
 
643
 * Modules should try to include only absolutely necessary information that may be required
 
644
 * when displaying course view page. The information is stored in application-level cache
 
645
 * and reset when {@link rebuild_course_cache()} is called or cache is purged by admin.
 
646
 *
 
647
 * Modules can implement callback XXX_get_coursemodule_info() returning instance of object
 
648
 * {@link cached_cm_info}
 
649
 *
 
650
 * <b>Stage 2 - dynamic data.</b>
 
651
 * Dynamic data is user-dependend, it is stored in request-level cache. To reset this cache
 
652
 * {@link get_fast_modinfo()} with $reset argument may be called.
 
653
 *
 
654
 * Dynamic data is obtained when any of the following properties/methods is requested:
 
655
 * - {@link cm_info::$url}
 
656
 * - {@link cm_info::$name}
 
657
 * - {@link cm_info::$onclick}
 
658
 * - {@link cm_info::get_icon_url()}
 
659
 * - {@link cm_info::$uservisible}
 
660
 * - {@link cm_info::$available}
 
661
 * - {@link cm_info::$showavailability}
 
662
 * - {@link cm_info::$availableinfo}
 
663
 * - plus any of the properties listed in Stage 3.
 
664
 *
 
665
 * Modules can implement callback <b>XXX_cm_info_dynamic()</b> and inside this callback they
 
666
 * are allowed to use any of the following set methods:
 
667
 * - {@link cm_info::set_available()}
 
668
 * - {@link cm_info::set_name()}
 
669
 * - {@link cm_info::set_no_view_link()}
 
670
 * - {@link cm_info::set_user_visible()}
 
671
 * - {@link cm_info::set_on_click()}
 
672
 * - {@link cm_info::set_icon_url()}
 
673
 * Any methods affecting view elements can also be set in this callback.
 
674
 *
 
675
 * <b>Stage 3 (view data).</b>
 
676
 * Also user-dependend data stored in request-level cache. Second stage is created
 
677
 * because populating the view data can be expensive as it may access much more
 
678
 * Moodle APIs such as filters, user information, output renderers and we
 
679
 * don't want to request it until necessary.
 
680
 * View data is obtained when any of the following properties/methods is requested:
 
681
 * - {@link cm_info::$afterediticons}
 
682
 * - {@link cm_info::$content}
 
683
 * - {@link cm_info::get_formatted_content()}
 
684
 * - {@link cm_info::$extraclasses}
 
685
 * - {@link cm_info::$afterlink}
 
686
 *
 
687
 * Modules can implement callback <b>XXX_cm_info_view()</b> and inside this callback they
 
688
 * are allowed to use any of the following set methods:
 
689
 * - {@link cm_info::set_after_edit_icons()}
 
690
 * - {@link cm_info::set_after_link()}
 
691
 * - {@link cm_info::set_content()}
 
692
 * - {@link cm_info::set_extra_classes()}
 
693
 *
 
694
 * @property-read int $id Course-module ID - from course_modules table
 
695
 * @property-read int $instance Module instance (ID within module table) - from course_modules table
 
696
 * @property-read int $course Course ID - from course_modules table
 
697
 * @property-read string $idnumber 'ID number' from course-modules table (arbitrary text set by user) - from
 
698
 *    course_modules table
 
699
 * @property-read int $added Time that this course-module was added (unix time) - from course_modules table
 
700
 * @property-read int $visible Visible setting (0 or 1; if this is 0, students cannot see/access the activity) - from
 
701
 *    course_modules table
 
702
 * @property-read int $visibleold Old visible setting (if the entire section is hidden, the previous value for
 
703
 *    visible is stored in this field) - from course_modules table
 
704
 * @property-read int $groupmode Group mode (one of the constants NOGROUPS, SEPARATEGROUPS, or VISIBLEGROUPS) - from
 
705
 *    course_modules table. Use {@link cm_info::$effectivegroupmode} to find the actual group mode that may be forced by course.
 
706
 * @property-read int $groupingid Grouping ID (0 = all groupings)
 
707
 * @property-read int $groupmembersonly Group members only (if set to 1, only members of a suitable group see this link on the
 
708
 *    course page; 0 = everyone sees it even if they don't belong to a suitable group) - from
 
709
 *    course_modules table
 
710
 * @property-read bool $coursegroupmodeforce Indicates whether the course containing the module has forced the groupmode
 
711
 *    This means that cm_info::$groupmode should be ignored and cm_info::$coursegroupmode be used instead
 
712
 * @property-read int $coursegroupmode Group mode (one of the constants NOGROUPS, SEPARATEGROUPS, or VISIBLEGROUPS) - from
 
713
 *    course table - as specified for the course containing the module
 
714
 *    Effective only if {@link cm_info::$coursegroupmodeforce} is set
 
715
 * @property-read int $effectivegroupmode Effective group mode for this module (one of the constants NOGROUPS, SEPARATEGROUPS,
 
716
 *    or VISIBLEGROUPS). This can be different from groupmode set for the module if the groupmode is forced for the course.
 
717
 *    This value will always be NOGROUPS if module type does not support group mode.
 
718
 * @property-read int $indent Indent level on course page (0 = no indent) - from course_modules table
 
719
 * @property-read int $completion Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from
 
720
 *    course_modules table
 
721
 * @property-read mixed $completiongradeitemnumber Set to the item number (usually 0) if completion depends on a particular
 
722
 *    grade of this activity, or null if completion does not depend on a grade - from course_modules table
 
723
 * @property-read int $completionview 1 if 'on view' completion is enabled, 0 otherwise - from course_modules table
 
724
 * @property-read int $completionexpected Set to a unix time if completion of this activity is expected at a
 
725
 *    particular time, 0 if no time set - from course_modules table
 
726
 * @property-read int $availablefrom Available date for this activity (0 if not set, or set to seconds since epoch; before this
 
727
 *    date, activity does not display to students) - from course_modules table
 
728
 * @property-read int $availableuntil Available until date for this activity (0 if not set, or set to seconds since epoch; from
 
729
 *    this date, activity does not display to students) - from course_modules table
 
730
 * @property-read int $showavailability When activity is unavailable, this field controls whether it is shown to students (0 =
 
731
 *    hide completely, 1 = show greyed out with information about when it will be available) -
 
732
 *    from course_modules table
 
733
 * @property-read int $showdescription Controls whether the description of the activity displays on the course main page (in
 
734
 *    addition to anywhere it might display within the activity itself). 0 = do not show
 
735
 *    on main page, 1 = show on main page.
 
736
 * @property-read string $extra (deprecated) Extra HTML that is put in an unhelpful part of the HTML when displaying this module in
 
737
 *    course page - from cached data in modinfo field. Deprecated, replaced by ->extraclasses and ->onclick
 
738
 * @property-read string $icon Name of icon to use - from cached data in modinfo field
 
739
 * @property-read string $iconcomponent Component that contains icon - from cached data in modinfo field
 
740
 * @property-read string $modname Name of module e.g. 'forum' (this is the same name as the module's main database
 
741
 *    table) - from cached data in modinfo field
 
742
 * @property-read int $module ID of module type - from course_modules table
 
743
 * @property-read string $name Name of module instance for display on page e.g. 'General discussion forum' - from cached
 
744
 *    data in modinfo field
 
745
 * @property-read int $sectionnum Section number that this course-module is in (section 0 = above the calendar, section 1
 
746
 *    = week/topic 1, etc) - from cached data in modinfo field
 
747
 * @property-read int $section Section id - from course_modules table
 
748
 * @property-read array $conditionscompletion Availability conditions for this course-module based on the completion of other
 
749
 *    course-modules (array from other course-module id to required completion state for that
 
750
 *    module) - from cached data in modinfo field
 
751
 * @property-read array $conditionsgrade Availability conditions for this course-module based on course grades (array from
 
752
 *    grade item id to object with ->min, ->max fields) - from cached data in modinfo field
 
753
 * @property-read array $conditionsfield Availability conditions for this course-module based on user fields
 
754
 * @property-read bool $available True if this course-module is available to students i.e. if all availability conditions
 
755
 *    are met - obtained dynamically
 
756
 * @property-read string $availableinfo If course-module is not available to students, this string gives information about
 
757
 *    availability which can be displayed to students and/or staff (e.g. 'Available from 3
 
758
 *    January 2010') for display on main page - obtained dynamically
 
759
 * @property-read bool $uservisible True if this course-module is available to the CURRENT user (for example, if current user
 
760
 *    has viewhiddenactivities capability, they can access the course-module even if it is not
 
761
 *    visible or not available, so this would be true in that case)
 
762
 * @property-read context_module $context Module context
 
763
 * @property-read string $modfullname Returns a localised human-readable name of the module type - calculated on request
 
764
 * @property-read string $modplural Returns a localised human-readable name of the module type in plural form - calculated on request
 
765
 * @property-read string $content Content to display on main (view) page - calculated on request
 
766
 * @property-read moodle_url $url URL to link to for this module, or null if it doesn't have a view page - calculated on request
 
767
 * @property-read string $extraclasses Extra CSS classes to add to html output for this activity on main page - calculated on request
 
768
 * @property-read string $onclick Content of HTML on-click attribute already escaped - calculated on request
 
769
 * @property-read mixed $customdata Optional custom data stored in modinfo cache for this activity, or null if none
 
770
 * @property-read string $afterlink Extra HTML code to display after link - calculated on request
 
771
 * @property-read string $afterediticons Extra HTML code to display after editing icons (e.g. more icons) - calculated on request
420
772
 */
421
 
class cm_info extends stdClass {
 
773
class cm_info implements IteratorAggregate {
422
774
    /**
423
775
     * State: Only basic data from modinfo cache is available.
424
776
     */
425
777
    const STATE_BASIC = 0;
426
778
 
427
779
    /**
 
780
     * State: In the process of building dynamic data (to avoid recursive calls to obtain_dynamic_data())
 
781
     */
 
782
    const STATE_BUILDING_DYNAMIC = 1;
 
783
 
 
784
    /**
428
785
     * State: Dynamic data is available too.
429
786
     */
430
 
    const STATE_DYNAMIC = 1;
 
787
    const STATE_DYNAMIC = 2;
 
788
 
 
789
    /**
 
790
     * State: In the process of building view data (to avoid recursive calls to obtain_view_data())
 
791
     */
 
792
    const STATE_BUILDING_VIEW = 3;
431
793
 
432
794
    /**
433
795
     * State: View data (for course page) is available.
434
796
     */
435
 
    const STATE_VIEW = 2;
 
797
    const STATE_VIEW = 4;
436
798
 
437
799
    /**
438
800
     * Parent object
446
808
     */
447
809
    private $state;
448
810
 
449
 
    // Existing data fields
450
 
    ///////////////////////
451
 
 
452
811
    /**
453
812
     * Course-module ID - from course_modules table
454
813
     * @var int
455
814
     */
456
 
    public $id;
 
815
    private $id;
457
816
 
458
817
    /**
459
818
     * Module instance (ID within module table) - from course_modules table
460
819
     * @var int
461
820
     */
462
 
    public $instance;
463
 
 
464
 
    /**
465
 
     * Course ID - from course_modules table
466
 
     * @var int
467
 
     */
468
 
    public $course;
 
821
    private $instance;
469
822
 
470
823
    /**
471
824
     * 'ID number' from course-modules table (arbitrary text set by user) - from
472
825
     * course_modules table
473
826
     * @var string
474
827
     */
475
 
    public $idnumber;
 
828
    private $idnumber;
476
829
 
477
830
    /**
478
831
     * Time that this course-module was added (unix time) - from course_modules table
479
832
     * @var int
480
833
     */
481
 
    public $added;
 
834
    private $added;
482
835
 
483
836
    /**
484
837
     * This variable is not used and is included here only so it can be documented.
487
840
     * @var int
488
841
     * @deprecated Do not use this variable
489
842
     */
490
 
    public $score;
 
843
    private $score;
491
844
 
492
845
    /**
493
846
     * Visible setting (0 or 1; if this is 0, students cannot see/access the activity) - from
494
847
     * course_modules table
495
848
     * @var int
496
849
     */
497
 
    public $visible;
 
850
    private $visible;
498
851
 
499
852
    /**
500
853
     * Old visible setting (if the entire section is hidden, the previous value for
501
854
     * visible is stored in this field) - from course_modules table
502
855
     * @var int
503
856
     */
504
 
    public $visibleold;
 
857
    private $visibleold;
505
858
 
506
859
    /**
507
860
     * Group mode (one of the constants NONE, SEPARATEGROUPS, or VISIBLEGROUPS) - from
508
861
     * course_modules table
509
862
     * @var int
510
863
     */
511
 
    public $groupmode;
 
864
    private $groupmode;
512
865
 
513
866
    /**
514
867
     * Grouping ID (0 = all groupings)
515
868
     * @var int
516
869
     */
517
 
    public $groupingid;
 
870
    private $groupingid;
518
871
 
519
872
    /**
520
873
     * Group members only (if set to 1, only members of a suitable group see this link on the
522
875
     * course_modules table
523
876
     * @var int
524
877
     */
525
 
    public $groupmembersonly;
526
 
 
527
 
    /**
528
 
     * Indicates whether the course containing the module has forced the groupmode
529
 
     * This means that cm_info::$groupmode should be ignored and cm_info::$coursegroupmode be
530
 
     * used instead
531
 
     * @var bool
532
 
     */
533
 
    public $coursegroupmodeforce;
534
 
 
535
 
    /**
536
 
     * Group mode (one of the constants NONE, SEPARATEGROUPS, or VISIBLEGROUPS) - from
537
 
     * course table - as specified for the course containing the module
538
 
     * Effective only if cm_info::$coursegroupmodeforce is set
539
 
     * @var int
540
 
     */
541
 
    public $coursegroupmode;
 
878
    private $groupmembersonly;
542
879
 
543
880
    /**
544
881
     * Indent level on course page (0 = no indent) - from course_modules table
545
882
     * @var int
546
883
     */
547
 
    public $indent;
 
884
    private $indent;
548
885
 
549
886
    /**
550
887
     * Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from
551
888
     * course_modules table
552
889
     * @var int
553
890
     */
554
 
    public $completion;
 
891
    private $completion;
555
892
 
556
893
    /**
557
894
     * Set to the item number (usually 0) if completion depends on a particular
559
896
     * course_modules table
560
897
     * @var mixed
561
898
     */
562
 
    public $completiongradeitemnumber;
 
899
    private $completiongradeitemnumber;
563
900
 
564
901
    /**
565
902
     * 1 if 'on view' completion is enabled, 0 otherwise - from course_modules table
566
903
     * @var int
567
904
     */
568
 
    public $completionview;
 
905
    private $completionview;
569
906
 
570
907
    /**
571
908
     * Set to a unix time if completion of this activity is expected at a
572
909
     * particular time, 0 if no time set - from course_modules table
573
910
     * @var int
574
911
     */
575
 
    public $completionexpected;
 
912
    private $completionexpected;
576
913
 
577
914
    /**
578
915
     * Available date for this activity (0 if not set, or set to seconds since epoch; before this
579
916
     * date, activity does not display to students) - from course_modules table
580
917
     * @var int
581
918
     */
582
 
    public $availablefrom;
 
919
    private $availablefrom;
583
920
 
584
921
    /**
585
922
     * Available until date for this activity (0 if not set, or set to seconds since epoch; from
586
923
     * this date, activity does not display to students) - from course_modules table
587
924
     * @var int
588
925
     */
589
 
    public $availableuntil;
 
926
    private $availableuntil;
590
927
 
591
928
    /**
592
929
     * When activity is unavailable, this field controls whether it is shown to students (0 =
594
931
     * from course_modules table
595
932
     * @var int
596
933
     */
597
 
    public $showavailability;
 
934
    private $showavailability;
598
935
 
599
936
    /**
600
937
     * Controls whether the description of the activity displays on the course main page (in
602
939
     * on main page, 1 = show on main page.
603
940
     * @var int
604
941
     */
605
 
    public $showdescription;
 
942
    private $showdescription;
606
943
 
607
944
    /**
608
945
     * Extra HTML that is put in an unhelpful part of the HTML when displaying this module in
610
947
     * @deprecated This is crazy, don't use it. Replaced by ->extraclasses and ->onclick
611
948
     * @var string
612
949
     */
613
 
    public $extra;
 
950
    private $extra;
614
951
 
615
952
    /**
616
953
     * Name of icon to use - from cached data in modinfo field
617
954
     * @var string
618
955
     */
619
 
    public $icon;
 
956
    private $icon;
620
957
 
621
958
    /**
622
959
     * Component that contains icon - from cached data in modinfo field
623
960
     * @var string
624
961
     */
625
 
    public $iconcomponent;
 
962
    private $iconcomponent;
626
963
 
627
964
    /**
628
965
     * Name of module e.g. 'forum' (this is the same name as the module's main database
629
966
     * table) - from cached data in modinfo field
630
967
     * @var string
631
968
     */
632
 
    public $modname;
 
969
    private $modname;
633
970
 
634
971
    /**
635
972
     * ID of module - from course_modules table
636
973
     * @var int
637
974
     */
638
 
    public $module;
 
975
    private $module;
639
976
 
640
977
    /**
641
978
     * Name of module instance for display on page e.g. 'General discussion forum' - from cached
642
979
     * data in modinfo field
643
980
     * @var string
644
981
     */
645
 
    public $name;
 
982
    private $name;
646
983
 
647
984
    /**
648
985
     * Section number that this course-module is in (section 0 = above the calendar, section 1
649
986
     * = week/topic 1, etc) - from cached data in modinfo field
650
 
     * @var string
 
987
     * @var int
651
988
     */
652
 
    public $sectionnum;
 
989
    private $sectionnum;
653
990
 
654
991
    /**
655
992
     * Section id - from course_modules table
656
993
     * @var int
657
994
     */
658
 
    public $section;
 
995
    private $section;
659
996
 
660
997
    /**
661
998
     * Availability conditions for this course-module based on the completion of other
663
1000
     * module) - from cached data in modinfo field
664
1001
     * @var array
665
1002
     */
666
 
    public $conditionscompletion;
 
1003
    private $conditionscompletion;
667
1004
 
668
1005
    /**
669
1006
     * Availability conditions for this course-module based on course grades (array from
670
1007
     * grade item id to object with ->min, ->max fields) - from cached data in modinfo field
671
1008
     * @var array
672
1009
     */
673
 
    public $conditionsgrade;
 
1010
    private $conditionsgrade;
674
1011
 
675
1012
    /**
676
1013
     * Availability conditions for this course-module based on user fields
677
1014
     * @var array
678
1015
     */
679
 
    public $conditionsfield;
 
1016
    private $conditionsfield;
680
1017
 
681
1018
    /**
682
1019
     * True if this course-module is available to students i.e. if all availability conditions
683
1020
     * are met - obtained dynamically
684
1021
     * @var bool
685
1022
     */
686
 
    public $available;
 
1023
    private $available;
687
1024
 
688
1025
    /**
689
1026
     * If course-module is not available to students, this string gives information about
691
1028
     * January 2010') for display on main page - obtained dynamically
692
1029
     * @var string
693
1030
     */
694
 
    public $availableinfo;
 
1031
    private $availableinfo;
695
1032
 
696
1033
    /**
697
1034
     * True if this course-module is available to the CURRENT user (for example, if current user
699
1036
     * visible or not available, so this would be true in that case)
700
1037
     * @var bool
701
1038
     */
702
 
    public $uservisible;
703
 
 
704
 
    /**
705
 
     * Module context - hacky shortcut
706
 
     * @deprecated
707
 
     * @var stdClass
708
 
     */
709
 
    public $context;
710
 
 
711
 
 
712
 
    // New data available only via functions
713
 
    ////////////////////////////////////////
 
1039
    private $uservisible;
714
1040
 
715
1041
    /**
716
1042
     * @var moodle_url
753
1079
    private $afterediticons;
754
1080
 
755
1081
    /**
 
1082
     * List of class read-only properties and their getter methods.
 
1083
     * Used by magic functions __get(), __isset(), __empty()
 
1084
     * @var array
 
1085
     */
 
1086
    private static $standardproperties = array(
 
1087
        'url' => 'get_url',
 
1088
        'content' => 'get_content',
 
1089
        'extraclasses' => 'get_extra_classes',
 
1090
        'onclick' => 'get_on_click',
 
1091
        'customdata' => 'get_custom_data',
 
1092
        'afterlink' => 'get_after_link',
 
1093
        'afterediticons' => 'get_after_edit_icons',
 
1094
        'modfullname' => 'get_module_type_name',
 
1095
        'modplural' => 'get_module_type_name_plural',
 
1096
        'id' => false,
 
1097
        'added' => false,
 
1098
        'available' => 'get_available',
 
1099
        'availablefrom' => false,
 
1100
        'availableinfo' => 'get_available_info',
 
1101
        'availableuntil' => false,
 
1102
        'completion' => false,
 
1103
        'completionexpected' => false,
 
1104
        'completiongradeitemnumber' => false,
 
1105
        'completionview' => false,
 
1106
        'conditionscompletion' => false,
 
1107
        'conditionsfield' => false,
 
1108
        'conditionsgrade' => false,
 
1109
        'context' => 'get_context',
 
1110
        'course' => 'get_course_id',
 
1111
        'coursegroupmode' => 'get_course_groupmode',
 
1112
        'coursegroupmodeforce' => 'get_course_groupmodeforce',
 
1113
        'effectivegroupmode' => 'get_effective_groupmode',
 
1114
        'extra' => false,
 
1115
        'groupingid' => false,
 
1116
        'groupmembersonly' => false,
 
1117
        'groupmode' => false,
 
1118
        'icon' => false,
 
1119
        'iconcomponent' => false,
 
1120
        'idnumber' => false,
 
1121
        'indent' => false,
 
1122
        'instance' => false,
 
1123
        'modname' => false,
 
1124
        'module' => false,
 
1125
        'name' => 'get_name',
 
1126
        'score' => false,
 
1127
        'section' => false,
 
1128
        'sectionnum' => false,
 
1129
        'showavailability' => 'get_show_availability',
 
1130
        'showdescription' => false,
 
1131
        'uservisible' => 'get_user_visible',
 
1132
        'visible' => false,
 
1133
        'visibleold' => false,
 
1134
    );
 
1135
 
 
1136
    /**
 
1137
     * List of methods with no arguments that were public prior to Moodle 2.6.
 
1138
     *
 
1139
     * They can still be accessed publicly via magic __call() function with no warnings
 
1140
     * but are not listed in the class methods list.
 
1141
     * For the consistency of the code it is better to use corresponding properties.
 
1142
     *
 
1143
     * These methods be deprecated completely in later versions.
 
1144
     *
 
1145
     * @var array $standardmethods
 
1146
     */
 
1147
    private static $standardmethods = array(
 
1148
        // Following methods are not recommended to use because there have associated read-only properties.
 
1149
        'get_url',
 
1150
        'get_content',
 
1151
        'get_extra_classes',
 
1152
        'get_on_click',
 
1153
        'get_custom_data',
 
1154
        'get_after_link',
 
1155
        'get_after_edit_icons',
 
1156
        // Method obtain_dynamic_data() should not be called from outside of this class but it was public before Moodle 2.6.
 
1157
        'obtain_dynamic_data',
 
1158
    );
 
1159
 
 
1160
    /**
 
1161
     * Magic method to call functions that are now declared as private now but
 
1162
     * were public in Moodle before 2.6. Developers can access them without
 
1163
     * any warnings but they are not listed in the class methods list.
 
1164
     *
 
1165
     * @param string $name
 
1166
     * @param array $arguments
 
1167
     * @return mixed
 
1168
     */
 
1169
    public function __call($name, $arguments) {
 
1170
        if (in_array($name, self::$standardmethods)) {
 
1171
            // All standard methods do not have arguments anyway.
 
1172
            return $this->$name();
 
1173
        }
 
1174
        throw new coding_exception("Method cm_info::{$name}() does not exist");
 
1175
    }
 
1176
 
 
1177
    /**
756
1178
     * Magic method getter
757
1179
     *
758
1180
     * @param string $name
759
1181
     * @return mixed
760
1182
     */
761
1183
    public function __get($name) {
762
 
        switch ($name) {
763
 
            case 'modplural':
764
 
                return $this->get_module_type_name(true);
765
 
            case 'modfullname':
766
 
                return $this->get_module_type_name();
767
 
            default:
768
 
                debugging('Invalid cm_info property accessed: '.$name);
769
 
                return null;
770
 
        }
 
1184
        if (isset(self::$standardproperties[$name])) {
 
1185
            if ($method = self::$standardproperties[$name]) {
 
1186
                return $this->$method();
 
1187
            } else {
 
1188
                return $this->$name;
 
1189
            }
 
1190
        } else {
 
1191
            debugging('Invalid cm_info property accessed: '.$name);
 
1192
            return null;
 
1193
        }
 
1194
    }
 
1195
 
 
1196
    /**
 
1197
     * Implementation of IteratorAggregate::getIterator(), allows to cycle through properties
 
1198
     * and use {@link convert_to_array()}
 
1199
     *
 
1200
     * @return ArrayIterator
 
1201
     */
 
1202
    public function getIterator() {
 
1203
        // Make sure dynamic properties are retrieved prior to view properties.
 
1204
        $this->obtain_dynamic_data();
 
1205
        $ret = array();
 
1206
        foreach (self::$standardproperties as $key => $unused) {
 
1207
            $ret[$key] = $this->__get($key);
 
1208
        }
 
1209
        return new ArrayIterator($ret);
 
1210
    }
 
1211
 
 
1212
    /**
 
1213
     * Magic method for function isset()
 
1214
     *
 
1215
     * @param string $name
 
1216
     * @return bool
 
1217
     */
 
1218
    public function __isset($name) {
 
1219
        if (isset(self::$standardproperties[$name])) {
 
1220
            $value = $this->__get($name);
 
1221
            return isset($value);
 
1222
        }
 
1223
        return false;
 
1224
    }
 
1225
 
 
1226
    /**
 
1227
     * Magic method for function empty()
 
1228
     *
 
1229
     * @param string $name
 
1230
     * @return bool
 
1231
     */
 
1232
    public function __empty($name) {
 
1233
        if (isset(self::$standardproperties[$name])) {
 
1234
            $value = $this->__get($name);
 
1235
            return empty($value);
 
1236
        }
 
1237
        return true;
 
1238
    }
 
1239
 
 
1240
    /**
 
1241
     * Magic method setter
 
1242
     *
 
1243
     * Will display the developer warning when trying to set/overwrite property.
 
1244
     *
 
1245
     * @param string $name
 
1246
     * @param mixed $value
 
1247
     */
 
1248
    public function __set($name, $value) {
 
1249
        debugging("It is not allowed to set the property cm_info::\${$name}", DEBUG_DEVELOPER);
771
1250
    }
772
1251
 
773
1252
    /**
782
1261
    /**
783
1262
     * @return moodle_url URL to link to for this module, or null if it doesn't have a view page
784
1263
     */
785
 
    public function get_url() {
 
1264
    private function get_url() {
 
1265
        $this->obtain_dynamic_data();
786
1266
        return $this->url;
787
1267
    }
788
1268
 
791
1271
     * Note: Will collect view data, if not already obtained.
792
1272
     * @return string Content to display on main page below link, or empty string if none
793
1273
     */
794
 
    public function get_content() {
 
1274
    private function get_content() {
795
1275
        $this->obtain_view_data();
796
1276
        return $this->content;
797
1277
    }
816
1296
 
817
1297
        $options = (array)$options;
818
1298
        if (!isset($options['context'])) {
819
 
            $options['context'] = context_module::instance($this->id);
 
1299
            $options['context'] = $this->get_context();
820
1300
        }
821
1301
        return format_text($this->content, FORMAT_HTML, $options);
822
1302
    }
823
1303
 
824
1304
    /**
 
1305
     * Getter method for property $name, ensures that dynamic data is obtained.
 
1306
     * @return string
 
1307
     */
 
1308
    private function get_name() {
 
1309
        $this->obtain_dynamic_data();
 
1310
        return $this->name;
 
1311
    }
 
1312
 
 
1313
    /**
825
1314
     * Returns the name to display on course/overview page, formatted and passed through filters
826
1315
     *
827
1316
     * if $options['context'] is not specified, the module context is used
832
1321
    public function get_formatted_name($options = array()) {
833
1322
        $options = (array)$options;
834
1323
        if (!isset($options['context'])) {
835
 
            $options['context'] = context_module::instance($this->id);
 
1324
            $options['context'] = $this->get_context();
836
1325
        }
837
 
        return format_string($this->name, true,  $options);
 
1326
        return format_string($this->get_name(), true,  $options);
838
1327
    }
839
1328
 
840
1329
    /**
841
1330
     * Note: Will collect view data, if not already obtained.
842
1331
     * @return string Extra CSS classes to add to html output for this activity on main page
843
1332
     */
844
 
    public function get_extra_classes() {
 
1333
    private function get_extra_classes() {
845
1334
        $this->obtain_view_data();
846
1335
        return $this->extraclasses;
847
1336
    }
850
1339
     * @return string Content of HTML on-click attribute. This string will be used literally
851
1340
     * as a string so should be pre-escaped.
852
1341
     */
853
 
    public function get_on_click() {
 
1342
    private function get_on_click() {
854
1343
        // Does not need view data; may be used by navigation
 
1344
        $this->obtain_dynamic_data();
855
1345
        return $this->onclick;
856
1346
    }
857
1347
    /**
858
1348
     * @return mixed Optional custom data stored in modinfo cache for this activity, or null if none
859
1349
     */
860
 
    public function get_custom_data() {
 
1350
    private function get_custom_data() {
861
1351
        return $this->customdata;
862
1352
    }
863
1353
 
865
1355
     * Note: Will collect view data, if not already obtained.
866
1356
     * @return string Extra HTML code to display after link
867
1357
     */
868
 
    public function get_after_link() {
 
1358
    private function get_after_link() {
869
1359
        $this->obtain_view_data();
870
1360
        return $this->afterlink;
871
1361
    }
874
1364
     * Note: Will collect view data, if not already obtained.
875
1365
     * @return string Extra HTML code to display after editing icons (e.g. more icons)
876
1366
     */
877
 
    public function get_after_edit_icons() {
 
1367
    private function get_after_edit_icons() {
878
1368
        $this->obtain_view_data();
879
1369
        return $this->afterediticons;
880
1370
    }
885
1375
     */
886
1376
    public function get_icon_url($output = null) {
887
1377
        global $OUTPUT;
 
1378
        $this->obtain_dynamic_data();
888
1379
        if (!$output) {
889
1380
            $output = $OUTPUT;
890
1381
        }
928
1419
    }
929
1420
 
930
1421
    /**
 
1422
     * Returns a localised human-readable name of the module type in plural form - calculated on request
 
1423
     *
 
1424
     * @return string
 
1425
     */
 
1426
    private function get_module_type_name_plural() {
 
1427
        return $this->get_module_type_name(true);
 
1428
    }
 
1429
 
 
1430
    /**
931
1431
     * @return course_modinfo Modinfo object that this came from
932
1432
     */
933
1433
    public function get_modinfo() {
935
1435
    }
936
1436
 
937
1437
    /**
938
 
     * @return object Moodle course object that was used to construct this data
 
1438
     * Returns course object that was used in the first {@link get_fast_modinfo()} call.
 
1439
     *
 
1440
     * It may not contain all fields from DB table {course} but always has at least the following:
 
1441
     * id,shortname,fullname,format,enablecompletion,groupmode,groupmodeforce,cacherev
 
1442
     *
 
1443
     * If the course object lacks the field you need you can use the global
 
1444
     * function {@link get_course()} that will save extra query if you access
 
1445
     * current course or frontpage course.
 
1446
     *
 
1447
     * @return stdClass
939
1448
     */
940
1449
    public function get_course() {
941
1450
        return $this->modinfo->get_course();
942
1451
    }
943
1452
 
 
1453
    /**
 
1454
     * Returns course id for which the modinfo was generated.
 
1455
     *
 
1456
     * @return int
 
1457
     */
 
1458
    private function get_course_id() {
 
1459
        return $this->modinfo->get_course_id();
 
1460
    }
 
1461
 
 
1462
    /**
 
1463
     * Returns group mode used for the course containing the module
 
1464
     *
 
1465
     * @return int one of constants NOGROUPS, SEPARATEGROUPS, VISIBLEGROUPS
 
1466
     */
 
1467
    private function get_course_groupmode() {
 
1468
        return $this->modinfo->get_course()->groupmode;
 
1469
    }
 
1470
 
 
1471
    /**
 
1472
     * Returns whether group mode is forced for the course containing the module
 
1473
     *
 
1474
     * @return bool
 
1475
     */
 
1476
    private function get_course_groupmodeforce() {
 
1477
        return $this->modinfo->get_course()->groupmodeforce;
 
1478
    }
 
1479
 
 
1480
    /**
 
1481
     * Returns effective groupmode of the module that may be overwritten by forced course groupmode.
 
1482
     *
 
1483
     * @return int one of constants NOGROUPS, SEPARATEGROUPS, VISIBLEGROUPS
 
1484
     */
 
1485
    private function get_effective_groupmode() {
 
1486
        $groupmode = $this->groupmode;
 
1487
        if ($this->modinfo->get_course()->groupmodeforce) {
 
1488
            $groupmode = $this->modinfo->get_course()->groupmode;
 
1489
            if ($groupmode != NOGROUPS && !plugin_supports('mod', $this->modname, FEATURE_GROUPS, 0)) {
 
1490
                $groupmode = NOGROUPS;
 
1491
            }
 
1492
        }
 
1493
        return $groupmode;
 
1494
    }
 
1495
 
 
1496
    /**
 
1497
     * @return context_module Current module context
 
1498
     */
 
1499
    private function get_context() {
 
1500
        return context_module::instance($this->id);
 
1501
    }
 
1502
 
944
1503
    // Set functions
945
1504
    ////////////////
946
1505
 
1011
1570
     * @return void
1012
1571
     */
1013
1572
    public function set_name($name) {
1014
 
        $this->update_user_visible();
 
1573
        if ($this->state < self::STATE_BUILDING_DYNAMIC) {
 
1574
            $this->update_user_visible();
 
1575
        }
1015
1576
        $this->name = $name;
1016
1577
    }
1017
1578
 
1077
1638
    }
1078
1639
 
1079
1640
    /**
1080
 
     * Constructor should not be called directly; use get_fast_modinfo.
 
1641
     * Constructor should not be called directly; use {@link get_fast_modinfo()}
 
1642
     *
1081
1643
     * @param course_modinfo $modinfo Parent object
1082
 
     * @param object $course Course row
1083
 
     * @param object $mod Module object from the modinfo field of course table
1084
 
     * @param object $info Entire object from modinfo field of course table
 
1644
     * @param stdClass $notused1 Argument not used
 
1645
     * @param stdClass $mod Module object from the modinfo field of course table
 
1646
     * @param stdClass $notused2 Argument not used
1085
1647
     */
1086
 
    public function __construct(course_modinfo $modinfo, $course, $mod, $info) {
1087
 
        global $CFG;
 
1648
    public function __construct(course_modinfo $modinfo, $notused1, $mod, $notused2) {
1088
1649
        $this->modinfo = $modinfo;
1089
1650
 
1090
1651
        $this->id               = $mod->cm;
1091
1652
        $this->instance         = $mod->id;
1092
 
        $this->course           = $course->id;
1093
1653
        $this->modname          = $mod->mod;
1094
1654
        $this->idnumber         = isset($mod->idnumber) ? $mod->idnumber : '';
1095
1655
        $this->name             = $mod->name;
1098
1658
        $this->groupmode        = isset($mod->groupmode) ? $mod->groupmode : 0;
1099
1659
        $this->groupingid       = isset($mod->groupingid) ? $mod->groupingid : 0;
1100
1660
        $this->groupmembersonly = isset($mod->groupmembersonly) ? $mod->groupmembersonly : 0;
1101
 
        $this->coursegroupmodeforce = $course->groupmodeforce;
1102
 
        $this->coursegroupmode  = $course->groupmode;
1103
1661
        $this->indent           = isset($mod->indent) ? $mod->indent : 0;
1104
1662
        $this->extra            = isset($mod->extra) ? $mod->extra : '';
1105
1663
        $this->extraclasses     = isset($mod->extraclasses) ? $mod->extraclasses : '';
1110
1668
        $this->icon             = isset($mod->icon) ? $mod->icon : '';
1111
1669
        $this->iconcomponent    = isset($mod->iconcomponent) ? $mod->iconcomponent : '';
1112
1670
        $this->customdata       = isset($mod->customdata) ? $mod->customdata : '';
1113
 
        $this->context          = context_module::instance($mod->cm);
1114
1671
        $this->showdescription  = isset($mod->showdescription) ? $mod->showdescription : 0;
1115
1672
        $this->state = self::STATE_BASIC;
1116
1673
 
1117
 
        // Note: These fields from $cm were not present in cm_info in Moodle
1118
 
        // 2.0.2 and prior. They may not be available if course cache hasn't
1119
 
        // been rebuilt since then.
1120
1674
        $this->section = isset($mod->sectionid) ? $mod->sectionid : 0;
1121
1675
        $this->module = isset($mod->module) ? $mod->module : 0;
1122
1676
        $this->added = isset($mod->added) ? $mod->added : 0;
1143
1697
        $this->conditionsfield = isset($mod->conditionsfield)
1144
1698
                ? $mod->conditionsfield : array();
1145
1699
 
1146
 
        static $modviews;
 
1700
        static $modviews = array();
1147
1701
        if (!isset($modviews[$this->modname])) {
1148
1702
            $modviews[$this->modname] = !plugin_supports('mod', $this->modname,
1149
1703
                    FEATURE_NO_VIEW_LINK);
1156
1710
    /**
1157
1711
     * If dynamic data for this course-module is not yet available, gets it.
1158
1712
     *
1159
 
     * This function is automatically called when constructing course_modinfo, so users don't
1160
 
     * need to call it.
 
1713
     * This function is automatically called when requesting any course_modinfo property
 
1714
     * that can be modified by modules (have a set_xxx method).
1161
1715
     *
1162
1716
     * Dynamic data is data which does not come directly from the cache but is calculated at
1163
1717
     * runtime based on the current user. Primarily this concerns whether the user can access
1167
1721
     * be called (if it exists).
1168
1722
     * @return void
1169
1723
     */
1170
 
    public function obtain_dynamic_data() {
 
1724
    private function obtain_dynamic_data() {
1171
1725
        global $CFG;
1172
 
        if ($this->state >= self::STATE_DYNAMIC) {
 
1726
        $userid = $this->modinfo->get_user_id();
 
1727
        if ($this->state >= self::STATE_BUILDING_DYNAMIC || $userid == -1) {
1173
1728
            return;
1174
1729
        }
1175
 
        $userid = $this->modinfo->get_user_id();
 
1730
        $this->state = self::STATE_BUILDING_DYNAMIC;
1176
1731
 
1177
1732
        if (!empty($CFG->enableavailability)) {
 
1733
            require_once($CFG->libdir. '/conditionlib.php');
1178
1734
            // Get availability information
1179
1735
            $ci = new condition_info($this);
1180
1736
            // Note that the modinfo currently available only includes minimal details (basic data)
1181
 
            // so passing it to this function is a bit dangerous as it would cause infinite
1182
 
            // recursion if it tried to get dynamic data, however we know that this function only
1183
 
            // uses basic data.
 
1737
            // but we know that this function does not need anything more than basic data.
1184
1738
            $this->available = $ci->is_available($this->availableinfo, true,
1185
1739
                    $userid, $this->modinfo);
1186
1740
 
1205
1759
    }
1206
1760
 
1207
1761
    /**
 
1762
     * Getter method for property $uservisible, ensures that dynamic data is retrieved.
 
1763
     * @return bool
 
1764
     */
 
1765
    private function get_user_visible() {
 
1766
        $this->obtain_dynamic_data();
 
1767
        return $this->uservisible;
 
1768
    }
 
1769
 
 
1770
    /**
 
1771
     * Getter method for property $available, ensures that dynamic data is retrieved
 
1772
     * @return bool
 
1773
     */
 
1774
    private function get_available() {
 
1775
        $this->obtain_dynamic_data();
 
1776
        return $this->available;
 
1777
    }
 
1778
 
 
1779
    /**
 
1780
     * Getter method for property $showavailability, ensures that dynamic data is retrieved
 
1781
     * @return int
 
1782
     */
 
1783
    private function get_show_availability() {
 
1784
        $this->obtain_dynamic_data();
 
1785
        return $this->showavailability;
 
1786
    }
 
1787
 
 
1788
    /**
 
1789
     * Getter method for property $availableinfo, ensures that dynamic data is retrieved
 
1790
     * @return type
 
1791
     */
 
1792
    private function get_available_info() {
 
1793
        $this->obtain_dynamic_data();
 
1794
        return $this->availableinfo;
 
1795
    }
 
1796
 
 
1797
    /**
1208
1798
     * Works out whether activity is available to the current user
1209
1799
     *
1210
1800
     * If the activity is unavailable, additional checks are required to determine if its hidden or greyed out
1214
1804
     * @return void
1215
1805
     */
1216
1806
    private function update_user_visible() {
1217
 
        global $CFG;
1218
 
        $modcontext = context_module::instance($this->id);
1219
1807
        $userid = $this->modinfo->get_user_id();
 
1808
        if ($userid == -1) {
 
1809
            return null;
 
1810
        }
1220
1811
        $this->uservisible = true;
1221
1812
 
1222
1813
        // If the user cannot access the activity set the uservisible flag to false.
1223
1814
        // Additional checks are required to determine whether the activity is entirely hidden or just greyed out.
1224
 
        if ((!$this->visible or !$this->available) and
1225
 
                !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
 
1815
        if ((!$this->visible or !$this->get_available()) and
 
1816
                !has_capability('moodle/course:viewhiddenactivities', $this->get_context(), $userid)) {
1226
1817
 
1227
1818
            $this->uservisible = false;
1228
1819
        }
1246
1837
        global $CFG;
1247
1838
 
1248
1839
        if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly)) {
1249
 
            $modcontext = context_module::instance($this->id);
1250
1840
            $userid = $this->modinfo->get_user_id();
1251
 
            if (!has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
 
1841
            if ($userid == -1) {
 
1842
                return null;
 
1843
            }
 
1844
            if (!has_capability('moodle/site:accessallgroups', $this->get_context(), $userid)) {
1252
1845
                // If the activity has 'group members only' and you don't have accessallgroups...
1253
1846
                $groups = $this->modinfo->get_groups($this->groupingid);
1254
1847
                if (empty($groups)) {
1266
1859
     * @return bool True if the user access is restricted.
1267
1860
     */
1268
1861
    public function is_user_access_restricted_by_capability() {
 
1862
        $userid = $this->modinfo->get_user_id();
 
1863
        if ($userid == -1) {
 
1864
            return null;
 
1865
        }
1269
1866
        $capability = 'mod/' . $this->modname . ':view';
1270
1867
        $capabilityinfo = get_capability_info($capability);
1271
1868
        if (!$capabilityinfo) {
1274
1871
        }
1275
1872
 
1276
1873
        // You are blocked if you don't have the capability.
1277
 
        $userid = $this->modinfo->get_user_id();
1278
 
        return !has_capability($capability, context_module::instance($this->id), $userid);
 
1874
        return !has_capability($capability, $this->get_context(), $userid);
1279
1875
    }
1280
1876
 
1281
1877
    /**
1290
1886
            return false;
1291
1887
        }
1292
1888
 
 
1889
        $userid = $this->modinfo->get_user_id();
 
1890
        if ($userid == -1) {
 
1891
            return null;
 
1892
        }
 
1893
 
1293
1894
        // If module will always be visible anyway (but greyed out), don't bother checking anything else
1294
 
        if ($this->showavailability == CONDITION_STUDENTVIEW_SHOW) {
 
1895
        if ($this->get_show_availability() == CONDITION_STUDENTVIEW_SHOW) {
1295
1896
            return false;
1296
1897
        }
1297
1898
 
1298
1899
        // Can the user see hidden modules?
1299
 
        $modcontext = context_module::instance($this->id);
1300
 
        $userid = $this->modinfo->get_user_id();
1301
 
        if (has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
 
1900
        if (has_capability('moodle/course:viewhiddenactivities', $this->get_context(), $userid)) {
1302
1901
            return false;
1303
1902
        }
1304
1903
 
1305
1904
        // Is the module hidden due to unmet conditions?
1306
 
        if (!$this->available) {
 
1905
        if (!$this->get_available()) {
1307
1906
            return true;
1308
1907
        }
1309
1908
 
1348
1947
     * @return void
1349
1948
     */
1350
1949
    private function obtain_view_data() {
1351
 
        if ($this->state >= self::STATE_VIEW) {
 
1950
        if ($this->state >= self::STATE_BUILDING_VIEW || $this->modinfo->get_user_id() == -1) {
1352
1951
            return;
1353
1952
        }
 
1953
        $this->obtain_dynamic_data();
 
1954
        $this->state = self::STATE_BUILDING_VIEW;
1354
1955
 
1355
1956
        // Let module make changes at this point
1356
1957
        $this->call_mod_function('cm_info_view');
1366
1967
 * use get_fast_modinfo($courseid, 0, true) to reset the static cache for particular course
1367
1968
 * use get_fast_modinfo(0, 0, true) to reset the static cache for all courses
1368
1969
 *
1369
 
 * @uses MAX_MODINFO_CACHE_SIZE
1370
 
 * @param int|stdClass $courseorid object from DB table 'course' or just a course id
1371
 
 * @param int $userid User id to populate 'uservisible' attributes of modules and sections.
1372
 
 *     Set to 0 for current user (default)
 
1970
 * use rebuild_course_cache($courseid, true) to reset the application AND static cache
 
1971
 * for particular course when it's contents has changed
 
1972
 *
 
1973
 * @param int|stdClass $courseorid object from DB table 'course' (must have field 'id'
 
1974
 *     and recommended to have field 'cacherev') or just a course id. Just course id
 
1975
 *     is enough when calling get_fast_modinfo() for current course or site or when
 
1976
 *     calling for any other course for the second time.
 
1977
 * @param int $userid User id to populate 'availble' and 'uservisible' attributes of modules and sections.
 
1978
 *     Set to 0 for current user (default). Set to -1 to avoid calculation of dynamic user-depended data.
1373
1979
 * @param bool $resetonly whether we want to get modinfo or just reset the cache
1374
1980
 * @return course_modinfo|null Module information for course, or null if resetting
 
1981
 * @throws moodle_exception when course is not found (nothing is thrown if resetting)
1375
1982
 */
1376
1983
function get_fast_modinfo($courseorid, $userid = 0, $resetonly = false) {
1377
 
    global $CFG, $USER;
1378
 
 
1379
 
    static $cache = array();
1380
 
 
1381
1984
    // compartibility with syntax prior to 2.4:
1382
1985
    if ($courseorid === 'reset') {
1383
1986
        debugging("Using the string 'reset' as the first argument of get_fast_modinfo() is deprecated. Use get_fast_modinfo(0,0,true) instead.", DEBUG_DEVELOPER);
1390
1993
        upgrade_ensure_not_running();
1391
1994
    }
1392
1995
 
1393
 
    if (is_object($courseorid)) {
1394
 
        $course = $courseorid;
1395
 
    } else {
1396
 
        $course = (object)array('id' => $courseorid);
1397
 
    }
1398
 
 
1399
1996
    // Function is called with $reset = true
1400
1997
    if ($resetonly) {
1401
 
        if (isset($course->id) && $course->id > 0) {
1402
 
            $cache[$course->id] = false;
1403
 
        } else {
1404
 
            foreach (array_keys($cache) as $key) {
1405
 
                $cache[$key] = false;
1406
 
            }
1407
 
        }
 
1998
        course_modinfo::clear_instance_cache($courseorid);
1408
1999
        return null;
1409
2000
    }
1410
2001
 
1411
2002
    // Function is called with $reset = false, retrieve modinfo
1412
 
    if (empty($userid)) {
1413
 
        $userid = $USER->id;
1414
 
    }
1415
 
 
1416
 
    if (array_key_exists($course->id, $cache)) {
1417
 
        if ($cache[$course->id] === false) {
1418
 
            // this course has been recently reset, do not rely on modinfo and sectioncache in $course
1419
 
            $course->modinfo = null;
1420
 
            $course->sectioncache = null;
1421
 
        } else if ($cache[$course->id]->userid == $userid) {
1422
 
            // this course's modinfo for the same user was recently retrieved, return cached
1423
 
            return $cache[$course->id];
1424
 
        }
1425
 
    }
1426
 
 
1427
 
    unset($cache[$course->id]); // prevent potential reference problems when switching users
1428
 
 
1429
 
    $cache[$course->id] = new course_modinfo($course, $userid);
1430
 
 
1431
 
    // Ensure cache does not use too much RAM
1432
 
    if (count($cache) > MAX_MODINFO_CACHE_SIZE) {
1433
 
        reset($cache);
1434
 
        $key = key($cache);
1435
 
        // Unsetting static variable in PHP is percular, it removes the reference,
1436
 
        // but data remain in memory. Prior to unsetting, the varable needs to be
1437
 
        // set to empty to remove its remains from memory.
1438
 
        $cache[$key] = '';
1439
 
        unset($cache[$key]);
1440
 
    }
1441
 
 
1442
 
    return $cache[$course->id];
 
2003
    return course_modinfo::instance($courseorid, $userid);
1443
2004
}
1444
2005
 
1445
2006
/**
1446
 
 * Rebuilds the cached list of course activities stored in the database
1447
 
 * @param int $courseid - id of course to rebuild, empty means all
1448
 
 * @param boolean $clearonly - only clear the modinfo fields, gets rebuild automatically on the fly
 
2007
 * Rebuilds or resets the cached list of course activities stored in MUC.
 
2008
 *
 
2009
 * rebuild_course_cache() must NEVER be called from lib/db/upgrade.php.
 
2010
 * At the same time course cache may ONLY be cleared using this function in
 
2011
 * upgrade scripts of plugins.
 
2012
 *
 
2013
 * During the bulk operations if it is necessary to reset cache of multiple
 
2014
 * courses it is enough to call {@link increment_revision_number()} for the
 
2015
 * table 'course' and field 'cacherev' specifying affected courses in select.
 
2016
 *
 
2017
 * Cached course information is stored in MUC core/coursemodinfo and is
 
2018
 * validated with the DB field {course}.cacherev
 
2019
 *
 
2020
 * @global moodle_database $DB
 
2021
 * @param int $courseid id of course to rebuild, empty means all
 
2022
 * @param boolean $clearonly only clear the cache, gets rebuild automatically on the fly.
 
2023
 *     Recommended to set to true to avoid unnecessary multiple rebuilding.
1449
2024
 */
1450
2025
function rebuild_course_cache($courseid=0, $clearonly=false) {
1451
2026
    global $COURSE, $SITE, $DB, $CFG;
1463
2038
        format_base::reset_course_cache($courseid);
1464
2039
    }
1465
2040
 
 
2041
    $cachecoursemodinfo = cache::make('core', 'coursemodinfo');
 
2042
    if (empty($courseid)) {
 
2043
        // Clearing caches for all courses.
 
2044
        increment_revision_number('course', 'cacherev', '');
 
2045
        $cachecoursemodinfo->purge();
 
2046
        course_modinfo::clear_instance_cache();
 
2047
        // Update global values too.
 
2048
        $sitecacherev = $DB->get_field('course', 'cacherev', array('id' => SITEID));
 
2049
        $SITE->cachrev = $sitecacherev;
 
2050
        if ($COURSE->id == SITEID) {
 
2051
            $COURSE->cacherev = $sitecacherev;
 
2052
        } else {
 
2053
            $COURSE->cacherev = $DB->get_field('course', 'cacherev', array('id' => $COURSE->id));
 
2054
        }
 
2055
    } else {
 
2056
        // Clearing cache for one course, make sure it is deleted from user request cache as well.
 
2057
        increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $courseid));
 
2058
        $cachecoursemodinfo->delete($courseid);
 
2059
        course_modinfo::clear_instance_cache($courseid);
 
2060
        // Update global values too.
 
2061
        if ($courseid == $COURSE->id || $courseid == $SITE->id) {
 
2062
            $cacherev = $DB->get_field('course', 'cacherev', array('id' => $courseid));
 
2063
            if ($courseid == $COURSE->id) {
 
2064
                $COURSE->cacherev = $cacherev;
 
2065
            }
 
2066
            if ($courseid == $SITE->id) {
 
2067
                $SITE->cachrev = $cacherev;
 
2068
            }
 
2069
        }
 
2070
    }
 
2071
 
1466
2072
    if ($clearonly) {
1467
 
        if (empty($courseid)) {
1468
 
            $DB->execute('UPDATE {course} set modinfo = ?, sectioncache = ?', array(null, null));
1469
 
        } else {
1470
 
            // Clear both fields in one update
1471
 
            $resetobj = (object)array('id' => $courseid, 'modinfo' => null, 'sectioncache' => null);
1472
 
            $DB->update_record('course', $resetobj);
1473
 
        }
1474
 
        // update cached global COURSE too ;-)
1475
 
        if ($courseid == $COURSE->id or empty($courseid)) {
1476
 
            $COURSE->modinfo = null;
1477
 
            $COURSE->sectioncache = null;
1478
 
        }
1479
 
        if ($courseid == $SITE->id) {
1480
 
            $SITE->modinfo = null;
1481
 
            $SITE->sectioncache = null;
1482
 
        }
1483
 
        // reset the fast modinfo cache
1484
 
        get_fast_modinfo($courseid, 0, true);
1485
2073
        return;
1486
2074
    }
1487
2075
 
1488
 
    require_once("$CFG->dirroot/course/lib.php");
1489
 
 
1490
2076
    if ($courseid) {
1491
2077
        $select = array('id'=>$courseid);
1492
2078
    } else {
1494
2080
        @set_time_limit(0);  // this could take a while!   MDL-10954
1495
2081
    }
1496
2082
 
1497
 
    $rs = $DB->get_recordset("course", $select,'','id,fullname');
 
2083
    $rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields));
 
2084
    // Rebuild cache for each course.
1498
2085
    foreach ($rs as $course) {
1499
 
        $modinfo = serialize(get_array_of_activities($course->id));
1500
 
        $sectioncache = serialize(course_modinfo::build_section_cache($course->id));
1501
 
        $updateobj = (object)array('id' => $course->id,
1502
 
                'modinfo' => $modinfo, 'sectioncache' => $sectioncache);
1503
 
        $DB->update_record("course", $updateobj);
1504
 
        // update cached global COURSE too ;-)
1505
 
        if ($course->id == $COURSE->id) {
1506
 
            $COURSE->modinfo = $modinfo;
1507
 
            $COURSE->sectioncache = $sectioncache;
1508
 
        }
1509
 
        if ($course->id == $SITE->id) {
1510
 
            $SITE->modinfo = $modinfo;
1511
 
            $SITE->sectioncache = $sectioncache;
1512
 
        }
 
2086
        course_modinfo::build_course_cache($course);
1513
2087
    }
1514
2088
    $rs->close();
1515
 
    // reset the fast modinfo cache
1516
 
    get_fast_modinfo($courseid, 0, true);
1517
2089
}
1518
2090
 
1519
2091
 
1590
2162
/**
1591
2163
 * Data about a single section on a course. This contains the fields from the
1592
2164
 * course_sections table, plus additional data when required.
 
2165
 *
 
2166
 * @property-read int $id Section ID - from course_sections table
 
2167
 * @property-read int $course Course ID - from course_sections table
 
2168
 * @property-read int $section Section number - from course_sections table
 
2169
 * @property-read string $name Section name if specified - from course_sections table
 
2170
 * @property-read int $visible Section visibility (1 = visible) - from course_sections table
 
2171
 * @property-read string $summary Section summary text if specified - from course_sections table
 
2172
 * @property-read int $summaryformat Section summary text format (FORMAT_xx constant) - from course_sections table
 
2173
 * @property-read int $showavailability When section is unavailable, this field controls whether it is shown to students (0 =
 
2174
 *    hide completely, 1 = show greyed out with information about when it will be available) -
 
2175
 *    from course_sections table
 
2176
 * @property-read int $availablefrom Available date for this section (0 if not set, or set to seconds since epoch;
 
2177
 *    before this date, section does not display to students) - from course_sections table
 
2178
 * @property-read int $availableuntil Available until date for this section  (0 if not set, or set to seconds since epoch;
 
2179
 *    from this date, section does not display to students) - from course_sections table
 
2180
 * @property-read int $groupingid If section is restricted to users of a particular grouping, this is its id (0 if not set) -
 
2181
 *    from course_sections table
 
2182
 * @property-read array $conditionscompletion Availability conditions for this section based on the completion of
 
2183
 *    course-modules (array from course-module id to required completion state
 
2184
 *    for that module) - from cached data in sectioncache field
 
2185
 * @property-read array $conditionsgrade Availability conditions for this section based on course grades (array from
 
2186
 *    grade item id to object with ->min, ->max fields) - from cached data in
 
2187
 *    sectioncache field
 
2188
 * @property-read array $conditionsfield Availability conditions for this section based on user fields
 
2189
 * @property-read bool $available True if this section is available to the given user i.e. if all availability conditions
 
2190
 *    are met - obtained dynamically
 
2191
 * @property-read string $availableinfo If section is not available to some users, this string gives information about
 
2192
 *    availability which can be displayed to students and/or staff (e.g. 'Available from 3 January 2010')
 
2193
 *    for display on main page - obtained dynamically
 
2194
 * @property-read bool $uservisible True if this section is available to the given user (for example, if current user
 
2195
 *    has viewhiddensections capability, they can access the section even if it is not
 
2196
 *    visible or not available, so this would be true in that case) - obtained dynamically
 
2197
 * @property-read string $sequence Comma-separated list of all modules in the section. Note, this field may not exactly
 
2198
 *    match course_sections.sequence if later has references to non-existing modules or not modules of not available module types.
1593
2199
 */
1594
2200
class section_info implements IteratorAggregate {
1595
2201
    /**
1599
2205
    private $_id;
1600
2206
 
1601
2207
    /**
1602
 
     * Course ID - from course_sections table
1603
 
     * @var int
1604
 
     */
1605
 
    private $_course;
1606
 
 
1607
 
    /**
1608
2208
     * Section number - from course_sections table
1609
2209
     * @var int
1610
2210
     */
1687
2287
 
1688
2288
    /**
1689
2289
     * True if this section is available to students i.e. if all availability conditions
1690
 
     * are met - obtained dynamically
1691
 
     * @var bool
 
2290
     * are met - obtained dynamically on request, see function {@link section_info::get_available()}
 
2291
     * @var bool|null
1692
2292
     */
1693
2293
    private $_available;
1694
2294
 
1695
2295
    /**
1696
 
     * If section is not available to students, this string gives information about
 
2296
     * If section is not available to some users, this string gives information about
1697
2297
     * availability which can be displayed to students and/or staff (e.g. 'Available from 3
1698
 
     * January 2010') for display on main page - obtained dynamically
 
2298
     * January 2010') for display on main page - obtained dynamically on request, see
 
2299
     * function {@link section_info::get_availableinfo()}
1699
2300
     * @var string
1700
2301
     */
1701
2302
    private $_availableinfo;
1703
2304
    /**
1704
2305
     * True if this section is available to the CURRENT user (for example, if current user
1705
2306
     * has viewhiddensections capability, they can access the section even if it is not
1706
 
     * visible or not available, so this would be true in that case)
1707
 
     * @var bool
 
2307
     * visible or not available, so this would be true in that case) - obtained dynamically
 
2308
     * on request, see function {@link section_info::get_uservisible()}
 
2309
     * @var bool|null
1708
2310
     */
1709
2311
    private $_uservisible;
1710
2312
 
1733
2335
    private $cachedformatoptions = array();
1734
2336
 
1735
2337
    /**
 
2338
     * Stores the list of all possible section options defined in each used course format.
 
2339
     * @var array
 
2340
     */
 
2341
    static private $sectionformatoptions = array();
 
2342
 
 
2343
    /**
 
2344
     * Stores the modinfo object passed in constructor, may be used when requesting
 
2345
     * dynamically obtained attributes such as available, availableinfo, uservisible.
 
2346
     * Also used to retrun information about current course or user.
 
2347
     * @var course_modinfo
 
2348
     */
 
2349
    private $modinfo;
 
2350
 
 
2351
    /**
1736
2352
     * Constructs object from database information plus extra required data.
1737
2353
     * @param object $data Array entry from cached sectioncache
1738
2354
     * @param int $number Section number (array key)
1739
 
     * @param int $courseid Course ID
1740
 
     * @param int $sequence Sequence of course-module ids contained within
 
2355
     * @param int $notused1 argument not used (informaion is available in $modinfo)
 
2356
     * @param int $notused2 argument not used (informaion is available in $modinfo)
1741
2357
     * @param course_modinfo $modinfo Owner (needed for checking availability)
1742
 
     * @param int $userid User ID
 
2358
     * @param int $notused3 argument not used (informaion is available in $modinfo)
1743
2359
     */
1744
 
    public function __construct($data, $number, $courseid, $sequence, $modinfo, $userid) {
 
2360
    public function __construct($data, $number, $notused1, $notused2, $modinfo, $notused3) {
1745
2361
        global $CFG;
1746
2362
        require_once($CFG->dirroot.'/course/lib.php');
1747
2363
 
1762
2378
            }
1763
2379
        }
1764
2380
 
1765
 
        // cached course format data
1766
 
        $formatoptionsdef = course_get_format($courseid)->section_format_options();
1767
 
        foreach ($formatoptionsdef as $field => $option) {
 
2381
        // Other data from constructor arguments.
 
2382
        $this->_section = $number;
 
2383
        $this->modinfo = $modinfo;
 
2384
 
 
2385
        // Cached course format data.
 
2386
        $course = $modinfo->get_course();
 
2387
        if (!isset(self::$sectionformatoptions[$course->format])) {
 
2388
            // Store list of section format options defined in each used course format.
 
2389
            // They do not depend on particular course but only on its format.
 
2390
            self::$sectionformatoptions[$course->format] =
 
2391
                    course_get_format($course)->section_format_options();
 
2392
        }
 
2393
        foreach (self::$sectionformatoptions[$course->format] as $field => $option) {
1768
2394
            if (!empty($option['cache'])) {
1769
2395
                if (isset($data->{$field})) {
1770
2396
                    $this->cachedformatoptions[$field] = $data->{$field};
1773
2399
                }
1774
2400
            }
1775
2401
        }
1776
 
 
1777
 
        // Other data from other places
1778
 
        $this->_course = $courseid;
1779
 
        $this->_section = $number;
1780
 
        $this->_sequence = $sequence;
1781
 
 
1782
 
        // Availability data
1783
 
        if (!empty($CFG->enableavailability)) {
1784
 
            require_once($CFG->libdir. '/conditionlib.php');
1785
 
            // Get availability information
1786
 
            $ci = new condition_info_section($this);
1787
 
            $this->_available = $ci->is_available($this->_availableinfo, true,
1788
 
                    $userid, $modinfo);
1789
 
            // Display grouping info if available & not already displaying
1790
 
            // (it would already display if current user doesn't have access)
1791
 
            // for people with managegroups - same logic/class as grouping label
1792
 
            // on individual activities.
1793
 
            $context = context_course::instance($courseid);
1794
 
            if ($this->_availableinfo === '' && $this->_groupingid &&
1795
 
                    has_capability('moodle/course:managegroups', $context)) {
1796
 
                $groupings = groups_get_all_groupings($courseid);
1797
 
                $this->_availableinfo = html_writer::tag('span', '(' . format_string(
1798
 
                        $groupings[$this->_groupingid]->name, true, array('context' => $context)) .
1799
 
                        ')', array('class' => 'groupinglabel'));
1800
 
            }
1801
 
        } else {
1802
 
            $this->_available = true;
1803
 
        }
1804
 
 
1805
 
        // Update visibility for current user
1806
 
        $this->update_user_visible($userid);
1807
2402
    }
1808
2403
 
1809
2404
    /**
1813
2408
     * @return bool
1814
2409
     */
1815
2410
    public function __isset($name) {
1816
 
        if (property_exists($this, '_'.$name)) {
1817
 
            return isset($this->{'_'.$name});
1818
 
        }
1819
 
        $defaultformatoptions = course_get_format($this->_course)->section_format_options();
1820
 
        if (array_key_exists($name, $defaultformatoptions)) {
 
2411
        if (method_exists($this, 'get_'.$name) ||
 
2412
                property_exists($this, '_'.$name) ||
 
2413
                array_key_exists($name, self::$sectionformatoptions[$this->modinfo->get_course()->format])) {
1821
2414
            $value = $this->__get($name);
1822
2415
            return isset($value);
1823
2416
        }
1831
2424
     * @return bool
1832
2425
     */
1833
2426
    public function __empty($name) {
1834
 
        if (property_exists($this, '_'.$name)) {
1835
 
            return empty($this->{'_'.$name});
1836
 
        }
1837
 
        $defaultformatoptions = course_get_format($this->_course)->section_format_options();
1838
 
        if (array_key_exists($name, $defaultformatoptions)) {
 
2427
        if (method_exists($this, 'get_'.$name) ||
 
2428
                property_exists($this, '_'.$name) ||
 
2429
                array_key_exists($name, self::$sectionformatoptions[$this->modinfo->get_course()->format])) {
1839
2430
            $value = $this->__get($name);
1840
2431
            return empty($value);
1841
2432
        }
1850
2441
     * @return bool
1851
2442
     */
1852
2443
    public function __get($name) {
 
2444
        if (method_exists($this, 'get_'.$name)) {
 
2445
            return $this->{'get_'.$name}();
 
2446
        }
1853
2447
        if (property_exists($this, '_'.$name)) {
1854
2448
            return $this->{'_'.$name};
1855
2449
        }
1856
2450
        if (array_key_exists($name, $this->cachedformatoptions)) {
1857
2451
            return $this->cachedformatoptions[$name];
1858
2452
        }
1859
 
        $defaultformatoptions = course_get_format($this->_course)->section_format_options();
1860
2453
        // precheck if the option is defined in format to avoid unnecessary DB queries in get_format_options()
1861
 
        if (array_key_exists($name, $defaultformatoptions)) {
1862
 
            $formatoptions = course_get_format($this->_course)->get_format_options($this);
 
2454
        if (array_key_exists($name, self::$sectionformatoptions[$this->modinfo->get_course()->format])) {
 
2455
            $formatoptions = course_get_format($this->modinfo->get_course())->get_format_options($this);
1863
2456
            return $formatoptions[$name];
1864
2457
        }
1865
2458
        debugging('Invalid section_info property accessed! '.$name);
1867
2460
    }
1868
2461
 
1869
2462
    /**
 
2463
     * Finds whether this section is available at the moment for the current user.
 
2464
     *
 
2465
     * The value can be accessed publicly as $sectioninfo->available
 
2466
     *
 
2467
     * @return bool
 
2468
     */
 
2469
    private function get_available() {
 
2470
        global $CFG;
 
2471
        $userid = $this->modinfo->get_user_id();
 
2472
        if ($this->_available !== null || $userid == -1) {
 
2473
            // Has already been calculated or does not need calculation.
 
2474
            return $this->_available;
 
2475
        }
 
2476
        if (!empty($CFG->enableavailability)) {
 
2477
            require_once($CFG->libdir. '/conditionlib.php');
 
2478
            // Get availability information
 
2479
            $ci = new condition_info_section($this);
 
2480
            $this->_available = $ci->is_available($this->_availableinfo, true,
 
2481
                    $userid, $this->modinfo);
 
2482
            if ($this->_availableinfo === '' && $this->_groupingid) {
 
2483
                // Still may have some extra text in availableinfo because of groupping.
 
2484
                // Set as undefined so the next call to get_availabeinfo() calculates it.
 
2485
                $this->_availableinfo = null;
 
2486
            }
 
2487
        } else {
 
2488
            $this->_available = true;
 
2489
            $this->_availableinfo = '';
 
2490
        }
 
2491
        return $this->_available;
 
2492
    }
 
2493
 
 
2494
    /**
 
2495
     * Returns the availability text shown next to the section on course page.
 
2496
     *
 
2497
     * @return string
 
2498
     */
 
2499
    private function get_availableinfo() {
 
2500
        // Make sure $this->_available has been calculated, it may also fill the _availableinfo property.
 
2501
        $this->get_available();
 
2502
        $userid = $this->modinfo->get_user_id();
 
2503
        if ($this->_availableinfo !== null || $userid == -1) {
 
2504
            // It has been already calculated or does not need calculation.
 
2505
            return $this->_availableinfo;
 
2506
        }
 
2507
        $this->_availableinfo = '';
 
2508
        // Display grouping info if available & not already displaying
 
2509
        // (it would already display if current user doesn't have access)
 
2510
        // for people with managegroups - same logic/class as grouping label
 
2511
        // on individual activities.
 
2512
        $context = context_course::instance($this->get_course());
 
2513
        if ($this->_groupingid && has_capability('moodle/course:managegroups', $context, $userid)) {
 
2514
            $groupings = groups_get_all_groupings($this->get_course());
 
2515
            $this->_availableinfo = html_writer::tag('span', '(' . format_string(
 
2516
                    $groupings[$this->_groupingid]->name, true, array('context' => $context)) .
 
2517
                    ')', array('class' => 'groupinglabel'));
 
2518
        }
 
2519
        return $this->_availableinfo;
 
2520
    }
 
2521
 
 
2522
    /**
1870
2523
     * Implementation of IteratorAggregate::getIterator(), allows to cycle through properties
1871
2524
     * and use {@link convert_to_array()}
1872
2525
     *
1876
2529
        $ret = array();
1877
2530
        foreach (get_object_vars($this) as $key => $value) {
1878
2531
            if (substr($key, 0, 1) == '_') {
1879
 
                $ret[substr($key, 1)] = $this->$key;
 
2532
                if (method_exists($this, 'get'.$key)) {
 
2533
                    $ret[substr($key, 1)] = $this->{'get'.$key}();
 
2534
                } else {
 
2535
                    $ret[substr($key, 1)] = $this->$key;
 
2536
                }
1880
2537
            }
1881
2538
        }
1882
 
        $ret = array_merge($ret, course_get_format($this->_course)->get_format_options($this));
 
2539
        $ret['sequence'] = $this->get_sequence();
 
2540
        $ret['course'] = $this->get_course();
 
2541
        $ret = array_merge($ret, course_get_format($this->modinfo->get_course())->get_format_options($this->_section));
1883
2542
        return new ArrayIterator($ret);
1884
2543
    }
1885
2544
 
1886
2545
    /**
1887
2546
     * Works out whether activity is visible *for current user* - if this is false, they
1888
2547
     * aren't allowed to access it.
1889
 
     * @param int $userid User ID
1890
 
     * @return void
 
2548
     *
 
2549
     * @return bool
1891
2550
     */
1892
 
    private function update_user_visible($userid) {
1893
 
        global $CFG;
1894
 
        $coursecontext = context_course::instance($this->_course);
 
2551
    private function get_uservisible() {
 
2552
        $userid = $this->modinfo->get_user_id();
 
2553
        if ($this->_uservisible !== null || $userid == -1) {
 
2554
            // Has already been calculated or does not need calculation.
 
2555
            return $this->_uservisible;
 
2556
        }
1895
2557
        $this->_uservisible = true;
1896
 
        if ((!$this->_visible || !$this->_available) &&
1897
 
                !has_capability('moodle/course:viewhiddensections', $coursecontext, $userid)) {
1898
 
            $this->_uservisible = false;
1899
 
        }
 
2558
        if (!$this->_visible || !$this->get_available()) {
 
2559
            $coursecontext = context_course::instance($this->get_course());
 
2560
            if (!has_capability('moodle/course:viewhiddensections', $coursecontext, $userid)) {
 
2561
                $this->_uservisible = false;
 
2562
            }
 
2563
        }
 
2564
        return $this->_uservisible;
 
2565
    }
 
2566
 
 
2567
    /**
 
2568
     * Restores the course_sections.sequence value
 
2569
     *
 
2570
     * @return string
 
2571
     */
 
2572
    private function get_sequence() {
 
2573
        if (!empty($this->modinfo->sections[$this->_section])) {
 
2574
            return implode(',', $this->modinfo->sections[$this->_section]);
 
2575
        } else {
 
2576
            return '';
 
2577
        }
 
2578
    }
 
2579
 
 
2580
    /**
 
2581
     * Returns course ID - from course_sections table
 
2582
     *
 
2583
     * @return int
 
2584
     */
 
2585
    private function get_course() {
 
2586
        return $this->modinfo->get_course_id();
1900
2587
    }
1901
2588
 
1902
2589
    /**