ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
class.ilObjSCORMTracking.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
27{
28 public static function storeJsApi(): void
29 {
30 global $DIC;
31 $obj_id = $DIC->http()->wrapper()->query()->retrieve('package_id', $DIC->refinery()->kindlyTo()->int());
32 $refId = $DIC->http()->wrapper()->query()->retrieve('ref_id', $DIC->refinery()->kindlyTo()->int());
33 $in = file_get_contents("php://input");
34 $data = json_decode($in);
35 $user_id = (int) $data->p;
36
37 header('Content-Type: text/plain; charset=UTF-8');
38
39 $rval = self::storeJsApiCmi($user_id, $obj_id, $data);
40 if ($rval != true) {
41 print("storeJsApiCmi failed");
42 } else {
43 $rval = self::syncGlobalStatus($user_id, $obj_id, $refId, $data, $data->now_global_status);
44 if ($rval != true) {
45 print("syncGlobalStatus failed");
46 }
47 }
48 if ($rval == true) {
49 print("ok");
50 }
51 }
52
53 public static function storeJsApiCmi(int $user_id, int $obj_id, object $data): bool
54 {
55 global $DIC;
56 $ilLog = ilLoggerFactory::getLogger('sahs');
57 $ilDB = $DIC->database();
58
59 $b_updateStatus = false;
60 $i_score_max = 0;
61 $i_score_raw = 0;
62
63 $aa_data = array();
64 // if (is_array($_POST["S"])) {
65 // foreach($_POST["S"] as $key => $value) {
66 // $aa_data[] = array("sco_id" => $value, "left" => $_POST["L"][$key], "right" => $_POST["R"][$key]);
67 // }
68 // }
69 foreach ($data->cmi as $value) {
70 $aa_data[] = array("sco_id" => (int) $value[0],
71 "left" => $value[1],
72 "right" => $value[2]
73 );
74 // $aa_data[] = array("sco_id" => (int) $data->cmi[$i][0], "left" => $data->cmi[$i][1], "right" => rawurldecode($data->cmi[$i][2]));
75 }
76
77 if ($obj_id <= 1) {
78 $ilLog->write("ScormAicc: storeJsApi: Error: No valid obj_id given.");
79 } else {
80 foreach ($aa_data as $a_data) {
81 $set = $ilDB->queryF(
82 '
83 SELECT rvalue FROM scorm_tracking
84 WHERE user_id = %s
85 AND sco_id = %s
86 AND lvalue = %s
87 AND obj_id = %s',
88 array('integer', 'integer', 'text', 'integer'),
89 array($user_id, $a_data["sco_id"], $a_data["left"], $obj_id)
90 );
91 if ($rec = $ilDB->fetchAssoc($set)) {
92 if ($a_data["left"] === 'cmi.core.lesson_status' && $a_data["right"] != $rec["rvalue"]) {
93 $b_updateStatus = true;
94 }
95 $ilDB->update(
96 'scorm_tracking',
97 array(
98 'rvalue' => array('clob', $a_data["right"]),
99 'c_timestamp' => array('timestamp', ilUtil::now())
100 ),
101 array(
102 'user_id' => array('integer', $user_id),
103 'sco_id' => array('integer', $a_data["sco_id"]),
104 'lvalue' => array('text', $a_data["left"]),
105 'obj_id' => array('integer', $obj_id)
106 )
107 );
108 $ilLog->debug("ScormAicc: storeJsApi Updated - L:" . $a_data["left"] . ",R:" .
109 $a_data["right"] . " for obj_id:" . $obj_id . ",sco_id:" . $a_data["sco_id"] . ",user_id:" . $user_id);
110 } else {
111 if ($a_data["left"] === 'cmi.core.lesson_status') {
112 $b_updateStatus = true;
113 }
114 $ilDB->insert('scorm_tracking', array(
115 'obj_id' => array('integer', $obj_id),
116 'user_id' => array('integer', $user_id),
117 'sco_id' => array('integer', $a_data["sco_id"]),
118 'lvalue' => array('text', $a_data["left"]),
119 'rvalue' => array('clob', $a_data["right"]),
120 'c_timestamp' => array('timestamp', ilUtil::now())
121 ));
122 $ilLog->debug("ScormAicc: storeJsApi Inserted - L:" . $a_data["left"] . ",R:" .
123 $a_data["right"] . " for obj_id:" . $obj_id . ",sco_id:" . $a_data["sco_id"] . ",user_id:" . $user_id);
124 }
125 if ($a_data["left"] === 'cmi.core.score.max') {
126 $i_score_max = $a_data["right"];
127 }
128 if ($a_data["left"] === 'cmi.core.score.raw') {
129 $i_score_raw = $a_data["right"];
130 }
131 }
132 // mantis #30293
133 if ($i_score_max > 0 && $i_score_raw > 0) {
134 if (count(ilSCORMObject::_lookupPresentableItems($obj_id)) == 1) {
136 $obj_id,
137 $user_id,
138 ($i_score_raw / $i_score_max) * 100
139 );
140 }
141 }
142 }
143
144 // update status
145 if ($b_updateStatus === true) {
147 }
148
149 return true;
150 }
151
152 public static function syncGlobalStatus(int $userId, int $packageId, int $refId, object $data, ?int $new_global_status): bool
153 {
154 global $DIC;
155 $ilDB = $DIC->database();
156 $ilLog = ilLoggerFactory::getLogger('sahs');
157 $saved_global_status = $data->saved_global_status;
158 $ilLog->write("saved_global_status=" . $saved_global_status);
159
160 // get attempts
161 if (!isset($data->packageAttempts)) {
162 $val_set = $ilDB->queryF(
163 'SELECT package_attempts FROM sahs_user WHERE obj_id = %s AND user_id = %s',
164 array('integer', 'integer'),
165 array($packageId, $userId)
166 );
167 $val_rec = $ilDB->fetchAssoc($val_set);
168 $attempts = $val_rec["package_attempts"];
169 } else {
170 $attempts = $data->packageAttempts;
171 }
172 if ($attempts == null) {
173 $attempts = 1;
174 }
175
176 //update percentage_completed, sco_total_time_sec,status in sahs_user
177 $totalTime = (int) $data->totalTimeCentisec;
178 $totalTime = round($totalTime / 100);
179 $ilDB->queryF(
180 'UPDATE sahs_user SET last_visited=%s, last_access = %s, sco_total_time_sec=%s, status=%s, percentage_completed=%s, package_attempts=%s WHERE obj_id = %s AND user_id = %s',
181 array('text', 'timestamp', 'integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
182 array($data->last_visited,
183 date('Y-m-d H:i:s'),
184 $totalTime,
185 $new_global_status,
186 $data->percentageCompleted,
187 $attempts,
189 $userId
190 )
191 );
192
193 // self::ensureObjectDataCacheExistence();
194 global $DIC;
195 $ilObjDataCache = $DIC['ilObjDataCache'];
196 //workaround if $row->read_count == null TODO ERASE
197 try {
199 "sahs",
200 $refId,
202 $userId,
203 $attempts,
204 $totalTime
205 );
206 } catch (\Exception $exception) {
208 "sahs",
209 $refId,
211 $userId,
212 null,
213 $totalTime
214 );
215 }
216
217 //end sync access number and time in read event table
218
219 // update learning progress
220 if ($new_global_status !== null) {
222
223 // here put code for soap to MaxCMS e.g. when if($saved_global_status != $new_global_status)
224 }
225 return true;
226 }
227
228 public static function _insertTrackData(int $a_sahs_id, string $a_lval, string $a_rval, int $a_obj_id): void
229 {
230 global $DIC;
231 $ilDB = $DIC->database();
232 $ilUser = $DIC->user();
233
234 $ilDB->insert('scorm_tracking', array(
235 'obj_id' => array('integer', $a_obj_id),
236 'user_id' => array('integer', $ilUser->getId()),
237 'sco_id' => array('integer', $a_sahs_id),
238 'lvalue' => array('text', $a_lval),
239 'rvalue' => array('clob', $a_rval),
240 'c_timestamp' => array('timestamp', ilUtil::now())
241 ));
242
243 if ($a_lval === "cmi.core.lesson_status") {
244 ilLPStatusWrapper::_updateStatus($a_obj_id, $ilUser->getId());
245 }
246 }
247
248 //erase later see ilSCORM2004StoreData
252 public static function _getCompleted(object $scorm_item_id, int $a_obj_id): array
253 {
254 global $DIC;
255 $ilDB = $DIC->database();
256
257 $user_ids = [];
258
259 if (is_array($scorm_item_id)) {
260 $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
261
262 $res = $ilDB->queryF(
263 'SELECT DISTINCT(user_id) FROM scorm_tracking
264 WHERE ' . $in . '
265 AND obj_id = %s
266 AND lvalue = %s
267 AND (' . $ilDB->like('rvalue', 'clob', 'completed') . ' OR ' . $ilDB->like(
268 'rvalue',
269 'clob',
270 'passed'
271 ) . ')',
272 array('integer', 'text'),
273 array($a_obj_id, 'cmi.core.lesson_status')
274 );
275 } else {
276 $res = $ilDB->queryF(
277 'SELECT DISTINCT(user_id) FROM scorm_tracking
278 WHERE sco_id = %s
279 AND obj_id = %s
280 AND lvalue = %s
281 AND (' . $ilDB->like('rvalue', 'clob', 'completed') . ' OR ' . $ilDB->like(
282 'rvalue',
283 'clob',
284 'passed'
285 ) . ')',
286 array('integer', 'integer', 'text'),
287 array($scorm_item_id, $a_obj_id, 'cmi.core.lesson_status')
288 );
289 }
290
291 while ($row = $ilDB->fetchObject($res)) {
292 $user_ids[] = $row->user_id;
293 }
294 return $user_ids;
295 }
296
297 public static function _getCollectionStatus(?array $a_scos, int $a_obj_id, int $a_user_id): string
298 {
299 global $DIC;
300 $ilDB = $DIC->database();
301
302 $status = "not_attempted";
303
304 if (is_array($a_scos)) {
305 $in = $ilDB->in('sco_id', $a_scos, false, 'integer');
306
307 $res = $ilDB->queryF(
308 'SELECT sco_id, rvalue FROM scorm_tracking
309 WHERE ' . $in . '
310 AND obj_id = %s
311 AND lvalue = %s
312 AND user_id = %s',
313 array('integer', 'text', 'integer'),
314 array($a_obj_id, 'cmi.core.lesson_status', $a_user_id)
315 );
316
317 $cnt = 0;
318 $completed = true;
319 $failed = false;
320 while ($rec = $ilDB->fetchAssoc($res)) {
321 if ($rec["rvalue"] === "failed") {
322 $failed = true;
323 }
324 if ($rec["rvalue"] !== "completed" && $rec["rvalue"] !== "passed") {
325 $completed = false;
326 }
327 $cnt++;
328 }
329 if ($cnt > 0) {
330 $status = "in_progress";
331 }
332 if ($completed && $cnt == count($a_scos)) {
333 $status = "completed";
334 }
335 if ($failed) {
336 $status = "failed";
337 }
338 }
339 return $status;
340 }
341
342 public static function _countCompleted(?array $a_scos, int $a_obj_id, int $a_user_id): int
343 {
344 global $DIC;
345 $ilDB = $DIC->database();
346 $cnt = 0;
347
348 if (is_array($a_scos)) {
349 $in = $ilDB->in('sco_id', $a_scos, false, 'integer');
350
351 $res = $ilDB->queryF(
352 'SELECT sco_id, rvalue FROM scorm_tracking
353 WHERE ' . $in . '
354 AND obj_id = %s
355 AND lvalue = %s
356 AND user_id = %s',
357 array('integer', 'text', 'integer'),
358 array($a_obj_id, 'cmi.core.lesson_status', $a_user_id)
359 );
360
361 while ($rec = $ilDB->fetchAssoc($res)) {
362 if ($rec["rvalue"] === "completed" || $rec["rvalue"] === "passed") {
363 $cnt++;
364 }
365 }
366 }
367 return $cnt;
368 }
369
374 public static function lookupLastAccessTimes(int $a_obj_id): array
375 {
376 global $DIC;
377 $ilDB = $DIC->database();
378 $users = array();
379
380 $query = 'SELECT user_id, MAX(c_timestamp) tst ' .
381 'FROM scorm_tracking ' .
382 'WHERE obj_id = ' . $ilDB->quote($a_obj_id, 'integer') . ' ' .
383 'GROUP BY user_id';
384 $res = $ilDB->query($query);
385
386 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
387 $users[$row->user_id] = $row->tst;
388 }
389 return $users;
390 }
391
395 public static function _getTrackedUsers(int $a_obj_id): array
396 {
397 global $DIC;
398 $ilDB = $DIC->database();
399 // $ilLog = ilLoggerFactory::getLogger('sahs');
400
401 $res = $ilDB->queryF(
402 'SELECT DISTINCT user_id FROM scorm_tracking
403 WHERE obj_id = %s
404 AND lvalue = %s',
405 array('integer', 'text'),
406 array($a_obj_id, 'cmi.core.lesson_status')
407 );
408
409 $users = array();
410 while ($row = $ilDB->fetchAssoc($res)) {
411 $users[] = $row["user_id"];
412 }
413 return $users;
414 }
415
419 public static function _getFailed(object $scorm_item_id, int $a_obj_id): array
420 {
421 global $DIC;
422 $ilDB = $DIC->database();
423 $user_ids = [];
424
425 if (is_array($scorm_item_id)) {
426 $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
427
428 $res = $ilDB->queryF(
429 '
430 SELECT DISTINCT(user_id) FROM scorm_tracking
431 WHERE ' . $in . '
432 AND obj_id = %s
433 AND lvalue = %s
434 AND ' . $ilDB->like('rvalue', 'clob', 'failed') . ' ',
435 array('integer', 'text'),
436 array($a_obj_id, 'cmi.core.lesson_status')
437 );
438 } else {
439 $res = $ilDB->queryF(
440 '
441 SELECT DISTINCT(user_id) FROM scorm_tracking
442 WHERE sco_id = %s
443 AND obj_id = %s
444 AND lvalue = %s
445 AND ' . $ilDB->like('rvalue', 'clob', 'failed') . ' ',
446 array('integer', 'integer', 'text'),
447 array($scorm_item_id, $a_obj_id, 'cmi.core.lesson_status')
448 );
449 }
450
451 while ($row = $ilDB->fetchObject($res)) {
452 $user_ids[] = $row->user_id;
453 }
454 return $user_ids;
455 }
456
461 public static function _getCountCompletedPerUser(array $a_scorm_item_ids, int $a_obj_id): array
462 {
463 global $DIC;
464 $ilDB = $DIC->database();
465 $users = [];
466
467 $in = $ilDB->in('sco_id', $a_scorm_item_ids, false, 'integer');
468
469 // Why does this query use a like search against "passed" and "failed"
470 //because it's clob and we support Oracle
471 $res = $ilDB->queryF(
472 '
473 SELECT user_id, COUNT(user_id) completed FROM scorm_tracking
474 WHERE ' . $in . '
475 AND obj_id = %s
476 AND lvalue = %s
477 AND (' . $ilDB->like('rvalue', 'clob', 'completed') . ' OR ' . $ilDB->like('rvalue', 'clob', 'passed') . ')
478 GROUP BY user_id',
479 array('integer', 'text'),
480 array($a_obj_id, 'cmi.core.lesson_status')
481 );
482 while ($row = $ilDB->fetchObject($res)) {
483 $users[$row->user_id] = $row->completed;
484 }
485 return $users;
486 }
487 //not correct because of assets!
492 public static function _getProgressInfo(array $sco_item_ids, int $a_obj_id): array
493 {
494 global $DIC;
495 $ilDB = $DIC->database();
496
497 $in = $ilDB->in('sco_id', $sco_item_ids, false, 'integer');
498
499 $res = $ilDB->queryF(
500 '
501 SELECT * FROM scorm_tracking
502 WHERE ' . $in . '
503 AND obj_id = %s
504 AND lvalue = %s ',
505 array('integer', 'text'),
506 array($a_obj_id, 'cmi.core.lesson_status')
507 );
508
509 $info['completed'] = array();
510 $info['failed'] = array();
511
512 $user_ids = array();
513 while ($row = $ilDB->fetchObject($res)) {
514 switch ($row->rvalue) {
515 case 'completed':
516 case 'passed':
517 $info['completed'][$row->sco_id][] = $row->user_id;
518 $user_ids[$row->sco_id][] = $row->user_id;
519 break;
520
521 case 'failed':
522 $info['failed'][$row->sco_id][] = $row->user_id;
523 $user_ids[$row->sco_id][] = $row->user_id;
524 break;
525 }
526 }
527 $info['in_progress'] = ilObjSCORMTracking::_getInProgress($sco_item_ids, $a_obj_id, $user_ids);
528
529 return $info;
530 }
531
535 public static function _getInProgress(array|int $scorm_item_id, int $a_obj_id, ?array $a_blocked_user_ids = null): array
536 {
537 global $DIC;
538 $ilDB = $DIC->database();
539
540 if (is_array($scorm_item_id)) {
541 $in = $ilDB->in('sco_id', $scorm_item_id, false, 'integer');
542
543 $res = $ilDB->queryF(
544 'SELECT user_id,sco_id FROM scorm_tracking
545 WHERE ' . $in . '
546 AND obj_id = %s
547 GROUP BY user_id, sco_id',
548 array('integer'),
549 array($a_obj_id)
550 );
551 } else {
552 $res = $ilDB->queryF(
553 'SELECT user_id,sco_id FROM scorm_tracking
554 WHERE sco_id = %s
555 AND obj_id = %s',
556 array('integer', 'integer'),
557 array($scorm_item_id, $a_obj_id)
558 );
559 }
560
561 $in_progress = array();
562
563 while ($row = $ilDB->fetchObject($res)) {
564 // #15061 - see _getProgressInfo()
565 if (!($a_blocked_user_ids &&
566 isset($a_blocked_user_ids[$row->sco_id]) &&
567 is_array($a_blocked_user_ids[$row->sco_id]) &&
568 in_array($row->user_id, $a_blocked_user_ids[$row->sco_id]))) {
569 $in_progress[$row->sco_id][] = $row->user_id;
570 }
571 }
572 return $in_progress;
573 }
574
575 public static function scorm12PlayerUnload(): void
576 {
577 global $DIC;
578 $ilDB = $DIC->database();
579 $user_id = $DIC->http()->wrapper()->query()->retrieve('p', $DIC->refinery()->kindlyTo()->int());
580 $ref_id = $DIC->http()->wrapper()->query()->retrieve('ref_id', $DIC->refinery()->kindlyTo()->int());
581 $obj_id = $DIC->http()->wrapper()->query()->retrieve('package_id', $DIC->refinery()->kindlyTo()->int());
582 if ($obj_id <= 1) {
583 $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ' no valid obj_id');
584 } else {
585 $last_visited = "";
586 if ($DIC->http()->wrapper()->query()->has('last_visited')) {
587 $last_visited = $DIC->http()->wrapper()->query()->retrieve('last_visited', $DIC->refinery()->kindlyTo()->string());
588 }
589
590 $endDate = date(
591 'Y-m-d H:i:s',
592 mktime((int) date('H'), (int) date('i') + 5, (int) date('s'), (int) date('m'), (int) date('d'), (int) date('Y'))
593 );
594 $ilDB->manipulateF(
595 'UPDATE sahs_user
596 SET last_visited = %s, hash_end =%s, last_access = %s
597 WHERE obj_id = %s AND user_id = %s',
598 array('text', 'timestamp', 'timestamp', 'integer', 'integer'),
599 array($last_visited, $endDate, date('Y-m-d H:i:s'), $obj_id, $user_id)
600 );
601 // update time and numbers of attempts in change event
602 //NOTE: here it is correct (not count of commit with changed values); be careful to performance issues
604 }
605 header('Content-Type: text/plain; charset=UTF-8');
606 print("");
607 }
608
609 public static function checkIfAllowed(int $packageId, int $userId, int $hash): void
610 {
611 global $DIC;
612 $ilDB = $DIC->database();
613 $res = $ilDB->queryF(
614 'select hash from sahs_user where obj_id=%s AND user_id=%s AND hash_end>%s',
615 array('integer', 'integer', 'timestamp'),
616 array($packageId, $userId, date('Y-m-d H:i:s'))
617 );
618 $rowtmp = $ilDB->fetchAssoc($res);
619 if (! ($rowtmp && $rowtmp['hash'] == $hash)) {
620 //output used by api
621 die("not allowed");
622 }
623 }
624
625 public static function _syncReadEvent(int $a_obj_id, int $a_user_id, string $a_type, int $a_ref_id): void
626 {
627 global $DIC;
628 $ilDB = $DIC->database();
629 $ilLog = ilLoggerFactory::getLogger('sahs');
630 $val_set = $ilDB->queryF(
631 'SELECT package_attempts, total_time_sec, sco_total_time_sec, time_from_lms FROM sahs_user, sahs_lm '
632 . 'WHERE sahs_user.obj_id = %s AND sahs_user.user_id = %s AND sahs_user.obj_id = sahs_lm.id',
633 array('integer', 'integer'),
634 array($a_obj_id, $a_user_id)
635 );
636
637 $val_rec = $ilDB->fetchAssoc($val_set);
638
639 if ($val_rec["package_attempts"] == null) {
640 $val_rec["package_attempts"] = "";
641 }
642 $attempts = $val_rec["package_attempts"];
643
644 $time = (int) $val_rec["sco_total_time_sec"];
645
646 // get learning time for old ILIAS-Versions
647 if ($time == 0) {
648 $sco_set = $ilDB->queryF(
649 '
650 SELECT sco_id, rvalue FROM scorm_tracking
651 WHERE obj_id = %s
652 AND user_id = %s
653 AND lvalue = %s
654 AND sco_id <> %s',
655 array('integer', 'integer', 'text', 'integer'),
656 array($a_obj_id, $a_user_id, 'cmi.core.total_time', 0)
657 );
658
659 while ($sco_rec = $ilDB->fetchAssoc($sco_set)) {
660 $tarr = explode(":", $sco_rec["rvalue"]);
661 $sec = (int) $tarr[2] + (int) $tarr[1] * 60 +
662 (int) substr($tarr[0], strlen($tarr[0]) - 3) * 60 * 60;
663 $time += $sec;
664 }
665 }
666 ilChangeEvent::_recordReadEvent($a_type, $a_ref_id, $a_obj_id, $a_user_id, $attempts, $time);
667 }
668} // END class.ilObjSCORMTracking
static _recordReadEvent(string $a_type, int $a_ref_id, int $obj_id, int $usr_id, $a_ext_rc=null, $a_ext_time=null)
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)
static handleOutcomeWithoutLP(int $a_obj_id, int $a_usr_id, ?float $a_percentage)
static getLogger(string $a_component_id)
Get component logger.
Class ilObjSCORMTracking.
static syncGlobalStatus(int $userId, int $packageId, int $refId, object $data, ?int $new_global_status)
static _getProgressInfo(array $sco_item_ids, int $a_obj_id)
Get info about.
static _syncReadEvent(int $a_obj_id, int $a_user_id, string $a_type, int $a_ref_id)
static _getCountCompletedPerUser(array $a_scorm_item_ids, int $a_obj_id)
Get users who have status completed or passed.
static _insertTrackData(int $a_sahs_id, string $a_lval, string $a_rval, int $a_obj_id)
static _getTrackedUsers(int $a_obj_id)
Get all tracked users.
static _getCollectionStatus(?array $a_scos, int $a_obj_id, int $a_user_id)
static checkIfAllowed(int $packageId, int $userId, int $hash)
static _getFailed(object $scorm_item_id, int $a_obj_id)
like necessary because of Oracle
static _getCompleted(object $scorm_item_id, int $a_obj_id)
like necessary because of Oracle
static _getInProgress(array|int $scorm_item_id, int $a_obj_id, ?array $a_blocked_user_ids=null)
static lookupLastAccessTimes(int $a_obj_id)
Lookup last acccess time for all users of a scorm module.
static storeJsApiCmi(int $user_id, int $obj_id, object $data)
static _countCompleted(?array $a_scos, int $a_obj_id, int $a_user_id)
static _lookupPresentableItems(int $a_slm_id)
Count number of presentable SCOs/Assets of SCORM learning module.
static now()
Return current timestamp in Y-m-d H:i:s format.
$info
Definition: entry_point.php:21
$ref_id
Definition: ltiauth.php:66
$res
Definition: ltiservices.php:69
global $DIC
Definition: shib_login.php:26
$packageId
Definition: storeScorm.php:27
$GLOBALS["DIC"]
Definition: wac.php:54
$refId
Definition: xapitoken.php:56