ILIAS  release_8 Revision v8.24
class.ilObjSCORM2004LearningModule.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
27{
28 protected ilObjUser $user;
29
30 protected ilTabsGUI $tabs;
31
32 protected bool $import_sequencing = false;
33
34 protected string $imsmanifestFile;
35
36 public const CONVERT_XSL = './Modules/Scorm2004/templates/xsl/op/scorm12To2004.xsl';
37 public const WRAPPER_HTML = './Modules/Scorm2004/scripts/converter/GenericRunTimeWrapper1.0_aadlc/GenericRunTimeWrapper.htm';
38 public const WRAPPER_JS = './Modules/Scorm2004/scripts/converter/GenericRunTimeWrapper1.0_aadlc/SCOPlayerWrapper.js';
39
45 public function __construct(int $a_id = 0, bool $a_call_by_reference = true)
46 {
47 global $DIC;
48
49 $this->lng = $DIC->language();
50 $this->error = $DIC["ilErr"];
51 $this->db = $DIC->database();
52 $this->log = ilLoggerFactory::getLogger('sc13');
53 $this->user = $DIC->user();
54 $this->tabs = $DIC->tabs();
55 $this->type = "sahs";
56 parent::__construct($a_id, $a_call_by_reference);
57 }
58
64 public function setImportSequencing(bool $a_val): void
65 {
66 $this->import_sequencing = $a_val;
67 }
68
74 public function getImportSequencing(): bool
75 {
77 }
78
82 public function readObject(): string
83 {
84 global $DIC;
87
88 //check for json_encode,json_decode
89 if (!function_exists('json_encode') || !function_exists('json_decode')) {
90 $ilErr->raiseError($lng->txt('scplayer_phpmysqlcheck'), $ilErr->WARNING);
91 }
92
93 $needs_convert = false;
94
95 // convert imsmanifest.xml file in iso to utf8 if needed
96
97 $manifest_file = $this->getDataDirectory() . "/imsmanifest.xml";
98
99 // check if manifestfile exists and space left on device...
100 $check_for_manifest_file = is_file($manifest_file);
101
102
103
104 // if no manifestfile
105 if (!$check_for_manifest_file) {
106 $ilErr->raiseError($this->lng->txt("Manifestfile $manifest_file not found!"), $ilErr->MESSAGE);
107 return "";
108 }
109
110
111 if ($check_for_manifest_file) {
112 $manifest_file_array = file($manifest_file);
113
114 foreach ($manifest_file_array as $mfa) {
115 // if (seems_not_utf8($mfa))
116 if (@iconv('UTF-8', 'UTF-8', $mfa) != $mfa) {
117 $needs_convert = true;
118 break;
119 }
120 }
121
122
123
124 // to copy the file we need some extraspace, counted in bytes *2 ... we need 2 copies....
125 $estimated_manifest_filesize = filesize($manifest_file) * 2;
126
127 // i deactivated this, because it seems to fail on some windows systems (see bug #1795)
128 //$check_disc_free = disk_free_space($this->getDataDirectory()) - $estimated_manifest_filesize;
129 $check_disc_free = 2;
130 }
131
132
133 // if $manifest_file needs to be converted to UTF8
134 if ($needs_convert) {
135 // if file exists and enough space left on device
136 if ($check_for_manifest_file && ($check_disc_free > 1)) {
137
138 // create backup from original
139 if (!copy($manifest_file, $manifest_file . ".old")) {
140 echo "Failed to copy $manifest_file...<br>\n";
141 }
142
143 // read backupfile, convert each line to utf8, write line to new file
144 // php < 4.3 style
145 $f_write_handler = fopen($manifest_file . ".new", "w");
146 $f_read_handler = fopen($manifest_file . ".old", "r");
147 while (!feof($f_read_handler)) {
148 $zeile = fgets($f_read_handler);
149 //echo mb_detect_encoding($zeile);
150 fwrite($f_write_handler, utf8_encode($zeile));
151 }
152 fclose($f_read_handler);
153 fclose($f_write_handler);
154
155 // copy new utf8-file to imsmanifest.xml
156 if (!copy($manifest_file . ".new", $manifest_file)) {
157 echo "Failed to copy $manifest_file...<br>\n";
158 }
159
160 if (!@is_file($manifest_file)) {
161 $ilErr->raiseError($this->lng->txt("cont_no_manifest"), $ilErr->WARNING);
162 }
163 } else {
164 // gives out the specific error
165
166 if (!($check_disc_free > 1)) {
167 $ilErr->raiseError($this->lng->txt("Not enough space left on device!"), $ilErr->MESSAGE);
168 }
169 return "";
170 }
171 } else {
172 // check whether file starts with BOM (that confuses some sax parsers, see bug #1795)
173 $hmani = fopen($manifest_file, "r");
174 $start = fread($hmani, 3);
175 if (strtolower(bin2hex($start)) === "efbbbf") {
176 $f_write_handler = fopen($manifest_file . ".new", "w");
177 while (!feof($hmani)) {
178 $n = fread($hmani, 900);
179 fwrite($f_write_handler, $n);
180 }
181 fclose($f_write_handler);
182 fclose($hmani);
183
184 // copy new utf8-file to imsmanifest.xml
185 if (!copy($manifest_file . ".new", $manifest_file)) {
186 echo "Failed to copy $manifest_file...<br>\n";
187 }
188 } else {
189 fclose($hmani);
190 }
191 }
192
193 //check for SCORM 1.2
194 $this->convert_1_2_to_2004($manifest_file);
195
196 return (new ilSCORM13Package())->il_import($this->getDataDirectory(), $this->getId());
197 }
198
199
200 public function fixReload(): void
201 {
202 $out = file_get_contents($this->imsmanifestFile);
203 $check = '/xmlns="http:\/\/www.imsglobal.org\/xsd\/imscp_v1p1"/';
204 $replace = "xmlns=\"http://www.imsproject.org/xsd/imscp_rootv1p1p2\"";
205 $out = preg_replace($check, $replace, $out);
206 file_put_contents($this->imsmanifestFile, $out);
207 }
208
209
210 public function convert_1_2_to_2004(string $manifest): void
211 {
212 $ilLog = $this->log;
213
214 ##check manifest-file for version. Check for schemaversion as this is a required element for SCORM 2004
215 ##accept 2004 3rd Edition an CAM 1.3 as valid schemas
216
217 //set variables
218 $packageFolder = $this->getDataDirectory();
219 $this->imsmanifestFile = $manifest;
220 $doc = new DomDocument();
221
222 //fix reload errors before loading
223 $this->fixReload();
224 $doc->load($this->imsmanifestFile);
225 $elements = $doc->getElementsByTagName("schemaversion");
226 $schema = "";
227 if (isset($elements->item(0)->nodeValue)) {
228 $schema = $elements->item(0)->nodeValue;
229 }
230 if (strtolower(trim($schema)) === "cam 1.3" || strtolower(trim($schema)) === "2004 3rd edition" || strtolower(trim($schema)) === "2004 4th edition") {
231 //no conversion
232 return;
233 }
234
235 //convert to SCORM 2004
236
237 //check for broken SCORM 1.2 manifest file (missing organization default-common error in a lot of manifest files)
238 $organizations = $doc->getElementsByTagName("organizations");
239 //first check if organizations is in manifest
240 if ($organizations->item(0) == null) {
241 die("organizations missing in manifest");
242 }
243 $default = $organizations->item(0)->getAttribute("default");
244 if ($default == "" || $default == null) {
245 //lookup identifier
246 $organization = $doc->getElementsByTagName("organization");
247 $item = $organization->item(0);
248 if ($item !== null) {
249 $ident = $item->getAttribute("identifier");
250 $organizations->item(0)->setAttribute("default", $ident);
251 }
252 }
253
254 //validate the fixed mainfest. If it's still not valid, don't transform an throw error
255
256
257 //first copy wrappers
258 $wrapperdir = $packageFolder . "/GenericRunTimeWrapper1.0_aadlc";
259 if (!mkdir($wrapperdir) && !is_dir($wrapperdir)) {
260 throw new \RuntimeException(sprintf('Directory "%s" was not created', $wrapperdir));
261 }
262 copy(self::WRAPPER_HTML, $wrapperdir . "/GenericRunTimeWrapper.htm");
263 copy(self::WRAPPER_JS, $wrapperdir . "/SCOPlayerWrapper.js");
264
265 //backup manifestfile
266 $backupManifest = $packageFolder . "/imsmanifest.xml.back";
267 $ret = copy($this->imsmanifestFile, $backupManifest);
268
269 //transform manifest file
270 $totransform = $doc;
271 $ilLog->debug("SCORM: about to transform to SCORM 2004");
272
273 $xsl = new DOMDocument();
274 $xsl->async = false;
275 $xsl->load(self::CONVERT_XSL);
276 $prc = new XSLTProcessor();
277 $r = @$prc->importStyleSheet($xsl);
278
279 file_put_contents($this->imsmanifestFile, $prc->transformToXML($totransform));
280
281 $ilLog->debug("SCORM: Transformation completed");
282 }
283
287 public static function _lookupLastAccess(int $a_obj_id, int $a_usr_id): ?string
288 {
289 global $DIC;
290
291 $ilDB = $DIC->database();
292
293 $result = $ilDB->queryF(
294 '
295 SELECT MAX(c_timestamp) last_access
296 FROM cmi_node, cp_node
297 WHERE cmi_node.cp_node_id = cp_node.cp_node_id
298 AND cp_node.slm_id = %s
299 AND user_id = %s
300 GROUP BY c_timestamp',
301 array('integer', 'integer'),
302 array($a_obj_id, $a_usr_id)
303 );
304 if ($ilDB->numRows($result)) {
305 $row = $ilDB->fetchAssoc($result);
306 return (string) $row["last_access"];
307 }
308
309 return null;
310 }
311
312 public function deleteTrackingDataOfUsers(array $a_users): void
313 {
316
317 foreach ($a_users as $user) {
320 }
321 }
322
323
328 public function getTrackedItems(): array
329 {
333
334 $sco_set = $ilDB->queryF(
335 '
336 SELECT DISTINCT cmi_node.cp_node_id id
337 FROM cp_node, cmi_node
338 WHERE slm_id = %s
339 AND cp_node.cp_node_id = cmi_node.cp_node_id
340 ORDER BY cmi_node.cp_node_id ',
341 array('integer'),
342 array($this->getId())
343 );
344
345 $items = array();
346
347 while ($sco_rec = $ilDB->fetchAssoc($sco_set)) {
348 $item['id'] = $sco_rec["id"];
349 $item['title'] = self::_lookupItemTitle((int) $sco_rec["id"]);
350 $items[] = $item;
351 }
352 return $items;
353 }
354
359 public function getTrackingDataAgg(int $a_user_id, ?bool $raw = false): array
360 {
362
363 $scos = array();
364 $data = array();
365 //get all SCO's of this object
366
367 $val_set = $ilDB->queryF(
368 'SELECT cp_node_id FROM cp_node
369 WHERE nodename = %s
370 AND cp_node.slm_id = %s',
371 array('text', 'integer'),
372 array('item',$this->getId())
373 );
374 while ($val_rec = $ilDB->fetchAssoc($val_set)) {
375 $scos[] = $val_rec['cp_node_id'];
376 }
377
378 foreach ($scos as $sco) {
379 $data_set = $ilDB->queryF(
380 '
381 SELECT c_timestamp last_access, total_time, success_status, completion_status,
382 c_raw, scaled, cp_node_id
383 FROM cmi_node
384 WHERE cp_node_id = %s
385 AND user_id = %s',
386 array('integer','integer'),
387 array($sco,$a_user_id)
388 );
389
390 while ($data_rec = $ilDB->fetchAssoc($data_set)) {
391 if ($data_rec["success_status"] != "" && $data_rec["success_status"] !== "unknown") {
392 $status = $data_rec["success_status"];
393 } else {
394 if ($data_rec["completion_status"] == "") {
395 $status = "unknown";
396 } else {
397 $status = $data_rec["completion_status"];
398 }
399 }
400 if (!$raw) {
401 $time = ilDatePresentation::secondsToString((int) round(self::_ISODurationToCentisec($data_rec["total_time"]) / 100));
402 $score = "";
403 if ($data_rec["c_raw"] != null) {
404 $score = $data_rec["c_raw"];
405 if ($data_rec["scaled"] != null) {
406 $score .= " = ";
407 }
408 }
409 if ($data_rec["scaled"] != null) {
410 $score .= ($data_rec["scaled"] * 100) . "%";
411 }
412 $title = self::_lookupItemTitle((int) $data_rec["cp_node_id"]);
413 $last_access = ilDatePresentation::formatDate(new ilDateTime($data_rec['last_access'], IL_CAL_DATETIME));
414 $data[] = array("sco_id" => $data_rec["cp_node_id"],
415 "score" => $score, "time" => $time, "status" => $status,"last_access" => $last_access,"title" => $title);
416 } else {
417 $data_rec["total_time"] = self::_ISODurationToCentisec($data_rec["total_time"]) / 100;
418 $data[$data_rec["cp_node_id"]] = $data_rec;
419 }
420 }
421 }
422
423 return $data;
424 }
425
429 public function getAttemptsForUser(int $a_user_id): int
430 {
432 $val_set = $ilDB->queryF(
433 'SELECT package_attempts FROM sahs_user WHERE user_id = %s AND obj_id = %s',
434 array('integer','integer'),
435 array($a_user_id, $this->getId())
436 );
437
438 $val_rec = $ilDB->fetchAssoc($val_set);
439
440 if ($val_rec["package_attempts"] == null) {
441 $val_rec["package_attempts"] = 0;
442 }
443
444 return (int) $val_rec["package_attempts"];
445 }
446
450 public function getModuleVersionForUser(int $a_user_id): string
451 {
453 $val_set = $ilDB->queryF(
454 'SELECT module_version FROM sahs_user WHERE user_id = %s AND obj_id = %s',
455 array('integer','integer'),
456 array($a_user_id, $this->getId())
457 );
458
459 $val_rec = $ilDB->fetchAssoc($val_set);
460
461 if ($val_rec["module_version"] == null) {
462 $val_rec["module_version"] = "";
463 }
464 return $val_rec["module_version"];
465 }
466
467 public function importSuccess(string $a_file): bool
468 {
471 $scos = array();
472 $olp = ilObjectLP::getInstance($this->getId());
473 $collection = $olp->getCollectionInstance();
474 if ($collection) {
475 $scos = $collection->getItems();
476 }
477
478 $fhandle = fopen($a_file, "rb");//changed from r to rb
479
480 $obj_id = $this->getID();
481 $users = array();
482 $usersToDelete = array();
483 $fields = fgetcsv($fhandle, 4096, ';');
484 while (($csv_rows = fgetcsv($fhandle, 4096, ";")) !== false) {
485 $user_id = 0;
486 $data = array_combine($fields, $csv_rows);
487 //no check the format - sufficient to import users
488 if (isset($data["Login"])) {
489 $user_id = $this->get_user_id($data["Login"]);
490 }
491 if (isset($data["login"])) {
492 $user_id = $this->get_user_id($data["login"]);
493 }
494 //add mail in future
495 if (isset($data["user"]) && is_numeric($data["user"])) {
496 $user_id = (int) $data["user"];
497 }
498 if ($user_id > 0) {
499 $last_access = ilUtil::now();
500 if (isset($data['Date'])) {
501 $date_ex = explode('.', $data['Date']);
502 $last_access = implode('-', array($date_ex[2], $date_ex[1], $date_ex[0]));
503 }
504 if (isset($data['LastAccess'])) {
505 $last_access = $data['LastAccess'];
506 }
507
509
510 if (isset($data["Status"])) {
511 if (is_numeric($data["Status"])) {
512 $status = $data["Status"];
513 } elseif ($data["Status"] == ilLPStatus::LP_STATUS_NOT_ATTEMPTED) {
515 } elseif ($data["Status"] == ilLPStatus::LP_STATUS_IN_PROGRESS) {
517 } elseif ($data["Status"] == ilLPStatus::LP_STATUS_FAILED) {
519 }
520 }
521 $attempts = null;
522 if (isset($data["Attempts"])) {
523 $attempts = (int) $data["Attempts"];
524 }
525
526 $percentage_completed = 0;
528 $percentage_completed = 100;
529 } elseif (isset($data['percentageCompletedSCOs'])) {
530 $percentage_completed = (int) $data['percentageCompletedSCOs'];
531 }
532
533 $sco_total_time_sec = null;
534 if (isset($data['SumTotal_timeSeconds'])) {
535 $sco_total_time_sec = (int) $data['SumTotal_timeSeconds'];
536 }
537
539 $usersToDelete[] = $user_id;
540 } else {
541 $this->importSuccessForSahsUser($user_id, $last_access, $status, $attempts, $percentage_completed, $sco_total_time_sec);
542 $users[] = $user_id;
543 }
544
546 foreach ($scos as $sco_id) {
547 $res = $ilDB->queryF(
548 '
549 SELECT completion_status, success_status, user_id FROM cmi_node WHERE cp_node_id = %s AND user_id = %s',
550 array('integer','integer'),
551 array($sco_id,$user_id)
552 );
553
554 if (!$ilDB->numRows($res)) {
555 $nextId = $ilDB->nextId('cmi_node');
556 $val_set = $ilDB->manipulateF(
557 'INSERT INTO cmi_node
558 (cp_node_id,user_id,completion_status,c_timestamp,cmi_node_id)
559 VALUES(%s,%s,%s,%s,%s)',
560 array('integer','integer','text','timestamp','integer'),
561 array($sco_id,$user_id,'completed',$last_access,$nextId)
562 );
563 } else {
564 $doUpdate = false;
565 while ($row = $ilDB->fetchAssoc($res)) {
566 if (($row["completion_status"] === "completed" && $row["success_status"] !== "failed") || $row["success_status"] === "passed") {
567 if ($doUpdate != true) {
568 $doUpdate = false;
569 } //note for issue if there are 2 entries for same sco_id
570 } else {
571 $doUpdate = true;
572 }
573 }
574 if ($doUpdate == true) {
575 $ilDB->update(
576 'cmi_node',
577 array(
578 'completion_status' => array('text', 'completed'),
579 'success_status' => array('text', ''),
580 'suspend_data' => array('text', ''),
581 'c_timestamp' => array('timestamp', $last_access)
582 ),
583 array(
584 'user_id' => array('integer', $user_id),
585 'cp_node_id' => array('integer', $sco_id)
586 )
587 );
588 }
589 }
590 }
591 }
592 } else {
593 //echo "Warning! User $csv_rows[0] does not exist in ILIAS. Data for this user was skipped.\n";
594 }
595 }
596
597 if (count($usersToDelete) > 0) {
598 // include_once("./Services/Tracking/classes/class.ilLPMarks.php");
599 // ilLPMarks::_deleteForUsers($this->getId(), $usersToDelete);
600 $this->deleteTrackingDataOfUsers($usersToDelete);
601 }
603
604 return true;
605 }
606
610 public static function _ISODurationToCentisec(string $str): float
611 {
612 $aV = array(0, 0, 0, 0, 0, 0);
613 $bErr = false;
614 $bTFound = false;
615 if (strpos($str, "P") != 0) {
616 $bErr = true;
617 }
618 if (!$bErr) {
619 $aT = array("Y", "M", "D", "H", "M", "S");
620 $p = 0;
621 $i = 0;
622 $str = substr($str, 1);
623 for ($i = 0, $max = count($aT); $i < $max; $i++) {
624 if (strpos($str, "T") === 0) {
625 $str = substr($str, 1);
626 $i = max($i, 3);
627 $bTFound = true;
628 }
629 $p = strpos($str, $aT[$i]);
630
631 if ($p > -1) {
632 if ($i == 1 && strpos($str, "T") > -1 && strpos($str, "T") < $p) {
633 continue;
634 }
635 if ($aT[$i] === "S") {
636 $aV[$i] = substr($str, 0, $p);
637 } else {
638 $aV[$i] = intval(substr($str, 0, $p));
639 }
640 if (!is_numeric($aV[$i])) {
641 $bErr = true;
642 break;
643 }
644
645 if ($i > 2 && !$bTFound) {
646 $bErr = true;
647 break;
648 }
649 $str = substr($str, $p + 1);
650 }
651 }
652 if (!$bErr && strlen($str) != 0) {
653 $bErr = true;
654 }
655 }
656
657 if ($bErr) {
658 return 0;
659 }
660 return $aV[0] * 3_155_760_000 + $aV[1] * 262_980_000 + $aV[2] * 8_640_000 + $aV[3] * 360000 + $aV[4] * 6000 + round($aV[5] * 100);
661 }
662
663 public static function getQuantityOfSCOs(int $a_slm_id): int
664 {
665 global $DIC;
666 $val_set = $DIC->database()->queryF(
667 '
668 SELECT distinct(cp_node.cp_node_id) FROM cp_node,cp_resource,cp_item
669 WHERE cp_item.cp_node_id = cp_node.cp_node_id
670 AND cp_item.resourceid = cp_resource.id
671 AND scormtype = %s
672 AND nodename = %s
673 AND cp_node.slm_id = %s ',
674 array('text','text','integer'),
675 array('sco','item',$a_slm_id)
676 );
677 return $DIC->database()->numRows($val_set);
678 }
679
684 public static function _getCourseCompletionForUser(int $a_id, int $a_user): bool
685 {
686 global $DIC;
687
688 $ilDB = $DIC->database();
689 $ilUser = $DIC->user();
690 $scos = array();
691 //get all SCO's of the object
692
693 $val_set = $ilDB->queryF(
694 '
695 SELECT cp_node.cp_node_id FROM cp_node,cp_resource,cp_item
696 WHERE cp_item.cp_node_id = cp_node.cp_node_id
697 AND cp_item.resourceid = cp_resource.id
698 AND scormtype = %s
699 AND nodename = %s
700 AND cp_node.slm_id = %s',
701 array('text','text','integer'),
702 array('sco' ,'item',$a_id)
703 );
704 while ($val_rec = $ilDB->fetchAssoc($val_set)) {
705 $scos[] = $val_rec['cp_node_id'];
706 }
707
708 $scos_c = $scos;
709 //copy SCO_array
710 //check if all SCO's are completed
711 foreach ($scos as $i => $value) {
712 $val_set = $ilDB->queryF(
713 '
714 SELECT * FROM cmi_node
715 WHERE (user_id= %s
716 AND cp_node_id= %s
717 AND (completion_status = %s OR success_status = %s))',
718 array('integer','integer','text','text'),
719 array($a_user, $value,'completed','passed')
720 );
721
722 if ($ilDB->numRows($val_set) > 0) {
723 //delete from array
724 $key = array_search($value, $scos_c);
725 unset($scos_c[$key]);
726 }
727 }
728 //check for completion
729 if (count($scos_c) == 0) {
730 $completion = true;
731 } else {
732 $completion = false;
733 }
734 return $completion;
735 }
736
742 public static function _getUniqueScaledScoreForUser(int $a_id, int $a_user): float
743 {
744 global $DIC;
745
746 $ilDB = $DIC->database();
747 $ilUser = $DIC->user();
748 $scos = array();
749
750 $val_set = $ilDB->queryF(
751 "SELECT cp_node.cp_node_id FROM cp_node,cp_resource,cp_item WHERE" .
752 " cp_item.cp_node_id=cp_node.cp_node_id AND cp_item.resourceId = cp_resource.id AND scormType='sco' AND nodeName='item' AND cp_node.slm_id = %s GROUP BY cp_node.cp_node_id",
753 array('integer'),
754 array($a_id)
755 );
756 while ($val_rec = $ilDB->fetchAssoc($val_set)) {
757 $scos[] = $val_rec['cp_node_id'];
758 }
759 $set = 0; //numbers of SCO that set cmi.score.scaled
760 $scaled = null;
761 foreach ($scos as $i => $iValue) {
762 $val_set = $ilDB->queryF(
763 "SELECT scaled FROM cmi_node WHERE (user_id = %s AND cp_node_id = %s)",
764 array('integer', 'integer'),
765 array($a_user, $scos[$i])
766 );
767 if ($val_set->numRows() > 0) {
768 $val_rec = $ilDB->fetchAssoc($val_set);
769 if ($val_rec['scaled'] != null) {
770 $set++;
771 $scaled = $val_rec['scaled'];
772 }
773 }
774 }
775 return ($set == 1) ? $scaled : -1;
776 }
777
783 public static function _getTrackingItems(int $a_obj_id): array
784 {
785 global $DIC;
786
787 $ilDB = $DIC->database();
788
789
790 $item_set = $ilDB->queryF(
791 '
792 SELECT cp_item.* FROM cp_node, cp_item WHERE slm_id = %s
793 AND cp_node.cp_node_id = cp_item.cp_node_id
794 ORDER BY cp_node.cp_node_id ',
795 array('integer'),
796 array($a_obj_id)
797 );
798
799 $items = array();
800 while ($item_rec = $ilDB->fetchAssoc($item_set)) {
801 $s2 = $ilDB->queryF(
802 '
803 SELECT cp_resource.* FROM cp_node, cp_resource
804 WHERE slm_id = %s
805 AND cp_node.cp_node_id = cp_resource.cp_node_id
806 AND cp_resource.id = %s ',
807 array('integer','text'),
808 array($a_obj_id,$item_rec["resourceid"])
809 );
810
811
812 if ($res = $ilDB->fetchAssoc($s2)) {
813 if ($res["scormtype"] === "sco") {
814 $items[] = array("id" => $item_rec["cp_node_id"],
815 "title" => $item_rec["title"]);
816 }
817 }
818 }
819
820 return $items;
821 }
822
826 public static function _getStatus(int $a_obj_id, int $a_user_id)
827 {
828 global $DIC;
829
830 $ilDB = $DIC->database();
831
832 $status_set = $ilDB->queryF(
833 '
834 SELECT * FROM cmi_gobjective
835 WHERE scope_id = %s
836 AND objective_id = %s
837 AND user_id = %s',
838 array('integer','text','integer'),
839 array($a_obj_id,'-course_overall_status-',$a_user_id)
840 );
841
842 if ($status_rec = $ilDB->fetchAssoc($status_set)) {
843 return $status_rec["status"];
844 }
845
846 return false;
847 }
848
852 public static function _getSatisfied(int $a_obj_id, int $a_user_id)
853 {
854 global $DIC;
855
856 $ilDB = $DIC->database();
857
858
859 $status_set = $ilDB->queryF(
860 '
861 SELECT * FROM cmi_gobjective
862 WHERE scope_id = %s
863 AND objective_id = %s
864 AND user_id = %s',
865 array('integer','text','integer'),
866 array($a_obj_id,'-course_overall_status-',$a_user_id)
867 );
868
869 if ($status_rec = $ilDB->fetchAssoc($status_set)) {
870 return $status_rec["satisfied"];
871 }
872
873 return false;
874 }
875
879 public static function _getMeasure(int $a_obj_id, int $a_user_id)
880 {
881 global $DIC;
882
883 $ilDB = $DIC->database();
884
885 $status_set = $ilDB->queryF(
886 '
887 SELECT * FROM cmi_gobjective
888 WHERE scope_id = %s
889 AND objective_id = %s
890 AND user_id = %s',
891 array('integer','text','integer'),
892 array($a_obj_id,'-course_overall_status-',$a_user_id)
893 );
894
895 if ($status_rec = $ilDB->fetchAssoc($status_set)) {
896 return (float) $status_rec["measure"];
897 }
898
899 return false;
900 }
901
902 public static function _lookupItemTitle(int $a_node_id): string
903 {
904 global $DIC;
905
906 $ilDB = $DIC->database();
907
908 $r = $ilDB->queryF(
909 '
910 SELECT * FROM cp_item
911 WHERE cp_node_id = %s',
912 array('integer'),
913 array($a_node_id)
914 );
915
916 if ($i = $ilDB->fetchAssoc($r)) {
917 return $i["title"];
918 }
919 return "";
920 }
921
925 public static function _getMaxScoreForUser(int $a_id, int $a_user): ?float
926 {
927 global $DIC;
928
929 $ilDB = $DIC->database();
930
931 $scos = array();
932
933 $result = $ilDB->query(
934 'SELECT cp_node.cp_node_id '
935 . 'FROM cp_node, cp_resource, cp_item '
936 . 'WHERE cp_item.cp_node_id = cp_node.cp_node_id '
937 . 'AND cp_item.resourceId = cp_resource.id '
938 . 'AND scormType = ' . $ilDB->quote('sco', 'text') . ' '
939 . 'AND nodeName = ' . $ilDB->quote('item', 'text') . ' '
940 . 'AND cp_node.slm_id = ' . $ilDB->quote($a_id, 'integer') . ' '
941 . 'GROUP BY cp_node.cp_node_id'
942 );
943
944 while ($row = $ilDB->fetchAssoc($result)) {
945 $scos[] = $row['cp_node_id'];
946 }
947
948 $set = 0; //numbers of SCO that set cmi.score.scaled
949 $max = null;
950 foreach ($scos as $i => $value) {
951 $res = $ilDB->queryF(
952 'SELECT c_max FROM cmi_node WHERE (user_id = %s AND cp_node_id = %s)',
953 array('integer', 'integer'),
954 array($a_user, $value)
955 );
956
957 if ($ilDB->numRows($res) > 0) {
958 $row = $ilDB->fetchAssoc($res);
959 if ($row['c_max'] != null) {
960 $set++;
961 $max = $row['c_max'];
962 }
963 }
964 }
965 return ($set == 1) ? $max : null;
966 }
967
971 public static function _getScores2004ForUser(int $a_cp_node_id, int $a_user): array
972 {
973 global $DIC;
974
975 $ilDB = $DIC->database();
976 $retAr = array("raw" => null, "max" => null, "scaled" => null);
977 $val_set = $ilDB->queryF(
978 "SELECT c_raw, c_max, scaled FROM cmi_node WHERE (user_id = %s AND cp_node_id = %s)",
979 array('integer', 'integer'),
980 array($a_user, $a_cp_node_id)
981 );
982 if ($val_set->numRows() > 0) {
983 $val_rec = $ilDB->fetchAssoc($val_set);
984 $retAr["raw"] = $val_rec['c_raw'];
985 $retAr["max"] = $val_rec['c_max'];
986 $retAr["scaled"] = $val_rec['scaled'];
987 if ($val_rec['scaled'] == null && $val_rec['c_raw'] != null && $val_rec['c_max'] != null) {
988 $retAr["scaled"] = ($val_rec['c_raw'] / $val_rec['c_max']);
989 }
990 }
991 return $retAr;
992 }
993}
$out
Definition: buildRTE.php:24
$check
Definition: buildRTE.php:81
const IL_CAL_DATETIME
error(string $a_errmsg)
static _deleteReadEventsForUsers(int $a_obj_id, array $a_user_ids)
static secondsToString(int $seconds, bool $force_with_seconds=false, ?ilLanguage $a_lng=null)
converts seconds to string: Long: 7 days 4 hour(s) ...
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
@classDescription Date and time handling
static _refreshStatus(int $a_obj_id, ?array $a_users=null)
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)
const LP_STATUS_COMPLETED_NUM
const LP_STATUS_FAILED
const LP_STATUS_IN_PROGRESS_NUM
const LP_STATUS_NOT_ATTEMPTED_NUM
const LP_STATUS_FAILED_NUM
const LP_STATUS_NOT_ATTEMPTED
const LP_STATUS_IN_PROGRESS
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
static getLogger(string $a_component_id)
Get component logger.
getDataDirectory(?string $mode="filesystem")
get data directory of lm
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _ISODurationToCentisec(string $str)
convert ISO 8601 Timeperiods to centiseconds
static _getTrackingItems(int $a_obj_id)
get all tracking items of scorm object currently a for learning progress only
static _getMeasure(int $a_obj_id, int $a_user_id)
getModuleVersionForUser(int $a_user_id)
get module version that tracking data for a user was recorded on
static _getCourseCompletionForUser(int $a_id, int $a_user)
Get the completion of a SCORM module for a given user.
static _getScores2004ForUser(int $a_cp_node_id, int $a_user)
static _getSatisfied(int $a_obj_id, int $a_user_id)
static _getStatus(int $a_obj_id, int $a_user_id)
static _lookupLastAccess(int $a_obj_id, int $a_usr_id)
Return the last access timestamp for a given user.
static _getUniqueScaledScoreForUser(int $a_id, int $a_user)
Get the Unique Scaled Score of a course Conditions: Only one SCO may set cmi.score....
getTrackedItems()
get all tracked items of current user
getTrackingDataAgg(int $a_user_id, ?bool $raw=false)
setImportSequencing(bool $a_val)
Set import sequencing.
__construct(int $a_id=0, bool $a_call_by_reference=true)
Constructor.
static _getMaxScoreForUser(int $a_id, int $a_user)
Returns score.max for the learning module, refered to the last sco where score.max is set.
getAttemptsForUser(int $a_user_id)
get number of atttempts for a certain user and package
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
importSuccessForSahsUser(int $user_id, string $last_access, int $status, ?int $attempts=null, ?int $percentage_completed=null, ?int $sco_total_time_sec=null)
User class.
static getInstance(int $obj_id)
string $title
ilLanguage $lng
ilErrorHandling $error
ilLogger $log
ilDBInterface $db
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static removeCMIDataForUserAndPackage(int $user_id, int $packageId)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static now()
Return current timestamp in Y-m-d H:i:s format.
global $DIC
Definition: feed.php:28
$ilUser
Definition: imgupload.php:34
$res
Definition: ltiservices.php:69
$i
Definition: metadata.php:41
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
string $key
Consumer key/client ID value.
Definition: System.php:193
$ilErr
Definition: raiseError.php:17