Commit | Line | Data |
---|---|---|
28f672b2 | 1 | <?php |
2 | ||
3 | // This file is part of Moodle - http://moodle.org/ | |
4 | // | |
5 | // Moodle is free software: you can redistribute it and/or modify | |
6 | // it under the terms of the GNU General Public License as published by | |
7 | // the Free Software Foundation, either version 3 of the License, or | |
8 | // (at your option) any later version. | |
9 | // | |
10 | // Moodle is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | // GNU General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU General Public License | |
16 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
e4aa175a | 17 | |
28f672b2 | 18 | /** |
19 | * @package mod-scorm | |
20 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
22 | */ | |
23 | ||
24 | /** SCORM_TYPE_LOCAL = local */ | |
9528568b | 25 | define('SCORM_TYPE_LOCAL', 'local'); |
28f672b2 | 26 | /** SCORM_TYPE_LOCALSYNC = localsync */ |
9528568b | 27 | define('SCORM_TYPE_LOCALSYNC', 'localsync'); |
28f672b2 | 28 | /** SCORM_TYPE_EXTERNAL = external */ |
9528568b | 29 | define('SCORM_TYPE_EXTERNAL', 'external'); |
28f672b2 | 30 | /** SCORM_TYPE_IMSREPOSITORY = imsrepository */ |
9528568b | 31 | define('SCORM_TYPE_IMSREPOSITORY', 'imsrepository'); |
32 | ||
33 | ||
e4aa175a | 34 | /** |
28f672b2 | 35 | * Given an object containing all the necessary data, |
36 | * (defined by the form in mod_form.php) this function | |
37 | * will create a new instance and return the id number | |
38 | * of the new instance. | |
39 | * | |
40 | * @global stdClass | |
41 | * @global object | |
42 | * @uses CONTEXT_MODULE | |
43 | * @uses SCORM_TYPE_LOCAL | |
44 | * @uses SCORM_TYPE_LOCALSYNC | |
45 | * @uses SCORM_TYPE_EXTERNAL | |
46 | * @uses SCORM_TYPE_IMSREPOSITORY | |
47 | * @param object $scorm Form data | |
48 | * @param object $mform | |
49 | * @return int new instance id | |
50 | */ | |
9528568b | 51 | function scorm_add_instance($scorm, $mform=null) { |
c18269c7 | 52 | global $CFG, $DB; |
a679d64d | 53 | |
86996ffe | 54 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
a679d64d | 55 | |
d54e2145 | 56 | if (empty($scorm->timerestrict)) { |
57 | $scorm->timeopen = 0; | |
58 | $scorm->timeclose = 0; | |
59 | } | |
60 | ||
9528568b | 61 | $cmid = $scorm->coursemodule; |
62 | $cmidnumber = $scorm->cmidnumber; | |
63 | $courseid = $scorm->course; | |
76ea4fb4 | 64 | |
9528568b | 65 | $context = get_context_instance(CONTEXT_MODULE, $cmid); |
a679d64d | 66 | |
9528568b | 67 | $scorm = scorm_option2text($scorm); |
68 | $scorm->width = (int)str_replace('%', '', $scorm->width); | |
69 | $scorm->height = (int)str_replace('%', '', $scorm->height); | |
e4aa175a | 70 | |
9528568b | 71 | if (!isset($scorm->whatgrade)) { |
72 | $scorm->whatgrade = 0; | |
73 | } | |
74 | $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; | |
b3659259 | 75 | |
a8f3a651 | 76 | $id = $DB->insert_record('scorm', $scorm); |
e4aa175a | 77 | |
9528568b | 78 | /// update course module record - from now on this instance properly exists and all function may be used |
bf8e93d7 | 79 | $DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid)); |
e4aa175a | 80 | |
9528568b | 81 | /// reload scorm instance |
82 | $scorm = $DB->get_record('scorm', array('id'=>$id)); | |
83 | ||
84 | /// store the package and verify | |
85 | if ($scorm->scormtype === SCORM_TYPE_LOCAL) { | |
86 | if ($mform) { | |
87 | $filename = $mform->get_new_filename('packagefile'); | |
88 | if ($filename !== false) { | |
89 | $fs = get_file_storage(); | |
90 | $fs->delete_area_files($context->id, 'scorm_package'); | |
91 | $mform->save_stored_file('packagefile', $context->id, 'scorm_package', 0, '/', $filename); | |
92 | $scorm->reference = $filename; | |
8ba4f1e0 | 93 | } |
e4aa175a | 94 | } |
95 | ||
9528568b | 96 | } else if ($scorm->scormtype === SCORM_TYPE_LOCALSYNC) { |
97 | $scorm->reference = $scorm->packageurl; | |
e4aa175a | 98 | |
9528568b | 99 | } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL) { |
100 | $scorm->reference = $scorm->packageurl; | |
101 | ||
102 | } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY) { | |
103 | $scorm->reference = $scorm->packageurl; | |
531fa830 | 104 | |
a679d64d | 105 | } else { |
9528568b | 106 | return false; |
e4aa175a | 107 | } |
9528568b | 108 | |
109 | // save reference | |
110 | $DB->update_record('scorm', $scorm); | |
111 | ||
112 | ||
113 | /// extra fields required in grade related functions | |
114 | $scorm->course = $courseid; | |
115 | $scorm->cmidnumber = $cmidnumber; | |
116 | $scorm->cmid = $cmid; | |
117 | ||
118 | scorm_parse($scorm, true); | |
119 | ||
120 | scorm_grade_item_update($scorm); | |
121 | ||
122 | return $scorm->id; | |
e4aa175a | 123 | } |
124 | ||
125 | /** | |
28f672b2 | 126 | * Given an object containing all the necessary data, |
127 | * (defined by the form in mod_form.php) this function | |
128 | * will update an existing instance with new data. | |
129 | * | |
130 | * @global stdClass | |
131 | * @global object | |
132 | * @uses CONTEXT_MODULE | |
133 | * @uses SCORM_TYPE_LOCAL | |
134 | * @uses SCORM_TYPE_LOCALSYNC | |
135 | * @uses SCORM_TYPE_EXTERNAL | |
136 | * @uses SCORM_TYPE_IMSREPOSITORY | |
137 | * @param object $scorm Form data | |
138 | * @param object $mform | |
139 | * @return bool | |
140 | */ | |
9528568b | 141 | function scorm_update_instance($scorm, $mform=null) { |
c18269c7 | 142 | global $CFG, $DB; |
e4aa175a | 143 | |
86996ffe | 144 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
76ea4fb4 | 145 | |
d54e2145 | 146 | if (empty($scorm->timerestrict)) { |
147 | $scorm->timeopen = 0; | |
148 | $scorm->timeclose = 0; | |
149 | } | |
150 | ||
9528568b | 151 | $cmid = $scorm->coursemodule; |
152 | $cmidnumber = $scorm->cmidnumber; | |
153 | $courseid = $scorm->course; | |
154 | ||
155 | $scorm->id = $scorm->instance; | |
156 | ||
157 | $context = get_context_instance(CONTEXT_MODULE, $cmid); | |
158 | ||
159 | if ($scorm->scormtype === SCORM_TYPE_LOCAL) { | |
160 | if ($mform) { | |
161 | $filename = $mform->get_new_filename('packagefile'); | |
162 | if ($filename !== false) { | |
163 | $scorm->reference = $filename; | |
164 | $fs = get_file_storage(); | |
165 | $fs->delete_area_files($context->id, 'scorm_package'); | |
166 | $mform->save_stored_file('packagefile', $context->id, 'scorm_package', 0, '/', $filename); | |
a679d64d | 167 | } |
76ea4fb4 | 168 | } |
76ea4fb4 | 169 | |
9528568b | 170 | } else if ($scorm->scormtype === SCORM_TYPE_LOCALSYNC) { |
171 | $scorm->reference = $scorm->packageurl; | |
172 | ||
173 | } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL) { | |
174 | $scorm->reference = $scorm->packageurl; | |
175 | ||
176 | } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY) { | |
177 | $scorm->reference = $scorm->packageurl; | |
178 | ||
179 | } else { | |
180 | return false; | |
181 | } | |
bfe8c2f0 | 182 | |
e4aa175a | 183 | $scorm = scorm_option2text($scorm); |
9528568b | 184 | $scorm->width = (int)str_replace('%','',$scorm->width); |
185 | $scorm->height = (int)str_replace('%','',$scorm->height); | |
186 | $scorm->timemodified = time(); | |
e4aa175a | 187 | |
b3659259 | 188 | if (!isset($scorm->whatgrade)) { |
189 | $scorm->whatgrade = 0; | |
190 | } | |
9528568b | 191 | $scorm->grademethod = ($scorm->whatgrade * 10) + $scorm->grademethod; |
a30b6819 | 192 | |
a8c31db2 | 193 | $DB->update_record('scorm', $scorm); |
531fa830 | 194 | |
9528568b | 195 | $scorm = $DB->get_record('scorm', array('id'=>$scorm->id)); |
196 | ||
197 | /// extra fields required in grade related functions | |
198 | $scorm->course = $courseid; | |
199 | $scorm->idnumber = $cmidnumber; | |
200 | $scorm->cmid = $cmid; | |
201 | ||
202 | scorm_parse($scorm, (bool)$scorm->updatefreq); | |
203 | ||
204 | scorm_grade_item_update($scorm); | |
205 | ||
206 | return true; | |
e4aa175a | 207 | } |
208 | ||
209 | /** | |
28f672b2 | 210 | * Given an ID of an instance of this module, |
211 | * this function will permanently delete the instance | |
212 | * and any data that depends on it. | |
213 | * | |
214 | * @global stdClass | |
215 | * @global object | |
216 | * @param int $id Scorm instance id | |
217 | * @return boolean | |
218 | */ | |
e4aa175a | 219 | function scorm_delete_instance($id) { |
c18269c7 | 220 | global $CFG, $DB; |
e4aa175a | 221 | |
c18269c7 | 222 | if (! $scorm = $DB->get_record('scorm', array('id'=>$id))) { |
e4aa175a | 223 | return false; |
224 | } | |
225 | ||
226 | $result = true; | |
227 | ||
e4aa175a | 228 | // Delete any dependent records |
c18269c7 | 229 | if (! $DB->delete_records('scorm_scoes_track', array('scormid'=>$scorm->id))) { |
e4aa175a | 230 | $result = false; |
231 | } | |
c18269c7 | 232 | if ($scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) { |
b3659259 | 233 | foreach ($scoes as $sco) { |
c18269c7 | 234 | if (! $DB->delete_records('scorm_scoes_data', array('scoid'=>$sco->id))) { |
b3659259 | 235 | $result = false; |
236 | } | |
9528568b | 237 | } |
c18269c7 | 238 | $DB->delete_records('scorm_scoes', array('scorm'=>$scorm->id)); |
b3659259 | 239 | } else { |
e4aa175a | 240 | $result = false; |
241 | } | |
c18269c7 | 242 | if (! $DB->delete_records('scorm', array('id'=>$scorm->id))) { |
e4aa175a | 243 | $result = false; |
244 | } | |
a30b6819 | 245 | |
c18269c7 | 246 | /*if (! $DB->delete_records('scorm_sequencing_controlmode', array('scormid'=>$scorm->id))) { |
e4aa175a | 247 | $result = false; |
248 | } | |
c18269c7 | 249 | if (! $DB->delete_records('scorm_sequencing_rolluprules', array('scormid'=>$scorm->id))) { |
e4aa175a | 250 | $result = false; |
251 | } | |
c18269c7 | 252 | if (! $DB->delete_records('scorm_sequencing_rolluprule', array('scormid'=>$scorm->id))) { |
e4aa175a | 253 | $result = false; |
254 | } | |
c18269c7 | 255 | if (! $DB->delete_records('scorm_sequencing_rollupruleconditions', array('scormid'=>$scorm->id))) { |
e4aa175a | 256 | $result = false; |
257 | } | |
c18269c7 | 258 | if (! $DB->delete_records('scorm_sequencing_rolluprulecondition', array('scormid'=>$scorm->id))) { |
e4aa175a | 259 | $result = false; |
260 | } | |
c18269c7 | 261 | if (! $DB->delete_records('scorm_sequencing_rulecondition', array('scormid'=>$scorm->id))) { |
e4aa175a | 262 | $result = false; |
263 | } | |
c18269c7 | 264 | if (! $DB->delete_records('scorm_sequencing_ruleconditions', array('scormid'=>$scorm->id))) { |
e4aa175a | 265 | $result = false; |
9528568b | 266 | }*/ |
531fa830 | 267 | |
c18269c7 | 268 | scorm_grade_item_delete($scorm); |
9528568b | 269 | |
e4aa175a | 270 | return $result; |
271 | } | |
272 | ||
273 | /** | |
28f672b2 | 274 | * Return a small object with summary information about what a |
275 | * user has done with a given particular instance of this module | |
276 | * Used for user activity reports. | |
277 | * | |
278 | * @global stdClass | |
279 | * @param int $course Course id | |
280 | * @param int $user User id | |
281 | * @param int $mod | |
282 | * @param int $scorm The scorm id | |
283 | * @return mixed | |
284 | */ | |
9528568b | 285 | function scorm_user_outline($course, $user, $mod, $scorm) { |
531fa830 | 286 | global $CFG; |
86996ffe | 287 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
a30b6819 | 288 | |
1a96363a NC |
289 | require_once("$CFG->libdir/gradelib.php"); |
290 | $grades = grade_get_grades($course->id, 'mod', 'scorm', $scorm->id, $user->id); | |
291 | if (!empty($grades->items[0]->grades)) { | |
292 | $grade = reset($grades->items[0]->grades); | |
293 | $result = new object(); | |
294 | $result->info = get_string('grade') . ': '. $grade->str_long_grade; | |
295 | $result->time = $grade->dategraded; | |
296 | return $result; | |
297 | } | |
298 | return null; | |
e4aa175a | 299 | } |
300 | ||
301 | /** | |
28f672b2 | 302 | * Print a detailed representation of what a user has done with |
303 | * a given particular instance of this module, for user activity reports. | |
304 | * | |
305 | * @global stdClass | |
306 | * @global object | |
307 | * @param object $course | |
308 | * @param object $user | |
309 | * @param object $mod | |
310 | * @param object $scorm | |
311 | * @return boolean | |
312 | */ | |
e4aa175a | 313 | function scorm_user_complete($course, $user, $mod, $scorm) { |
d436d197 | 314 | global $CFG, $DB, $OUTPUT; |
1a96363a | 315 | require_once("$CFG->libdir/gradelib.php"); |
e4aa175a | 316 | |
317 | $liststyle = 'structlist'; | |
e4aa175a | 318 | $now = time(); |
319 | $firstmodify = $now; | |
320 | $lastmodify = 0; | |
321 | $sometoreport = false; | |
322 | $report = ''; | |
9895302c | 323 | |
c86a91d5 | 324 | // First Access and Last Access dates for SCOs |
86996ffe | 325 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
c86a91d5 PH |
326 | $timetracks = scorm_get_sco_runtime($scorm->id, false, $user->id); |
327 | $firstmodify = $timetracks->start; | |
328 | $lastmodify = $timetracks->finish; | |
9895302c | 329 | |
1a96363a NC |
330 | $grades = grade_get_grades($course->id, 'mod', 'scorm', $scorm->id, $user->id); |
331 | if (!empty($grades->items[0]->grades)) { | |
332 | $grade = reset($grades->items[0]->grades); | |
333 | echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade); | |
334 | if ($grade->str_feedback) { | |
335 | echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback); | |
336 | } | |
337 | } | |
338 | ||
bf347041 | 339 | if ($orgs = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''),'id','id,identifier,title')) { |
e4aa175a | 340 | if (count($orgs) <= 1) { |
341 | unset($orgs); | |
342 | $orgs[]->identifier = ''; | |
343 | } | |
344 | $report .= '<div class="mod-scorm">'."\n"; | |
345 | foreach ($orgs as $org) { | |
bf347041 | 346 | $conditions = array(); |
e4aa175a | 347 | $currentorg = ''; |
348 | if (!empty($org->identifier)) { | |
349 | $report .= '<div class="orgtitle">'.$org->title.'</div>'; | |
350 | $currentorg = $org->identifier; | |
bf347041 | 351 | $conditions['organization'] = $currentorg; |
e4aa175a | 352 | } |
353 | $report .= "<ul id='0' class='$liststyle'>"; | |
bf347041 | 354 | $conditions['scorm'] = $scorm->id; |
355 | if ($scoes = $DB->get_records('scorm_scoes', $conditions, "id ASC")){ | |
9fb2de4e | 356 | // drop keys so that we can access array sequentially |
9528568b | 357 | $scoes = array_values($scoes); |
e4aa175a | 358 | $level=0; |
359 | $sublist=1; | |
360 | $parents[$level]='/'; | |
9fb2de4e | 361 | foreach ($scoes as $pos=>$sco) { |
e4aa175a | 362 | if ($parents[$level]!=$sco->parent) { |
363 | if ($level>0 && $parents[$level-1]==$sco->parent) { | |
364 | $report .= "\t\t</ul></li>\n"; | |
365 | $level--; | |
366 | } else { | |
367 | $i = $level; | |
368 | $closelist = ''; | |
369 | while (($i > 0) && ($parents[$level] != $sco->parent)) { | |
370 | $closelist .= "\t\t</ul></li>\n"; | |
371 | $i--; | |
372 | } | |
373 | if (($i == 0) && ($sco->parent != $currentorg)) { | |
374 | $report .= "\t\t<li><ul id='$sublist' class='$liststyle'>\n"; | |
375 | $level++; | |
376 | } else { | |
377 | $report .= $closelist; | |
378 | $level = $i; | |
379 | } | |
380 | $parents[$level]=$sco->parent; | |
381 | } | |
382 | } | |
383 | $report .= "\t\t<li>"; | |
9fb2de4e | 384 | if (isset($scoes[$pos+1])) { |
385 | $nextsco = $scoes[$pos+1]; | |
386 | } else { | |
387 | $nextsco = false; | |
388 | } | |
e4aa175a | 389 | if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { |
390 | $sublist++; | |
391 | } else { | |
485f4ce6 | 392 | $report .= '<img src="'.$OUTPUT->pix_url('spacer', 'scorm').'" alt="" />'; |
e4aa175a | 393 | } |
394 | ||
395 | if ($sco->launch) { | |
e4aa175a | 396 | $score = ''; |
397 | $totaltime = ''; | |
398 | if ($usertrack=scorm_get_tracks($sco->id,$user->id)) { | |
399 | if ($usertrack->status == '') { | |
400 | $usertrack->status = 'notattempted'; | |
401 | } | |
402 | $strstatus = get_string($usertrack->status,'scorm'); | |
485f4ce6 | 403 | $report .= "<img src='".$OUTPUT->pix_url($usertrack->status, 'scorm')."' alt='$strstatus' title='$strstatus' />"; |
e4aa175a | 404 | } else { |
405 | if ($sco->scormtype == 'sco') { | |
485f4ce6 | 406 | $report .= '<img src="'.$OUTPUT->pix_url('notattempted', 'scorm').'" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />'; |
e4aa175a | 407 | } else { |
485f4ce6 | 408 | $report .= '<img src="'.$OUTPUT->pix_url('asset', 'scorm').'" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />'; |
e4aa175a | 409 | } |
410 | } | |
411 | $report .= " $sco->title $score$totaltime</li>\n"; | |
412 | if ($usertrack !== false) { | |
413 | $sometoreport = true; | |
414 | $report .= "\t\t\t<li><ul class='$liststyle'>\n"; | |
415 | foreach($usertrack as $element => $value) { | |
416 | if (substr($element,0,3) == 'cmi') { | |
417 | $report .= '<li>'.$element.' => '.$value.'</li>'; | |
418 | } | |
419 | } | |
420 | $report .= "\t\t\t</ul></li>\n"; | |
9528568b | 421 | } |
e4aa175a | 422 | } else { |
423 | $report .= " $sco->title</li>\n"; | |
424 | } | |
425 | } | |
426 | for ($i=0;$i<$level;$i++) { | |
427 | $report .= "\t\t</ul></li>\n"; | |
428 | } | |
429 | } | |
430 | $report .= "\t</ul><br />\n"; | |
431 | } | |
432 | $report .= "</div>\n"; | |
433 | } | |
434 | if ($sometoreport) { | |
435 | if ($firstmodify < $now) { | |
436 | $timeago = format_time($now - $firstmodify); | |
437 | echo get_string('firstaccess','scorm').': '.userdate($firstmodify).' ('.$timeago.")<br />\n"; | |
438 | } | |
439 | if ($lastmodify > 0) { | |
440 | $timeago = format_time($now - $lastmodify); | |
441 | echo get_string('lastaccess','scorm').': '.userdate($lastmodify).' ('.$timeago.")<br />\n"; | |
442 | } | |
443 | echo get_string('report','scorm').":<br />\n"; | |
444 | echo $report; | |
445 | } else { | |
dabfd0ed | 446 | print_string('noactivity','scorm'); |
e4aa175a | 447 | } |
448 | ||
449 | return true; | |
450 | } | |
451 | ||
e4aa175a | 452 | /** |
28f672b2 | 453 | * Function to be run periodically according to the moodle cron |
454 | * This function searches for things that need to be done, such | |
455 | * as sending out mail, toggling flags etc ... | |
456 | * | |
457 | * @global stdClass | |
458 | * @global object | |
459 | * @return boolean | |
460 | */ | |
e4aa175a | 461 | function scorm_cron () { |
bf347041 | 462 | global $CFG, $DB; |
a679d64d | 463 | |
86996ffe | 464 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
a679d64d | 465 | |
466 | $sitetimezone = $CFG->timezone; | |
9528568b | 467 | /// Now see if there are any scorm updates to be done |
468 | ||
bfe8c2f0 | 469 | if (!isset($CFG->scorm_updatetimelast)) { // To catch the first time |
470 | set_config('scorm_updatetimelast', 0); | |
471 | } | |
472 | ||
a679d64d | 473 | $timenow = time(); |
c5b3ba6a | 474 | $updatetime = usergetmidnight($timenow, $sitetimezone); |
a679d64d | 475 | |
476 | if ($CFG->scorm_updatetimelast < $updatetime and $timenow > $updatetime) { | |
477 | ||
bfe8c2f0 | 478 | set_config('scorm_updatetimelast', $timenow); |
e4aa175a | 479 | |
376c9c70 | 480 | mtrace('Updating scorm packages which require daily update');//We are updating |
bfe8c2f0 | 481 | |
bf347041 | 482 | $scormsupdate = $DB->get_records('scorm', array('updatefreq'=>UPDATE_EVERYDAY)); |
9528568b | 483 | foreach($scormsupdate as $scormupdate) { |
484 | scorm_parse($scormupdate, true); | |
a679d64d | 485 | } |
486 | } | |
487 | ||
e4aa175a | 488 | return true; |
489 | } | |
490 | ||
491 | /** | |
531fa830 | 492 | * Return grade for given user or all users. |
493 | * | |
28f672b2 | 494 | * @global stdClass |
495 | * @global object | |
531fa830 | 496 | * @param int $scormid id of scorm |
497 | * @param int $userid optional user id, 0 means all users | |
498 | * @return array array of grades, false if none | |
499 | */ | |
500 | function scorm_get_user_grades($scorm, $userid=0) { | |
bf347041 | 501 | global $CFG, $DB; |
86996ffe | 502 | require_once($CFG->dirroot.'/mod/scorm/locallib.php'); |
531fa830 | 503 | |
504 | $grades = array(); | |
505 | if (empty($userid)) { | |
bf347041 | 506 | if ($scousers = $DB->get_records_select('scorm_scoes_track', "scormid=? GROUP BY userid", array($scorm->id), "", "userid,null")) { |
531fa830 | 507 | foreach ($scousers as $scouser) { |
508 | $grades[$scouser->userid] = new object(); | |
509 | $grades[$scouser->userid]->id = $scouser->userid; | |
510 | $grades[$scouser->userid]->userid = $scouser->userid; | |
511 | $grades[$scouser->userid]->rawgrade = scorm_grade_user($scorm, $scouser->userid); | |
512 | } | |
513 | } else { | |
514 | return false; | |
515 | } | |
e4aa175a | 516 | |
531fa830 | 517 | } else { |
bf347041 | 518 | if (!$DB->get_records_select('scorm_scoes_track', "scormid=? AND userid=? GROUP BY userid", array($scorm->id, $userid), "", "userid,null")) { |
531fa830 | 519 | return false; //no attempt yet |
520 | } | |
521 | $grades[$userid] = new object(); | |
522 | $grades[$userid]->id = $userid; | |
523 | $grades[$userid]->userid = $userid; | |
524 | $grades[$userid]->rawgrade = scorm_grade_user($scorm, $userid); | |
e4aa175a | 525 | } |
e4aa175a | 526 | |
531fa830 | 527 | return $grades; |
528 | } | |
529 | ||
530 | /** | |
531 | * Update grades in central gradebook | |
532 | * | |
28f672b2 | 533 | * @global stdClass |
534 | * @global object | |
775f811a | 535 | * @param object $scorm |
531fa830 | 536 | * @param int $userid specific user only, 0 mean all |
28f672b2 | 537 | * @param bool $nullifnone |
531fa830 | 538 | */ |
775f811a | 539 | function scorm_update_grades($scorm, $userid=0, $nullifnone=true) { |
bf347041 | 540 | global $CFG, $DB; |
775f811a | 541 | require_once($CFG->libdir.'/gradelib.php'); |
531fa830 | 542 | |
775f811a | 543 | if ($grades = scorm_get_user_grades($scorm, $userid)) { |
544 | scorm_grade_item_update($scorm, $grades); | |
531fa830 | 545 | |
775f811a | 546 | } else if ($userid and $nullifnone) { |
547 | $grade = new object(); | |
548 | $grade->userid = $userid; | |
549 | $grade->rawgrade = NULL; | |
550 | scorm_grade_item_update($scorm, $grade); | |
531fa830 | 551 | |
e4aa175a | 552 | } else { |
775f811a | 553 | scorm_grade_item_update($scorm); |
554 | } | |
555 | } | |
556 | ||
557 | /** | |
558 | * Update all grades in gradebook. | |
28f672b2 | 559 | * |
560 | * @global object | |
775f811a | 561 | */ |
562 | function scorm_upgrade_grades() { | |
563 | global $DB; | |
564 | ||
565 | $sql = "SELECT COUNT('x') | |
566 | FROM {scorm} s, {course_modules} cm, {modules} m | |
567 | WHERE m.name='scorm' AND m.id=cm.module AND cm.instance=s.id"; | |
568 | $count = $DB->count_records_sql($sql); | |
569 | ||
570 | $sql = "SELECT s.*, cm.idnumber AS cmidnumber, s.course AS courseid | |
571 | FROM {scorm} s, {course_modules} cm, {modules} m | |
572 | WHERE m.name='scorm' AND m.id=cm.module AND cm.instance=s.id"; | |
573 | if ($rs = $DB->get_recordset_sql($sql)) { | |
775f811a | 574 | $pbar = new progress_bar('scormupgradegrades', 500, true); |
575 | $i=0; | |
576 | foreach ($rs as $scorm) { | |
577 | $i++; | |
578 | upgrade_set_timeout(60*5); // set up timeout, may also abort execution | |
579 | scorm_update_grades($scorm, 0, false); | |
580 | $pbar->update($i, $count, "Updating Scorm grades ($i/$count)."); | |
531fa830 | 581 | } |
775f811a | 582 | $rs->close(); |
e4aa175a | 583 | } |
531fa830 | 584 | } |
e4aa175a | 585 | |
531fa830 | 586 | /** |
587 | * Update/create grade item for given scorm | |
588 | * | |
28f672b2 | 589 | * @global stdClass |
590 | * @global object | |
591 | * @uses GRADE_TYPE_VALUE | |
592 | * @uses GRADE_TYPE_NONE | |
531fa830 | 593 | * @param object $scorm object with extra cmidnumber |
28f672b2 | 594 | * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook |
531fa830 | 595 | * @return object grade_item |
596 | */ | |
0b5a80a1 | 597 | function scorm_grade_item_update($scorm, $grades=NULL) { |
bf347041 | 598 | global $CFG, $DB; |
531fa830 | 599 | if (!function_exists('grade_update')) { //workaround for buggy PHP versions |
600 | require_once($CFG->libdir.'/gradelib.php'); | |
601 | } | |
602 | ||
7ef16bf9 | 603 | $params = array('itemname'=>$scorm->name); |
604 | if (isset($scorm->cmidnumber)) { | |
605 | $params['idnumber'] = $scorm->cmidnumber; | |
606 | } | |
9528568b | 607 | |
531fa830 | 608 | if (($scorm->grademethod % 10) == 0) { // GRADESCOES |
5b4b959b | 609 | if ($maxgrade = $DB->count_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty()))) { |
531fa830 | 610 | $params['gradetype'] = GRADE_TYPE_VALUE; |
611 | $params['grademax'] = $maxgrade; | |
612 | $params['grademin'] = 0; | |
613 | } else { | |
614 | $params['gradetype'] = GRADE_TYPE_NONE; | |
e4aa175a | 615 | } |
531fa830 | 616 | } else { |
617 | $params['gradetype'] = GRADE_TYPE_VALUE; | |
618 | $params['grademax'] = $scorm->maxgrade; | |
619 | $params['grademin'] = 0; | |
e4aa175a | 620 | } |
531fa830 | 621 | |
0b5a80a1 | 622 | if ($grades === 'reset') { |
623 | $params['reset'] = true; | |
624 | $grades = NULL; | |
625 | } | |
626 | ||
627 | return grade_update('mod/scorm', $scorm->course, 'mod', 'scorm', $scorm->id, 0, $grades, $params); | |
531fa830 | 628 | } |
629 | ||
630 | /** | |
631 | * Delete grade item for given scorm | |
632 | * | |
28f672b2 | 633 | * @global stdClass |
531fa830 | 634 | * @param object $scorm object |
635 | * @return object grade_item | |
636 | */ | |
637 | function scorm_grade_item_delete($scorm) { | |
638 | global $CFG; | |
639 | require_once($CFG->libdir.'/gradelib.php'); | |
640 | ||
641 | return grade_update('mod/scorm', $scorm->course, 'mod', 'scorm', $scorm->id, 0, NULL, array('deleted'=>1)); | |
e4aa175a | 642 | } |
643 | ||
28f672b2 | 644 | /** |
645 | * @return array | |
646 | */ | |
e4aa175a | 647 | function scorm_get_view_actions() { |
648 | return array('pre-view','view','view all','report'); | |
649 | } | |
650 | ||
28f672b2 | 651 | /** |
652 | * @return array | |
653 | */ | |
e4aa175a | 654 | function scorm_get_post_actions() { |
655 | return array(); | |
656 | } | |
657 | ||
28f672b2 | 658 | /** |
659 | * @param object $scorm | |
660 | * @return object $scorm | |
661 | */ | |
e4aa175a | 662 | function scorm_option2text($scorm) { |
1adc77e6 | 663 | $scorm_popoup_options = scorm_get_popup_options_array(); |
e5dd8e3b | 664 | |
e4aa175a | 665 | if (isset($scorm->popup)) { |
76ea4fb4 | 666 | if ($scorm->popup == 1) { |
e4aa175a | 667 | $optionlist = array(); |
1adc77e6 | 668 | foreach ($scorm_popoup_options as $name => $option) { |
e4aa175a | 669 | if (isset($scorm->$name)) { |
670 | $optionlist[] = $name.'='.$scorm->$name; | |
671 | } else { | |
672 | $optionlist[] = $name.'=0'; | |
673 | } | |
9528568b | 674 | } |
e4aa175a | 675 | $scorm->options = implode(',', $optionlist); |
676 | } else { | |
677 | $scorm->options = ''; | |
9528568b | 678 | } |
e4aa175a | 679 | } else { |
680 | $scorm->popup = 0; | |
681 | $scorm->options = ''; | |
682 | } | |
683 | return $scorm; | |
684 | } | |
685 | ||
0b5a80a1 | 686 | /** |
687 | * Implementation of the function for printing the form elements that control | |
688 | * whether the course reset functionality affects the scorm. | |
e5dd8e3b | 689 | * |
28f672b2 | 690 | * @param object $mform form passed by reference |
0b5a80a1 | 691 | */ |
692 | function scorm_reset_course_form_definition(&$mform) { | |
693 | $mform->addElement('header', 'scormheader', get_string('modulenameplural', 'scorm')); | |
694 | $mform->addElement('advcheckbox', 'reset_scorm', get_string('deleteallattempts','scorm')); | |
695 | } | |
696 | ||
697 | /** | |
698 | * Course reset form defaults. | |
28f672b2 | 699 | * |
700 | * @return array | |
0b5a80a1 | 701 | */ |
702 | function scorm_reset_course_form_defaults($course) { | |
703 | return array('reset_scorm'=>1); | |
704 | } | |
705 | ||
706 | /** | |
707 | * Removes all grades from gradebook | |
28f672b2 | 708 | * |
709 | * @global stdClass | |
710 | * @global object | |
0b5a80a1 | 711 | * @param int $courseid |
712 | * @param string optional type | |
713 | */ | |
714 | function scorm_reset_gradebook($courseid, $type='') { | |
bf347041 | 715 | global $CFG, $DB; |
0b5a80a1 | 716 | |
717 | $sql = "SELECT s.*, cm.idnumber as cmidnumber, s.course as courseid | |
bf347041 | 718 | FROM {scorm} s, {course_modules} cm, {modules} m |
719 | WHERE m.name='scorm' AND m.id=cm.module AND cm.instance=s.id AND s.course=?"; | |
0b5a80a1 | 720 | |
bf347041 | 721 | if ($scorms = $DB->get_records_sql($sql, array($courseid))) { |
0b5a80a1 | 722 | foreach ($scorms as $scorm) { |
723 | scorm_grade_item_update($scorm, 'reset'); | |
724 | } | |
725 | } | |
726 | } | |
727 | ||
728 | /** | |
729 | * Actual implementation of the rest coures functionality, delete all the | |
730 | * scorm attempts for course $data->courseid. | |
28f672b2 | 731 | * |
732 | * @global stdClass | |
733 | * @global object | |
734 | * @param object $data the data submitted from the reset course. | |
0b5a80a1 | 735 | * @return array status array |
736 | */ | |
737 | function scorm_reset_userdata($data) { | |
bf347041 | 738 | global $CFG, $DB; |
0b5a80a1 | 739 | |
740 | $componentstr = get_string('modulenameplural', 'scorm'); | |
741 | $status = array(); | |
742 | ||
743 | if (!empty($data->reset_scorm)) { | |
744 | $scormssql = "SELECT s.id | |
bf347041 | 745 | FROM {scorm} s |
746 | WHERE s.course=?"; | |
0b5a80a1 | 747 | |
bf347041 | 748 | $DB->delete_records_select('scorm_scoes_track', "scormid IN ($scormssql)", array($data->courseid)); |
0b5a80a1 | 749 | |
750 | // remove all grades from gradebook | |
751 | if (empty($data->reset_gradebook_grades)) { | |
752 | scorm_reset_gradebook($data->courseid); | |
753 | } | |
754 | ||
755 | $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallattempts', 'scorm'), 'error'=>false); | |
756 | } | |
757 | ||
758 | // no dates to shift here | |
759 | ||
760 | return $status; | |
761 | } | |
762 | ||
f432bebf | 763 | /** |
764 | * Returns all other caps used in module | |
28f672b2 | 765 | * |
766 | * @return array | |
f432bebf | 767 | */ |
768 | function scorm_get_extra_capabilities() { | |
769 | return array('moodle/site:accessallgroups'); | |
770 | } | |
771 | ||
9528568b | 772 | /** |
773 | * Lists all file areas current user may browse | |
28f672b2 | 774 | * |
775 | * @param object $course | |
776 | * @param object $cm | |
777 | * @param object $context | |
778 | * @return array | |
9528568b | 779 | */ |
780 | function scorm_get_file_areas($course, $cm, $context) { | |
781 | $areas = array(); | |
782 | if (has_capability('moodle/course:managefiles', $context)) { | |
9528568b | 783 | $areas['scorm_content'] = get_string('areacontent', 'scorm'); |
784 | $areas['scorm_package'] = get_string('areapackage', 'scorm'); | |
785 | } | |
786 | return $areas; | |
787 | } | |
788 | ||
789 | /** | |
9895302c | 790 | * File browsing support for SCORM file areas |
e5dd8e3b | 791 | * |
9895302c DC |
792 | * @param stdclass $browser |
793 | * @param stdclass $areas | |
794 | * @param stdclass $course | |
795 | * @param stdclass $cm | |
796 | * @param stdclass $context | |
797 | * @param string $filearea | |
798 | * @param int $itemid | |
799 | * @param string $filepath | |
800 | * @param string $filename | |
801 | * @return stdclass file_info instance or null if not found | |
9528568b | 802 | */ |
803 | function scorm_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { | |
804 | global $CFG; | |
805 | ||
806 | if (!has_capability('moodle/course:managefiles', $context)) { | |
807 | return null; | |
808 | } | |
809 | ||
810 | // no writing for now! | |
811 | ||
812 | $fs = get_file_storage(); | |
813 | ||
814 | if ($filearea === 'scorm_content') { | |
815 | ||
816 | $filepath = is_null($filepath) ? '/' : $filepath; | |
817 | $filename = is_null($filename) ? '.' : $filename; | |
e5dd8e3b | 818 | |
9528568b | 819 | $urlbase = $CFG->wwwroot.'/pluginfile.php'; |
820 | if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) { | |
821 | if ($filepath === '/' and $filename === '.') { | |
822 | $storedfile = new virtual_root_file($context->id, $filearea, 0); | |
823 | } else { | |
824 | // not found | |
825 | return null; | |
826 | } | |
827 | } | |
28f672b2 | 828 | /** |
829 | * @package mod-scorm | |
830 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
831 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
832 | */ | |
9528568b | 833 | class scorm_package_file_info extends file_info_stored { |
834 | public function get_parent() { | |
835 | if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { | |
836 | return $this->browser->get_file_info($this->context); | |
837 | } | |
838 | return parent::get_parent(); | |
839 | } | |
840 | public function get_visible_name() { | |
841 | if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { | |
3156b8ca | 842 | return $this->topvisiblename; |
9528568b | 843 | } |
844 | return parent::get_visible_name(); | |
845 | } | |
846 | } | |
8546def3 | 847 | return new scorm_package_file_info($browser, $context, $storedfile, $urlbase, $areas[$filearea], true, true, false, false); |
9528568b | 848 | |
849 | } else if ($filearea === 'scorm_package') { | |
850 | $filepath = is_null($filepath) ? '/' : $filepath; | |
851 | $filename = is_null($filename) ? '.' : $filename; | |
e5dd8e3b | 852 | |
9528568b | 853 | $urlbase = $CFG->wwwroot.'/pluginfile.php'; |
854 | if (!$storedfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) { | |
855 | if ($filepath === '/' and $filename === '.') { | |
856 | $storedfile = new virtual_root_file($context->id, $filearea, 0); | |
857 | } else { | |
858 | // not found | |
859 | return null; | |
860 | } | |
861 | } | |
9895302c | 862 | return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, false, false); |
9528568b | 863 | } |
864 | ||
865 | // scorm_intro handled in file_browser | |
866 | ||
867 | return false; | |
868 | } | |
869 | ||
870 | /** | |
871 | * Serves scorm content, introduction images and packages. Implements needed access control ;-) | |
28f672b2 | 872 | * |
28f672b2 | 873 | * @param object $course |
874 | * @param object $cminfo | |
875 | * @param object $context | |
876 | * @param string $filearea | |
877 | * @param array $args | |
98edf7b6 | 878 | * @param bool $forcedownload |
86900a93 | 879 | * @return bool false if file not found, does not return if found - just send the file |
9528568b | 880 | */ |
98edf7b6 | 881 | function scorm_pluginfile($course, $cminfo, $context, $filearea, $args, $forcedownload) { |
9528568b | 882 | global $CFG; |
883 | ||
884 | if (!$cminfo->uservisible) { | |
885 | return false; // probably hidden | |
886 | } | |
887 | ||
888 | $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400; | |
889 | ||
0a8a7b6c | 890 | if (!$cm = get_coursemodule_from_instance('scorm', $cminfo->instance, $course->id)) { |
891 | return false; | |
892 | } | |
e5dd8e3b | 893 | |
0a8a7b6c | 894 | require_login($course, true, $cm); |
895 | ||
ac3668bf | 896 | if ($filearea === 'scorm_content') { |
9528568b | 897 | $revision = (int)array_shift($args); // prevents caching problems - ignored here |
898 | $relativepath = '/'.implode('/', $args); | |
899 | $fullpath = $context->id.'scorm_content0'.$relativepath; | |
900 | // TODO: add any other access restrictions here if needed! | |
901 | ||
902 | } else if ($filearea === 'scorm_package') { | |
903 | if (!has_capability('moodle/course:manageactivities', $context)) { | |
904 | return false; | |
905 | } | |
906 | $relativepath = '/'.implode('/', $args); | |
907 | $fullpath = $context->id.'scorm_package0'.$relativepath; | |
908 | $lifetime = 0; // no caching here | |
909 | ||
910 | } else { | |
911 | return false; | |
912 | } | |
913 | ||
914 | $fs = get_file_storage(); | |
915 | if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { | |
916 | return false; | |
917 | } | |
918 | ||
919 | // finally send the file | |
920 | send_stored_file($file, $lifetime, 0, false); | |
921 | } | |
922 | ||
42f103be | 923 | /** |
28f672b2 | 924 | * @uses FEATURE_GROUPS |
925 | * @uses FEATURE_GROUPINGS | |
926 | * @uses FEATURE_GROUPMEMBERSONLY | |
927 | * @uses FEATURE_MOD_INTRO | |
928 | * @uses FEATURE_COMPLETION_TRACKS_VIEWS | |
929 | * @uses FEATURE_GRADE_HAS_GRADE | |
930 | * @uses FEATURE_GRADE_OUTCOMES | |
42f103be | 931 | * @param string $feature FEATURE_xx constant for requested feature |
28f672b2 | 932 | * @return mixed True if module supports feature, false if not, null if doesn't know |
42f103be | 933 | */ |
934 | function scorm_supports($feature) { | |
935 | switch($feature) { | |
936 | case FEATURE_GROUPS: return false; | |
937 | case FEATURE_GROUPINGS: return false; | |
938 | case FEATURE_GROUPMEMBERSONLY: return true; | |
dc5c2bd9 | 939 | case FEATURE_MOD_INTRO: return true; |
42f103be | 940 | case FEATURE_COMPLETION_TRACKS_VIEWS: return true; |
941 | case FEATURE_GRADE_HAS_GRADE: return true; | |
942 | case FEATURE_GRADE_OUTCOMES: return true; | |
02488b86 | 943 | case FEATURE_BACKUP_MOODLE2: return true; |
42f103be | 944 | |
945 | default: return null; | |
946 | } | |
947 | } | |
0a1f8f8f SH |
948 | |
949 | /** | |
950 | * This function extends the global navigaiton for the site. | |
951 | * It is important to note that you should not rely on PAGE objects within this | |
952 | * body of code as there is no guarantee that during an AJAX request they are | |
953 | * available | |
954 | * | |
955 | * @param navigation_node $navigation The scorm node within the global navigation | |
956 | * @param stdClass $course The course object returned from the DB | |
957 | * @param stdClass $module The module object returned from the DB | |
958 | * @param stdClass $cm The course module isntance returned from the DB | |
959 | */ | |
960 | function scorm_extend_navigation($navigation, $course, $module, $cm) { | |
961 | /** | |
962 | * This is currently just a stub so that it can be easily expanded upon. | |
963 | * When expanding just remove this comment and the line below and then add | |
964 | * you content. | |
965 | */ | |
966 | $navigation->nodetype = navigation_node::NODETYPE_LEAF; | |
02488b86 | 967 | } |