ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilSCORM13PlayerGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22use JetBrains\PhpStorm\NoReturn;
23
30{
31 public const ENABLE_GZIP = 0;
32
33 public const NONE = 0;
34 public const READONLY = 1;
35 public const WRITEONLY = 2;
36 public const READWRITE = 3;
37
41 private static array $schema = array // order of entries matters!
42 (
43 'package' => array(
44 'user_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'user_id'),
45 'learner_name' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'learner_name'),
46 'slm_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'slm_id'),
47 'mode' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'c_mode'),
48 'credit' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'credit'),
49 ),
50 'node' => array(
51 'accesscount' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'accesscount'),
52 'accessduration' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'accessduration'),
53 'accessed' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'accessed'),
54 'activityAbsoluteDuration' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'activityabsduration'),
55 'activityAttemptCount' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'activityattemptcount'),
56 'activityExperiencedDuration' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'activityexpduration'),
57 'activityProgressStatus' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'activityprogstatus'),
58 'attemptAbsoluteDuration' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'attemptabsduration'),
59 'attemptCompletionAmount' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'attemptcomplamount'),
60 'attemptCompletionStatus' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'attemptcomplstatus'),
61 'attemptExperiencedDuration' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'attemptexpduration'),
62 'attemptProgressStatus' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'attemptprogstatus'),
63 'audio_captioning' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'audio_captioning'),
64 'audio_level' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'audio_level'),
65 'availableChildren' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'availablechildren'),
66 'cmi_node_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_node_id'),
67 'completion' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'completion'),
68 'completion_status' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'completion_status'),
69 'completion_threshold' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'completion_threshold'),
70 'cp_node_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cp_node_id'),
71 'created' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'created'),
72 'credit' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'credit'),
73 'delivery_speed' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'delivery_speed'),
74 'entry' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_entry'),
75 'exit' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_exit'),
76 'language' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_language'),
77 'launch_data' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'launch_data'),
78 'learner_name' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'learner_name'),
79 'location' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'location'),
80 'max' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_max'),
81 'min' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_min'),
82 'mode' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_mode'),
83 'modified' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'modified'),
84 'progress_measure' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'progress_measure'),
85 'raw' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_raw'),
86 'scaled' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'scaled'),
87 'scaled_passing_score' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'scaled_passing_score'),
88 'session_time' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'session_time'),
89 'success_status' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'success_status'),
90 'suspend_data' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'suspend_data'),
91 'total_time' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'total_time'),
92 'user_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'user_id'),
93 ),
94 'comment' => array(
95 'cmi_comment_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_comment_id'),
96 'cmi_node_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_node_id'),
97 'comment' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_comment'),
98 'timestamp' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_timestamp'),
99 'location' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'location'),
100 'sourceIsLMS' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'sourceislms'),
101 ),
102 'correct_response' => array(
103 'cmi_correct_response_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_correct_resp_id'),
104 'cmi_interaction_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_interaction_id'),
105 'pattern' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'pattern'),
106 ),
107 'interaction' => array(
108 'cmi_interaction_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_interaction_id'),
109 'cmi_node_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_node_id'),
110 'description' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'description'),
111 'id' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'id'),
112 'latency' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'latency'),
113 'learner_response' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'learner_response'),
114 'result' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'result'),
115 'timestamp' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_timestamp'),
116 'type' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_type'),
117 'weighting' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'weighting'),
118 ),
119 'objective' => array(
120 'cmi_interaction_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_interaction_id'),
121 'cmi_node_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_node_id'),
122 'cmi_objective_id' => array('pattern' => null, 'permission' => self::NONE, 'default' => null, 'dbfield' => 'cmi_objective_id'),
123 'completion_status' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'completion_status'),
124 'description' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'description'),
125 'id' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'id'),
126 'max' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_max'),
127 'min' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_min'),
128 'raw' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'c_raw'),
129 'scaled' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'scaled'),
130 'progress_measure' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'progress_measure'),
131 'success_status' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'success_status'),
132 'scope' => array('pattern' => null, 'permission' => self::READWRITE, 'default' => null, 'dbfield' => 'scope'),
133 ),
134 );
135
136 private int $userId;
137 private array $flat_structure;
138 public int $packageId;
139 public bool $jsMode;
140
143 public int $ref_id;
144 public ilCtrl $ctrl;
145 protected ilLanguage $lng;
146 protected string $page = "";
147
148 public function __construct()
149 {
150 global $DIC;
151 $this->ctrl = $DIC->ctrl();
152 $this->userId = $DIC->user()->getId();
153 $this->lng = $DIC->language();
154
155 // $this->packageId = (int) $_REQUEST['packageId'];
156 $this->jsMode = strpos($_SERVER['HTTP_ACCEPT'], 'text/javascript') !== false;
157
158 if ($DIC->http()->wrapper()->query()->has('page')) {
159 $this->page = $DIC->http()->wrapper()->query()->retrieve('page', $DIC->refinery()->kindlyTo()->string());
160 }
161
162 $this->ref_id = $DIC->http()->wrapper()->query()->retrieve('ref_id', $DIC->refinery()->kindlyTo()->int());
163 $this->slm = new ilObjSCORM2004LearningModule($this->ref_id, true);
164
165 $this->packageId = ilObject::_lookupObjectId($this->ref_id);
166 }
167
171 public function executeCommand(): void
172 {
173 global $DIC;
174 $ilAccess = $DIC->access();
175 $lng = $DIC->language();
176 $ilErr = $DIC['ilErr'];
177
178 $next_class = $this->ctrl->getNextClass($this);
179 $cmd = $this->ctrl->getCmd();
180
181 if (!$ilAccess->checkAccess("read", "", $this->ref_id)) {
182 $ilErr->raiseError($lng->txt("permission_denied"), $ilErr->WARNING);
183 }
184
185 $nodeId = 0;
186 if ($DIC->http()->wrapper()->query()->has('node_id')) {
187 $nodeId = $DIC->http()->wrapper()->query()->retrieve('node_id', $DIC->refinery()->kindlyTo()->int());
188 }
189
190
191 //$ilLog->write("SCORM2004 Player cmd: ".$cmd);
192
193 switch ($cmd) {
194 case 'getRTEjs':
195 $this->getRTEjs();
196 break;
197
198 case 'cp':
199 $this->getCPData();
200 break;
201
202 case 'adlact':
203 $this->getADLActData();
204 break;
205
206 case 'suspend':
207 $this->suspendADLActData();
208 break;
209
210 case 'getSuspend':
211 $this->getSuspendData();
212 break;
213
214 case 'gobjective':
215 // $this->writeGObjective();
216 break;
217
218 case 'getGobjective':
219 $this->readGObjective();
220 break;
221
222 case 'getSharedData':
223 $this->readSharedData($nodeId);
224 break;
225
226 case 'setSharedData':
227 $this->writeSharedData($nodeId);
228 break;
229
230 case 'cmi':
231 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
233 $this->packageId,
234 $this->ref_id,
235 $this->slm->getDefaultLessonMode(),
236 $this->slm->getComments(),
237 $this->slm->getInteractions(),
238 $this->slm->getObjectives(),
239 $this->slm->getTime_from_lms(),
240 null,
241 $this->userId
242 );
243 //error_log("Saved CMI Data");
244 } else {
245 $this->fetchCMIData();
246 }
247 break;
248
249 case 'specialPage':
250 $this->specialPage();
251 break;
252
253 case 'debugGUI':
254 $this->debugGUI();
255 break;
256
257 case 'postLogEntry':
258 $this->postLogEntry();
259 break;
260
261 case 'liveLogContent':
262 $this->liveLogContent();
263 break;
264 case 'downloadLog':
265 $this->downloadLog();
266 break;
267
268 case 'openLog':
269 $this->openLog();
270 break;
271
272 case 'pingSession':
273 $this->pingSession();
274 break;
275
276 case 'scormPlayerUnload':
277 ilSCORM2004StoreData::scormPlayerUnload($this->packageId, $this->ref_id, $this->slm->getTime_from_lms(), $this->userId);
278 break;
279
280 // case 'getConfigForPlayer':
281 // $this->getConfigForPlayer();
282 // break;
283
284 default:
285 $this->getPlayer();
286 break;
287 }
288 }
289
290 public function getRTEjs(): void
291 {
292 $js_data = file_get_contents("../components/ILIAS/Scorm2004/scripts/buildrte/rte.js");
293 if (self::ENABLE_GZIP == 1) {
294 ob_start("ob_gzhandler");
295 }
296 header('Content-Type: text/javascript; charset=UTF-8');
297 echo $js_data;
298 }
299
300
301 public function getDataDirectory(): string
302 {
303 $webdir = str_replace("/ilias.php", "", $_SERVER["SCRIPT_NAME"]);
304 //load ressources always with absolute URL..relative URLS fail on innersco navigation on certain browsers
305 $lm_dir = $webdir . "/" . ILIAS_WEB_DIR . "/" . CLIENT_ID . "/lm_data" . "/lm_" . (string) $this->packageId;
306 return $lm_dir;
307 }
308
309 //config data also used for SOP
313 public function getConfigForPlayer(): array
314 {
315 global $DIC;
316 $ilUser = $DIC->user();
317
318 $initSuspendData = null;
319 $config = array(
320 'scope' => $this->getScope(),
321 'learner_id' => (string) $ilUser->getID(),
322 'cmi_learner_id' => (string) $this->slm->getApiStudentId(),
323 'course_id' => (string) $this->packageId,
324 'learner_name' => (string) $this->slm->getApiStudentName(),
325 'mode' => $this->slm->getDefaultLessonMode(),
326 'credit' => $this->slm->getCreditMode(),
327 'auto_review' => $this->slm->getAutoReviewChar(),
328 'hide_navig' => $this->slm->getHideNavig(),
329 'hide_menu' => $this->slm->getNoMenu(),
330 'ie_force_render' => $this->slm->getIe_force_render(),
331 'fourth_edition' => $this->slm->getFourth_edition(),
332 'sequencing_enabled' => $this->slm->getSequencing(),
333 'interactions_storable' => $this->slm->getInteractions(),
334 'objectives_storable' => $this->slm->getObjectives(),
335 'comments_storable' => $this->slm->getComments(),
336 'time_from_lms' => $this->slm->getTime_from_lms(),
337 'auto_last_visited' => $this->slm->getAuto_last_visited(),
338 'lesson_mastery_score' => $this->slm->getMasteryScore(),
339 'checkSetValues' => $this->slm->getCheck_values(),
340 'auto_suspend' => $this->slm->getAutoSuspend(),
341 'suspend_data' => $initSuspendData,
342 'cp_data' => null,
343 'cmi_data' => null,
344 'adlact_data' => null,
345 'globalobj_data' => null
346 );
347 $config['status'] = ilObjSCORMInitData::getStatus($this->packageId, $ilUser->getID(), $this->slm->getAuto_last_visited(), "2004");
348 // $status['last_visited']=null;
349 // if($this->slm->getAuto_last_visited())
350 // {
351 // $status['last_visited']=$this->get_last_visited($this->packageId, $ilUser->getID());
352 // }
353 // $config['status'] = $status;
354
355 return $config;
356 }
357
362 public function getPlayer(): void
363 {
364 global $DIC;
365 $lng = $DIC->language();
366 $ilSetting = $DIC->settings();
367 ilWACSignedPath::signFolderOfStartFile($this->getDataDirectory() . '/imsmanifest.xml');
368
369 // player basic config data
370
371 $initSuspendData = null;
372 $initAdlactData = null;
373 $initGlobalobjData = null;
374 if ($this->slm->getSequencing() == true) {
375 $initSuspendData = json_decode($this->getSuspendDataInit());
376 $initAdlactData = json_decode($this->getADLActDataInit());
377 $initGlobalobjData = $this->readGObjectiveInit();
378 }
379
380 $config = $this->getConfigForPlayer();
381
382 //session
383 if ($this->slm->getSession()) {
385 $max_idle = (int) ilSession::getIdleValue();
386 if ($session_timeout > $max_idle) {
387 $session_timeout = $max_idle;
388 }
389 $min_idle = ilSessionControl::DEFAULT_MIN_IDLE * 60;
390 if ($session_timeout > $min_idle) {
391 $session_timeout = $min_idle;
392 }
393 $session_timeout -= 10; //buffer
394 } else {
395 $session_timeout = 0;
396 }
397 $config['session_ping'] = $session_timeout;
398
399 //url strings
400 $store_url = $DIC->ctrl()->getLinkTarget($this, 'cmi');
401 $unload_url = $DIC->ctrl()->getLinkTarget($this, 'scormPlayerUnload');
402 if ($this->slm->getSessionDeactivated()) {
403 $store_url = 'storeScorm2004.php?package_id=' . $this->packageId . '&ref_id=' . $this->ref_id . '&client_id=' . CLIENT_ID . '&do=store';
404 $unload_url = 'storeScorm2004.php?package_id=' . $this->packageId . '&ref_id=' . $this->ref_id . '&client_id=' . CLIENT_ID . '&do=unload';
405 }
406 $config['cp_url'] = $DIC->ctrl()->getLinkTarget($this, 'cp');
407 $config['cmi_url'] = $DIC->ctrl()->getLinkTarget($this, 'cmi');
408 $config['store_url'] = $store_url;
409 $config['get_adldata_url'] = $DIC->ctrl()->getLinkTarget($this, 'getSharedData');
410 $config['set_adldata_url'] = $DIC->ctrl()->getLinkTarget($this, 'setSharedData');
411 $config['adlact_url'] = $DIC->ctrl()->getLinkTarget($this, 'adlact');
412 $config['specialpage_url'] = $DIC->ctrl()->getLinkTarget($this, 'specialPage');
413 $config['suspend_url'] = $DIC->ctrl()->getLinkTarget($this, 'suspend');
414 $config['get_suspend_url'] = $DIC->ctrl()->getLinkTarget($this, 'getSuspend');
415 //next 2 lines could be deleted later
416 $config['gobjective_url'] = $DIC->ctrl()->getLinkTarget($this, 'gobjective');
417 $config['get_gobjective_url'] = $DIC->ctrl()->getLinkTarget($this, 'getGobjective');
418 $config['ping_url'] = $DIC->ctrl()->getLinkTarget($this, 'pingSession');
419 $config['scorm_player_unload_url'] = $unload_url;
420 $config['post_log_url'] = $DIC->ctrl()->getLinkTarget($this, 'postLogEntry');
421 $config['livelog_url'] = $DIC->ctrl()->getLinkTarget($this, 'liveLogContent');
422 $config['package_url'] = $this->getDataDirectory() . "/";
423
424 //editor
425 $config['envEditor'] = 0;
426
427 //debug
428 $config['debug'] = $this->slm->getDebug();
429 $config['debug_fields'] = $this->getDebugValues();
430 $config['debug_fields_test'] = $this->getDebugValues(true);
431
432 //language strings
433 $langstrings['btnStart'] = $lng->txt('scplayer_start');
434 $langstrings['btnExit'] = $lng->txt('scplayer_exit');
435 $langstrings['btnExitAll'] = $lng->txt('scplayer_exitall');
436 $langstrings['btnSuspendAll'] = $lng->txt('scplayer_suspendall');
437 $langstrings['btnPrevious'] = $lng->txt('scplayer_previous');
438 $langstrings['btnContinue'] = $lng->txt('scplayer_continue');
439 $langstrings['btnhidetree'] = $lng->txt('scplayer_hidetree');
440 $langstrings['btnshowtree'] = $lng->txt('scplayer_showtree');
441 $langstrings['linkexpandTree'] = $lng->txt('scplayer_expandtree');
442 $langstrings['linkcollapseTree'] = $lng->txt('scplayer_collapsetree');
443 $langstrings['contCreditOff'] = $lng->txt('cont_credit_off');
444 if ($this->slm->getAutoReviewChar() === "s") {
445 $langstrings['contCreditOff'] = $lng->txt('cont_sc_score_was_higher_message');
446 }
447 $config['langstrings'] = $langstrings;
448
449 //template variables
450 //$this->tpl = new ilTemplate("tpl.scorm2004.player.html", false, false, "components/ILIAS/Scorm2004");
451 $this->tpl = new ilGlobalTemplate("tpl.scorm2004.player.html", true, true, "components/ILIAS/Scorm2004");
452
453 // TODO should work with jquery.min.js
454 $jQueryPath = str_replace('.min', '', iljQueryUtil::getLocaljQueryPath());
455 $this->tpl->setVariable("JS_FILE", $jQueryPath);
456
457 // include ilias rte css, if given
458 $rte_css = $this->slm->getDataDirectory() . "/ilias_css_4_2/css/style.css";
459 if (is_file($rte_css)) {
460 $this->tpl->setCurrentBlock("rte_css");
461 $this->tpl->setVariable("RTE_CSS", $rte_css);
462 $this->tpl->parseCurrentBlock();
463 }
464
465
466 $this->tpl->setVariable('JSON_LANGSTRINGS', json_encode($langstrings));
467 // include_once("../components/ILIAS/YUI/classes/class.ilYuiUtil.php");
468 // $this->tpl->setVariable('YUI_PATH', ilYuiUtil::getLocalPath());
469 // $this->tpl->setVariable('TREE_JS', "../components/ILIAS/UIComponent/NestedList/js/ilNestedList.js");
470 $this->tpl->setVariable('TREE_JS', "components/ILIAS/Scorm2004/scripts/ilNestedList.js");
471 foreach ($langstrings as $key => $value) {
472 $this->tpl->setVariable($key, $value);
473 }
474 $this->tpl->setVariable('DOC_TITLE', 'ILIAS: ' . $this->slm->getTitle());
475 $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
476 $this->tpl->setVariable('INIT_CP_DATA', json_encode(json_decode($this->getCPDataInit())));
477 $this->tpl->setVariable('INIT_CMI_DATA', json_encode($this->getCMIData($this->userId, $this->packageId)));
478 $this->tpl->setVariable('INIT_ADLACT_DATA', json_encode($initAdlactData));
479 $this->tpl->setVariable('INIT_GLOBALOBJ_DATA', json_encode($initGlobalobjData));
480 $this->tpl->setVariable('JS_DATA', json_encode($config));
481 list($tsfrac, $tsint) = explode(' ', microtime());
482 $this->tpl->setVariable('TIMESTAMP', sprintf('%d%03d', $tsint, 1000 * (float) $tsfrac));
483 $this->tpl->setVariable('BASE_DIR', '../components/ILIAS/Scorm2004/');
484 $this->tpl->setVariable('TXT_COLLAPSE', $lng->txt('scplayer_collapsetree'));
485 if ($this->slm->getDebug()) {
486 $this->tpl->setVariable('TXT_DEBUGGER', $lng->txt('scplayer_debugger'));
487 $debug_url = $DIC->ctrl()->getLinkTarget($this, 'debugGUI');
488 $this->tpl->setVariable('DEBUG_URL', "PopupCenter('" . $debug_url . "','Debug',800,600);");
489 } else {
490 $this->tpl->setVariable('TXT_DEBUGGER', '');
491 $this->tpl->setVariable('DEBUG_URL', '');
492 }
493
494 //set icons path
495 $this->tpl->setVariable('INLINE_CSS', ilSCORM13PlayerGUI::getInlineCss());
496
497 //include scripts
498 if ($this->slm->getCacheDeactivated()) {
499 $rtejs_url = $DIC->ctrl()->getLinkTarget($this, 'getRTEjs');
500 $this->tpl->setVariable('JS_SCRIPTS', $rtejs_url);
501 } else {
502 $this->tpl->setVariable('JS_SCRIPTS', 'components/ILIAS/Scorm2004/scripts/buildrte/rte-min.js');
503 }
504
505 //disable top menu
506 if ($this->slm->getNoMenu() === true) {
507 $this->tpl->setVariable("VAL_DISPLAY", "style=\"display:none;\"");
508 } else {
509 $this->tpl->setVariable("VAL_DISPLAY", "");
510 }
511
512
513 //check for max_attempts and raise error if max_attempts is exceeded
514 // if ($this->get_max_attempts() != 0) {
515 // if ($this->get_actual_attempts() >= $this->get_max_attempts()) {
516 // header('Content-Type: text/html; charset=utf-8');
517 // echo($lng->txt("cont_sc_max_attempt_exceed"));
518 // exit;
519 // }
520 // }
521
522 //count attempt
524 $this->resetSharedData();
525
526 $this->tpl->printToStdout("DEFAULT", false);
527 }
528
529 public static function getInlineCSS(): string
530 {
531 $is_tpl = new ilTemplate("tpl.scorm2004.inlinecss.html", true, true, "components/ILIAS/Scorm2004");
532 $is_tpl->setVariable('IC_ASSET', ilUtil::getImagePath("scorm/asset.svg", ""));
533 $is_tpl->setVariable('IC_COMPLETED', ilUtil::getImagePath("scorm/completed.svg", ""));
534 $is_tpl->setVariable('IC_NOTATTEMPTED', ilUtil::getImagePath("scorm/not_attempted.svg", ""));
535 $is_tpl->setVariable('IC_RUNNING', ilUtil::getImagePath("scorm/running.svg", ""));
536 $is_tpl->setVariable('IC_INCOMPLETE', ilUtil::getImagePath("scorm/incomplete.svg", ""));
537 $is_tpl->setVariable('IC_PASSED', ilUtil::getImagePath("scorm/passed.svg", ""));
538 $is_tpl->setVariable('IC_FAILED', ilUtil::getImagePath("scorm/failed.svg", ""));
539 $is_tpl->setVariable('IC_BROWSED', ilUtil::getImagePath("scorm/browsed.svg", ""));
540 return $is_tpl->get();
541 }
542
543 public function getCPData(): void
544 {
545 $jsdata = $this->getCPDataInit();
546 if ($this->jsMode) {
547 header('Content-Type: text/javascript; charset=UTF-8');
548 print($jsdata);
549 } else {
550 header('Content-Type: text/plain; charset=UTF-8');
551 $jsdata = json_decode($jsdata);
552 print_r($jsdata);
553 }
554 }
555
556 public function getCPDataInit(): string
557 {
558 global $DIC;
559 $ilDB = $DIC->database();
560
561 $res = $ilDB->queryF(
562 'SELECT jsdata FROM cp_package WHERE obj_id = %s',
563 array('integer'),
564 array($this->packageId)
565 );
566 $packageData = $ilDB->fetchAssoc($res);
567
568 $jsdata = $packageData['jsdata'] ?? false;
569 if (!$jsdata) {
570 $jsdata = 'null';
571 }
572
573 return $jsdata;
574 }
575
576
577 public function getADLActDataInit(): string
578 {
579 global $DIC;
580 $ilDB = $DIC->database();
581
582 $res = $ilDB->queryF(
583 'SELECT activitytree FROM cp_package WHERE obj_id = %s',
584 array('integer'),
585 array($this->packageId)
586 );
587 $data = $ilDB->fetchAssoc($res);
588
589 $activitytree = $data['activitytree'] ?? false;
590
591 if (!$activitytree) {
592 $activitytree = 'null';
593 }
594 return $activitytree;
595 }
596
597 public function getADLActData(): void
598 {
599 $activitytree = $this->getADLActDataInit();
600 if ($this->jsMode) {
601 header('Content-Type: text/javascript; charset=UTF-8');
602 print($activitytree);
603 } else {
604 header('Content-Type: text/plain; charset=UTF-8');
605 $activitytree = json_decode($activitytree);
606 print_r($activitytree);
607 }
608 }
609
610 public function pingSession(): void
611 {
612 ilWACSignedPath::signFolderOfStartFile($this->getDataDirectory() . '/imsmanifest.xml');
613 //do nothing except returning header
614 header('Content-Type: text/plain; charset=UTF-8');
615 print("");
616 }
617
618 public function getScope(): string
619 {
620 global $DIC;
621 $ilDB = $DIC->database();
622 $ilUser = $DIC->user();
623
624 $res = $ilDB->queryF(
625 'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
626 array('integer'),
627 array($this->packageId)
628 );
629 $data = $ilDB->fetchAssoc($res);
630
631 $gystem = $data['global_to_system'] ?? 1;
632 if ($gystem == 1) {
633 $gsystem = 'null';
634 } else {
635 $gsystem = (string) $this->packageId;
636 }
637
638 return $gsystem;
639 }
640
641 public function getSuspendDataInit(): string
642 {
643 global $DIC;
644 $ilDB = $DIC->database();
645 $ilUser = $DIC->user();
646
647 $res = $ilDB->queryF(
648 'SELECT data FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
649 array('integer', 'integer'),
650 array($this->packageId, $ilUser->getId())
651 );
652 $data = $ilDB->fetchAssoc($res);
653
654 //delete delivered suspend data
655 $ilDB->manipulateF(
656 'DELETE FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
657 array('integer', 'integer'),
658 array($this->packageId, $ilUser->getId())
659 );
660 if (is_array($data)) {
661 return $data['data'];
662 }
663 return "";
664 }
665
666 public function getSuspendData(): void
667 {
668 $suspend_data = $this->getSuspendDataInit();
669 if ($this->jsMode) {
670 header('Content-Type: text/javascript; charset=UTF-8');
671 print($suspend_data);
672 } else {
673 header('Content-Type: text/plain; charset=UTF-8');
674 $suspend_data = json_decode($suspend_data);
675 print_r($suspend_data);
676 }
677 }
678
679 public function suspendADLActData(): void
680 {
681 global $DIC;
682 $ilDB = $DIC->database();
683 $ilUser = $DIC->user();
684
685 $res = $ilDB->queryF(
686 'SELECT * FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
687 array('integer', 'integer'),
688 array($this->packageId, $ilUser->getId())
689 );
690
691 if (!$ilDB->numRows($res)) {
692 $ilDB->insert('cp_suspend', array(
693 'data' => array('clob', file_get_contents('php://input')),
694 'obj_id' => array('integer', $this->packageId),
695 'user_id' => array('integer', $ilUser->getId())
696 ));
697 } else {
698 $ilDB->update(
699 'cp_suspend',
700 array(
701 'data' => array('clob', file_get_contents('php://input'))
702 ),
703 array(
704 'obj_id' => array('integer', $this->packageId),
705 'user_id' => array('integer', $ilUser->getId())
706 )
707 );
708 }
709 }
710
711 public function readGObjectiveInit(): array
712 {
713 global $DIC;
714 $ilDB = $DIC->database();
715 $ilUser = $DIC->user();
716
717 //get json string
718 $g_data = [];
719
720 $global_to_system = 1;
721
722 $res = $ilDB->queryF(
723 'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
724 array('integer'),
725 array($this->packageId)
726 );
727 while ($data = $ilDB->fetchAssoc($res)) {
728 $global_to_system = $data['global_to_system'];
729 }
730
731 $query = 'SELECT objective_id, scope_id, satisfied, measure, user_id,
732 score_min, score_max, score_raw, completion_status,
733 progress_measure '
734 . 'FROM cmi_gobjective, cp_node, cp_mapinfo '
735 . 'WHERE (cmi_gobjective.objective_id <> %s AND cmi_gobjective.status IS NULL '
736 . 'AND cp_node.slm_id = %s AND cp_node.nodename = %s '
737 . 'AND cp_node.cp_node_id = cp_mapinfo.cp_node_id '
738 . 'AND cmi_gobjective.objective_id = cp_mapinfo.targetobjectiveid) '
739 . 'GROUP BY objective_id, scope_id, satisfied, measure, user_id,
740 score_min, score_max, score_raw, completion_status,
741 progress_measure';
742 $res = $ilDB->queryF(
743 $query,
744 array('text', 'integer', 'text'),
745 array('-course_overall_status-', $this->packageId, 'mapInfo')
746 );
747 while ($row = $ilDB->fetchAssoc($res)) {
748 if (($global_to_system == 1 && $row['scope_id'] == 0) || ($global_to_system == 0 && $row['scope_id'] == $this->packageId)) {
749 $learner = $row['user_id'];
750 $objective_id = $row['objective_id'];
751 if ($row['scope_id'] == 0) {
752 $scope = "null";
753 } else {
754 $scope = $row['scope_id'];
755 }
756
757 if ($row['satisfied'] != null) {
758 $toset = $row['satisfied'];
759 $g_data["satisfied"][$objective_id][$learner][$scope] = $toset;
760 }
761
762 if ($row['measure'] != null) {
763 $toset = $row['measure'];
764 $g_data["measure"][$objective_id][$learner][$scope] = $toset;
765 }
766
767 if ($row['score_raw'] != null) {
768 $toset = $row['score_raw'];
769 $g_data["score_raw"][$objective_id][$learner][$scope] = $toset;
770 }
771
772 if ($row['score_min'] != null) {
773 $toset = $row['score_min'];
774 $g_data["score_min"][$objective_id][$learner][$scope] = $toset;
775 }
776
777 if ($row['score_max'] != null) {
778 $toset = $row['score_max'];
779 $g_data["score_max"][$objective_id][$learner][$scope] = $toset;
780 }
781
782 if ($row['progress_measure'] != null) {
783 $toset = $row['progress_measure'];
784 $g_data["progress_measure"][$objective_id][$learner][$scope] = $toset;
785 }
786
787 if ($row['completion_status'] != null) {
788 $toset = $row['completion_status'];
789 $g_data["completion_status"][$objective_id][$learner][$scope] = $toset;
790 }
791 }
792 }
793 return $g_data;
794 }
795
796 public function readGObjective(): void
797 {
798 $gobjective_data = json_encode($this->readGObjectiveInit());
799 if ($this->jsMode) {
800 header('Content-Type: text/javascript; charset=UTF-8');
801 print($gobjective_data);
802 } else {
803 header('Content-Type: text/plain; charset=UTF-8');
804 $gobjective_data = json_decode($gobjective_data);
805 print_r($gobjective_data);
806 }
807 }
808
809
810 //Read the shared datascores for a given SCO
811 public function readSharedData(int $sco_node_id): void
812 {
813 global $DIC;
814 $ilDB = $DIC->database();
815 $ilUser = $DIC->user();
816 $dataStores = array( "data" => array(),
817 "permissions" => array());
818 $readPermissions = array();
819
820 $query = 'SELECT target_id, read_shared_data, write_shared_data '
821 . 'FROM cp_datamap '
822 . 'WHERE slm_id = %s '
823 . 'AND sco_node_id = %s '
824 . 'GROUP BY target_id, read_shared_data, write_shared_data';
825
826
827 $res = $ilDB->queryF(
828 $query,
829 array('integer', 'integer'),
830 array($this->packageId, $sco_node_id)
831 );
832
833 //Pass 1: Get all the shared data target_ids
834 // for this content package
835 while ($row = $ilDB->fetchAssoc($res)) {
836 $storeVal = ($row['read_shared_data'] == 0 && $row['write_shared_data'] == 1)
837 ? 'notWritten'
838 : null;
839
840 $dataStores["data"][$row['target_id']] = array( "store" => $storeVal,
841 "readSharedData" => $row['read_shared_data'],
842 "writeSharedData" => $row['write_shared_data']);
843 $dataStores["readPermissions"][$row['target_id']] = $row['read_shared_data'];
844 }
845
846 if (count($dataStores) < 1) {
847 //If there are no datastores, then return nothing
848 echo "";
849 exit();
850 }
851
852 if (isset($dataStores["readPermissions"]) && $dataStores["readPermissions"] != null && array_sum($dataStores["readPermissions"]) != 0) {
853 //If there exists at least one readSharedData permission, then
854 //fill in the existing values (if any) already in the store.
855
856 //Create the params to add to the Pass 2 query (get existing values)
857 $params = array("types" => array("integer", "integer"),
858 "values" => array($this->userId, $this->packageId));
859
860 $paramTemplate = '';
861
862 //See if readSharedData is set for each datamap.
863 //If set to true, then add it to the search query
864 foreach ($dataStores["data"] as $key => $val) {
865 if ($dataStores["readPermissions"][(string) $key] == 1
866 && $dataStores["data"][(string) $key]["store"] !== 'notWritten') {
867 $params["types"][] = "text";
868 $params["values"][] = $key;
869 $paramTemplate .= '%s, ';
870 }
871 }
872
873 //Get rid of the trailing ', '
874 $paramTemplate = substr($paramTemplate, 0, -2);
875
876 //Pass 2: Query for values previously saved by the user
877 $query = 'SELECT target_id, store '
878 . 'FROM adl_shared_data '
879 . 'WHERE user_id = %s '
880 . 'AND slm_id = %s '
881 . 'AND target_id IN (' . $paramTemplate . ')';
882
883
884 $res = $ilDB->queryF(
885 $query,
886 $params["types"],
887 $params["values"]
888 );
889
890 while ($row = $ilDB->fetchAssoc($res)) {
891 $dataStores["data"][$row['target_id']]["store"] = $row['store'];
892 }
893 }
894
895 header('Content-Type: text/javascript; charset=UTF-8');
896
897 echo json_encode($dataStores["data"]);
898 }
899
900 public function writeSharedData(int $sco_node_id): void
901 {
902 global $DIC;
903 $ilDB = $DIC->database();
904 $ilUser = $DIC->user();
905 $g_data = json_decode(file_get_contents('php://input'));
906
907 if ($g_data == null) return;
908
909 //Step 1: Get the writeable stores for this SCO that already have values
910 $query = 'SELECT dm.target_id, sd.store '
911 . 'FROM cp_datamap dm '
912 . 'LEFT JOIN adl_shared_data sd '
913 . 'ON(dm.slm_id = sd.slm_id AND dm.target_id = sd.target_id) '
914 . 'WHERE sco_node_id = %s '
915 . 'AND dm.slm_id = %s '
916 . 'AND write_shared_data = 1 '
917 . 'AND user_id = %s';
918
919 $res = $ilDB->QueryF(
920 $query,
921 array('integer', 'integer', 'integer'),
922 array($sco_node_id, $this->packageId, $this->userId)
923 );
924
925 $dataStores = array();
926 $originalVals = array();
927 while ($row = $ilDB->fetchAssoc($res)) {
928 $id = $row['target_id'];
929 $dataStores[$id] = $g_data->{$id};
930 $originalVals[$id] = $row['store'];
931 }
932
933
934 //Step 2: Add the writeable stores
935 foreach ($g_data as $key => $obj) {
936 //If it's already created in adl_shared_data, we
937 //need to update it.
938 if (array_key_exists($key, $dataStores)) {
939 if ($obj === 'notWritten') {
940 continue;
941 }
942
943 $query = 'UPDATE adl_shared_data '
944 . 'SET store = %s '
945 . 'WHERE user_id = %s '
946 . 'AND target_id = %s '
947 . 'AND slm_id = %s ';
948
949 $ilDB->manipulateF(
950 $query,
951 array('text', 'integer', 'text', 'integer'),
952 array($dataStores[$key], $this->userId, $key, $this->packageId)
953 );
954 } else {
955 //Check for writability
956 $res = $ilDB->queryF(
957 'SELECT write_shared_data, cp_node_id '
958 . 'FROM cp_datamap '
959 . 'WHERE target_id = %s '
960 . 'AND slm_id = %s '
961 . 'AND sco_node_id = %s',
962 array('text', 'integer', 'integer'),
963 array($key, $this->packageId, $sco_node_id)
964 );
965
966 $row = $ilDB->fetchAssoc($res);
967 if ($row["write_shared_data"] != 1) {
968 continue;
969 }
970
971 //If it's writeable, then add the new value into the database
972 $res = $ilDB->manipulateF(
973 'INSERT INTO adl_shared_data (slm_id, user_id, target_id, store, cp_node_id) VALUES (%s, %s, %s, %s, %s)',
974 array('integer', 'integer', 'text', 'text', 'integer'),
975 array($this->packageId, $this->userId, $key, $obj, $row["cp_node_id"])
976 );
977 }
978 }
979 echo "1";
980 exit;
981 }
982
983 public function specialPage(): void
984 {
985 global $DIC;
986 $lng = $DIC->language();
987
988 $specialpages = array(
989 "_COURSECOMPLETE_" => "seq_coursecomplete",
990 "_ENDSESSION_" => "seq_endsession",
991 "_SEQBLOCKED_" => "seq_blocked",
992 "_NOTHING_" => "seq_nothing",
993 "_ERROR_" => "seq_error",
994 "_DEADLOCK_" => "seq_deadlock",
995 "_INVALIDNAVREQ_" => "seq_invalidnavreq",
996 "_SEQABANDON_" => "seq_abandon",
997 "_SEQABANDONALL_" => "seq_abandonall",
998 "_TOC_" => "seq_toc",
999 "" => ""
1000 );
1001
1002 $this->tpl = new ilGlobalTemplate("tpl.scorm2004.specialpages.html", false, false, "components/ILIAS/Scorm2004");
1003 $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
1004 $this->tpl->setVariable('TXT_SPECIALPAGE', $lng->txt($specialpages[$this->page]));
1005 if ($this->page !== "_TOC_" && $this->page !== "_SEQABANDON_" && $this->page !== "_SEQABANDONALL_") {
1006 $this->tpl->setVariable('CLOSE_WINDOW', $lng->txt('seq_close'));
1007 } else {
1008 $this->tpl->setVariable('CLOSE_WINDOW', "");
1009 }
1010 $this->tpl->printToStdout("DEFAULT", false);
1011 }
1012
1013
1014 public function fetchCMIData(): void
1015 {
1016 $data = $this->getCMIData($this->userId, $this->packageId);
1017 if ($this->jsMode) {
1018 header('Content-Type: text/javascript; charset=UTF-8');
1019 print(json_encode($data));
1020 } else {
1021 header('Content-Type: text/plain; charset=UTF-8');
1022 print(var_export($data, true));
1023 }
1024 }
1025
1026
1027 // /**
1028 // * maps API data structure type to internal datatype on a node
1029 // * and accepts only valid values, dropping invalid ones from input
1030 // */
1031 // private function normalizeFields($table, &$node) : void
1032 // {
1033 // return;
1034 // foreach (self::$schema[$table] as $k => $v) {
1035 // $value = $node->$k;
1036 // if (isset($value) && is_string($v) && !preg_match($v, $value)) {
1037 // unset($node->$k);
1038 // }
1039 // }
1040 // }
1041
1045 public function getCMIData(int $userId, int $packageId): array
1046 {
1047 global $DIC;
1048 $ilDB = $DIC->database();
1049
1050 $i_check = 0;
1051 $result = array(
1052 'schema' => array(),
1053 'data' => array()
1054 );
1055
1056 foreach (self::$schema as $k => &$v) {
1057 $result['schema'][$k] = array_keys($v);
1058 $q = '';
1059 switch ($k) {
1060 case "node":
1061 $q = 'SELECT cmi_node.*
1062 FROM cmi_node
1063 INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
1064 WHERE cmi_node.user_id = %s
1065 AND cp_node.slm_id = %s';
1066
1067 break;
1068
1069 case "comment":
1070 if ($i_check > 7) {
1071 $i_check -= 8;
1072 if ($this->slm->getComments()) {
1073 $q = 'SELECT
1074 cmi_comment.cmi_comment_id,
1075 cmi_comment.cmi_node_id,
1076 cmi_comment.c_comment,
1077 cmi_comment.c_timestamp,
1078 cmi_comment.location,
1079 cmi_comment.sourceislms
1080 FROM cmi_comment
1081 INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_comment.cmi_node_id
1082 INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1083 WHERE cmi_node.user_id = %s
1084 AND cp_node.slm_id = %s
1085 ORDER BY cmi_comment.cmi_comment_id';
1086 }
1087 }
1088
1089 break;
1090
1091 case "correct_response":
1092 if ($i_check > 3) {
1093 $i_check -= 4;
1094 if ($this->slm->getInteractions()) {
1095 $q = 'SELECT cmi_correct_response.*
1096 FROM cmi_correct_response
1097 INNER JOIN cmi_interaction
1098 ON cmi_interaction.cmi_interaction_id = cmi_correct_response.cmi_interaction_id
1099 INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
1100 INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1101 WHERE cmi_node.user_id = %s
1102 AND cp_node.slm_id = %s
1103 ORDER BY cmi_correct_response.cmi_correct_resp_id';
1104 }
1105 }
1106 break;
1107
1108 case "interaction":
1109 if ($i_check > 1) {
1110 $i_check -= 2;
1111 if ($this->slm->getInteractions()) {
1112 $q = 'SELECT
1113 cmi_interaction.cmi_interaction_id,
1114 cmi_interaction.cmi_node_id,
1115 cmi_interaction.description,
1116 cmi_interaction.id,
1117 cmi_interaction.latency,
1118 cmi_interaction.learner_response,
1119 cmi_interaction.result,
1120 cmi_interaction.c_timestamp,
1121 cmi_interaction.c_type,
1122 cmi_interaction.weighting
1123 FROM cmi_interaction
1124 INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
1125 INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1126 WHERE cmi_node.user_id = %s
1127 AND cp_node.slm_id = %s
1128 ORDER BY cmi_interaction.cmi_interaction_id';
1129 }
1130 }
1131 break;
1132
1133 case "objective":
1134 if ($i_check > 0) {
1135 if ($this->slm->getObjectives()) {
1136 $q = 'SELECT
1137 cmi_objective.cmi_interaction_id,
1138 cmi_objective.cmi_node_id,
1139 cmi_objective.cmi_objective_id,
1140 cmi_objective.completion_status,
1141 cmi_objective.description,
1142 cmi_objective.id,
1143 cmi_objective.c_max,
1144 cmi_objective.c_min,
1145 cmi_objective.c_raw,
1146 cmi_objective.scaled,
1147 cmi_objective.progress_measure,
1148 cmi_objective.success_status,
1149 cmi_objective.scope
1150 FROM cmi_objective
1151 INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_objective.cmi_node_id
1152 INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1153 WHERE cmi_node.user_id = %s
1154 AND cp_node.slm_id = %s
1155 ORDER BY cmi_objective.cmi_objective_id';
1156 }
1157 }
1158 break;
1159
1160 case "package"://delete because data exist except of learner_name
1161 $q = 'SELECT usr_data.usr_id user_id,
1162 CONCAT(CONCAT(COALESCE(usr_data.firstname, \'\'), \' \'), COALESCE(usr_data.lastname, \'\')) learner_name,
1163 sahs_lm.id slm_id , sahs_lm.default_lesson_mode "mode", sahs_lm.credit
1164 FROM usr_data, cp_package
1165 INNER JOIN sahs_lm ON cp_package.obj_id = sahs_lm.id
1166 WHERE usr_data.usr_id = %s
1167 AND sahs_lm.id = %s';
1168
1169 break;
1170 }
1171
1172 $result['data'][$k] = array();
1173 if ($q != '') {
1174 $types = array('integer', 'integer');
1175 $values = array($userId, $packageId);
1176 $res = $ilDB->queryF($q, $types, $values);
1177
1178 while ($row = $ilDB->fetchAssoc($res)) {
1179 $tmp_result = array();
1180 foreach ($row as $key => $value) {
1181 if ($k === "comment" && $key === "c_timestamp" && strpos($value, ' ') == 10) {
1182 $value = str_replace(' ', 'T', $value);
1183 }
1184 $tmp_result[] = $value;
1185 if ($k === "node" && $key === "additional_tables" && $i_check < $value) {
1186 $i_check = $value;
1187 // $GLOBALS['DIC']['ilLog']->write($i_check);
1188 }
1189 }
1190 $result['data'][$k][] = $tmp_result;
1191 }
1192 }
1193 }
1194 return $result;
1195 }
1196
1200 public function quoteJSONArray(?array $a_array): array
1201 {
1202 global $DIC;
1203 $ilDB = $DIC->database();
1204
1205 if (!is_array($a_array) or !count($a_array)) {
1206 return array("''");
1207 }
1208
1209 foreach ($a_array as $k => $item) {
1210 if ($item != null) {
1211 $a_array[$k] = $ilDB->quote($item);
1212 } else {
1213 $a_array[$k] = "NULL";
1214 }
1215 }
1216
1217 return $a_array;
1218 }
1219
1220 // /**
1221 // * estimate content type for a filename by extension
1222 // * first do it for common static web files from external list
1223 // * if not found peek into file by slow php function mime_content_type()
1224 // * @param $filename required
1225 // * @return string mimetype name e.g. image/jpeg
1226 // */
1227 // public function getMimetype($filename)
1228 // {
1229 // include_once("../components/ILIAS/MediaObjects/classes/class.ilObjMediaObject.php");
1230 // return ilObjMediaObject::getMimeType($filename);
1231 // }
1232
1233
1234 // /**
1235 // * Get max. number of attempts allowed for this package
1236 // */
1237 // public function get_max_attempts()
1238 // {
1239 // include_once "../components/ILIAS/ScormAicc/classes/SCORM/class.ilObjSCORMInitData.php";
1240 // return ilObjSCORMInitData::get_max_attempts($this->packageId);
1241 // }
1242
1243 public function get_Module_Version(): int
1244 {
1245 global $DIC;
1246 $ilDB = $DIC->database();
1247
1248 $res = $ilDB->queryF(
1249 'SELECT module_version FROM sahs_lm WHERE id = %s',
1250 array('integer'),
1251 array($this->packageId)
1252 );
1253 $row = $ilDB->fetchAssoc($res);
1254
1255 return (int) $row['module_version'];
1256 }
1257
1261 public function get_actual_attempts(): int
1262 {
1263 global $DIC;
1264 $ilDB = $DIC->database();
1265 $ilUser = $DIC->user();
1266 $val_set = $ilDB->queryF(
1267 'SELECT package_attempts FROM sahs_user WHERE obj_id = %s AND user_id = %s',
1268 array('integer','integer'),
1269 array($this->packageId,$this->userId)
1270 );
1271 $val_rec = $ilDB->fetchAssoc($val_set);
1272 $attempts = $val_rec["package_attempts"];
1273 if ($attempts == null) {
1274 $attempts = 0;
1275 }
1276 return (int) $attempts;
1277 }
1278
1283 {
1284 global $DIC;
1285 $ilDB = $DIC->database();
1286 $ilUser = $DIC->user();
1287 $res = $ilDB->queryF(
1288 'SELECT package_attempts,count(*) cnt FROM sahs_user WHERE obj_id = %s AND user_id = %s GROUP BY package_attempts',
1289 array('integer','integer'),
1290 array($this->slm->getId(),$ilUser->getId())
1291 );
1292 $val_rec = $ilDB->fetchAssoc($res);
1293 if ($val_rec["cnt"] == 0) { //offline_mode could be inserted
1294 $attempts = 1;
1295 $ilDB->manipulateF(
1296 'INSERT INTO sahs_user (obj_id,user_id,package_attempts,module_version,last_access) VALUES(%s,%s,%s,%s,%s)',
1297 array('integer', 'integer', 'integer', 'integer', 'timestamp'),
1298 array($this->slm->getId(), $ilUser->getId(), $attempts, $this->slm->getModuleVersion(), date('Y-m-d H:i:s'))
1299 );
1300 } else {
1301 $attempts = $val_rec["package_attempts"];
1302 if ($attempts == null) {
1303 $attempts = 0;
1304 }
1305 $attempts++;
1306 $ilDB->manipulateF(
1307 'UPDATE sahs_user SET package_attempts = %s, module_version = %s, last_access=%s WHERE obj_id = %s AND user_id = %s ',
1308 array('integer', 'integer', 'timestamp', 'integer', 'integer'),
1309 array($attempts, $this->slm->getModuleVersion(), date('Y-m-d H:i:s'), $this->slm->getId(), $ilUser->getId())
1310 );
1311 }
1312 }
1313
1314 public function resetSharedData(): void
1315 {
1316 global $DIC;
1317 $ilDB = $DIC->database();
1318 //Reset the shared data stores if sharedDataGlobalToSystem is false
1319 $res = $ilDB->queryF(
1320 'SELECT shared_data_global_to_system FROM cp_package WHERE obj_id = %s',
1321 array('integer'),
1322 array($this->packageId)
1323 );
1324
1325 $shared_global_to_sys = $ilDB->fetchObject($res)->shared_data_global_to_system ?? 0;
1326
1327 $res = $ilDB->queryF(
1328 'SELECT data FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
1329 array('integer', 'integer'),
1330 array($this->packageId, $this->userId)
1331 );
1332
1333 $suspended = false;
1334 while ($data = $ilDB->fetchAssoc($res)) {
1335 $dat = $data['data'];
1336 if ($dat != null && $dat != '') {
1337 $suspended = true;
1338 }
1339 }
1340
1341 if ($shared_global_to_sys == 0 && !$suspended) {
1342 $ilDB->manipulateF(
1343 'DELETE FROM adl_shared_data WHERE slm_id = %s AND user_id = %s',
1344 array('integer', 'integer'),
1345 array($this->packageId, $this->userId)
1346 );
1347 }
1348 }
1349
1350 //debug extentions
1354 private function getNodeData(string $sco_id): array
1355 {
1356 global $DIC;
1357 $ilDB = $DIC->database();
1358 $ilLog = ilLoggerFactory::getLogger('sc13');
1359
1360 $fieldList = "cmi_node.cp_node_id, cmi_node.completion_threshold, cmi_node.c_exit, cmi_node.completion_status, cmi_node.progress_measure, cmi_node.success_status, cmi_node.scaled, cmi_node.session_time," .
1361 "cmi_node.c_min, cmi_node.c_max, cmi_node.c_raw, cmi_node.location, cmi_node.suspend_data, cmi_node.scaled_passing_score, cmi_node.total_time";
1362
1363
1364 $res = $ilDB->queryF(
1365 '
1366 SELECT ' . $fieldList . '
1367 FROM cmi_node,cp_node,cp_item
1368 WHERE cp_node.slm_id = %s
1369 AND cp_node.cp_node_id = cp_item.cp_node_id
1370 AND cp_item.id = %s
1371 AND cmi_node.cp_node_id = cp_item.cp_node_id
1372 AND cmi_node.user_id = %s',
1373 array('integer','text','integer'),
1374 array($this->packageId, $sco_id, $this->userId)
1375 );
1376 // $ilLog->write("DEBUG SQL" . $row);
1377 return $ilDB->fetchAssoc($res);
1378 }
1379
1380 private function logTmpName(): string
1381 {
1382 $filename = $this->logDirectory() . "/" . $this->packageId . ".tmp";
1383 if (!file_exists($filename)) {
1384 umask(0000);
1385 $fHandle = fopen($filename, 'a') or die("can't open file");
1386 fwrite($fHandle, "");
1387 fclose($fHandle);
1388 }
1389 return $filename;
1390 }
1391
1392 private function summaryFileName(): string
1393 {
1394 $filename = $this->logDirectory() . "/" . $this->packageId . "_summary_" . $this->get_actual_attempts();
1395 $adder = "0";
1396 $suffix = ".csv";
1397 $i = 0;
1398 while (file_exists($filename . "_" . $adder . $suffix)) {
1399 $i++;
1400 $adder = (string) $i;
1401 }
1402 $retname = $filename . "_" . $adder . $suffix;
1403
1404 if (!file_exists($retname)) {
1405 umask(0000);
1406 $fHandle = fopen($retname, 'a') or die("can't open file");
1407 fwrite($fHandle, "");
1408 fclose($fHandle);
1409 }
1410 return $retname;
1411 }
1412
1413 private function logFileName(): string
1414 {
1415 global $DIC;
1416 $lng = $DIC->language();
1417 $lng->loadLanguageModule("scormdebug");
1418
1419 $filename = $this->logDirectory() . "/" . $this->packageId . "_" . $this->get_actual_attempts();
1420 $path_csv = $filename . ".csv";
1421 $path_txt = $filename . ".html";
1422 if (!file_exists($path_csv)) {
1423 umask(0000);
1424 $fHandle = fopen($path_csv, 'a') or die("can't open file");
1425 $string = '"CourseId";"ScoId";"ScoTitle";"Timestamp";"Action";"Key";"Value";"Return Value";"Errorcode";"Timespan";"ErrorDescription"' . "\n";
1426 fwrite($fHandle, $string);
1427 fclose($fHandle);
1428 }
1429 if (!file_exists($path_txt)) {
1430 if (file_exists($this->logTmpName())) {
1431 unlink($this->logTmpName());
1432 }
1433 umask(0000);
1434 $fHandle2 = fopen($path_txt, 'a') or die("can't open file");
1435 $logtpl = $this->getLogTemplate();
1436 $logtpl->setCurrentBlock('NewLog');
1437 $logtpl->setVariable("COURSETITLE", $this->slm->getTitle());
1438 $logtpl->setVariable("COURSEID", $this->packageId);
1439 $logtpl->setVariable("TIMESTAMP", date("d.m.Y H:i", time()));
1440 $logtpl->setVariable("SESSION", $this->get_actual_attempts());
1441 $logtpl->setVariable("error0", $lng->txt("error0"));
1442 $logtpl->setVariable("error101", $lng->txt("error101"));
1443 $logtpl->setVariable("error102", $lng->txt("error102"));
1444 $logtpl->setVariable("error103", $lng->txt("error103"));
1445 $logtpl->setVariable("error104", $lng->txt("error104"));
1446 $logtpl->setVariable("error111", $lng->txt("error111"));
1447 $logtpl->setVariable("error112", $lng->txt("error112"));
1448 $logtpl->setVariable("error113", $lng->txt("error113"));
1449 $logtpl->setVariable("error122", $lng->txt("error122"));
1450 $logtpl->setVariable("error123", $lng->txt("error123"));
1451 $logtpl->setVariable("error132", $lng->txt("error132"));
1452 $logtpl->setVariable("error133", $lng->txt("error133"));
1453 $logtpl->setVariable("error142", $lng->txt("error142"));
1454 $logtpl->setVariable("error143", $lng->txt("error143"));
1455 $logtpl->setVariable("error201", $lng->txt("error201"));
1456 $logtpl->setVariable("error301", $lng->txt("error301"));
1457 $logtpl->setVariable("error351", $lng->txt("error351"));
1458 $logtpl->setVariable("error391", $lng->txt("error391"));
1459 $logtpl->setVariable("error401", $lng->txt("error401"));
1460 $logtpl->setVariable("error402", $lng->txt("error402"));
1461 $logtpl->setVariable("error403", $lng->txt("error403"));
1462 $logtpl->setVariable("error404", $lng->txt("error404"));
1463 $logtpl->setVariable("error405", $lng->txt("error405"));
1464 $logtpl->setVariable("error406", $lng->txt("error406"));
1465 $logtpl->setVariable("error407", $lng->txt("error407"));
1466 $logtpl->setVariable("error408", $lng->txt("error408"));
1467 $logtpl->setVariable("SetValue", $lng->txt("SetValue"));
1468 $logtpl->setVariable("GetValue", $lng->txt("GetValue"));
1469 $logtpl->setVariable("Commit", $lng->txt("Commit"));
1470 $logtpl->setVariable("Initialize", $lng->txt("Initialize"));
1471 $logtpl->setVariable("Terminate", $lng->txt("Terminate"));
1472 $logtpl->setVariable("GetErrorString", $lng->txt("GetErrorString"));
1473 $logtpl->setVariable("GetLastError", $lng->txt("GetLastError"));
1474 $logtpl->setVariable("GetDiagnostic", $lng->txt("GetDiagnostic"));
1475 $logtpl->setVariable("cmi._version", $lng->txt("cmi._version"));
1476 $logtpl->setVariable("cmi.comments_from_learner._children", $lng->txt("cmi.comments_from_learner._children"));
1477 $logtpl->setVariable("cmi.comments_from_learner._count", $lng->txt("cmi.comments_from_learner._count"));
1478 $logtpl->setVariable("cmi.comments_from_learner.n.comment", $lng->txt("cmi.comments_from_learner.n.comment"));
1479 $logtpl->setVariable("cmi.comments_from_learner.n.location", $lng->txt("cmi.comments_from_learner.n.location"));
1480 $logtpl->setVariable("cmi.comments_from_learner.n.timestamp", $lng->txt("cmi.comments_from_learner.n.timestamp"));
1481 $logtpl->setVariable("cmi.comments_from_lms._children", $lng->txt("cmi.comments_from_lms._children"));
1482 $logtpl->setVariable("cmi.comments_from_lms._count", $lng->txt("cmi.comments_from_lms._count"));
1483 $logtpl->setVariable("cmi.comments_from_lms.n.comment", $lng->txt("cmi.comments_from_lms.n.comment"));
1484 $logtpl->setVariable("cmi.comments_from_lms.n.location", $lng->txt("cmi.comments_from_lms.n.location"));
1485 $logtpl->setVariable("cmi.comments_from_lms.n.timestamp", $lng->txt("cmi.comments_from_lms.n.timestamp"));
1486 $logtpl->setVariable("cmi.completion_status", $lng->txt("cmi.completion_status"));
1487 $logtpl->setVariable("cmi.completion_threshold", $lng->txt("cmi.completion_threshold"));
1488 $logtpl->setVariable("cmi.credit", $lng->txt("cmi.credit"));
1489 $logtpl->setVariable("cmi.entry", $lng->txt("cmi.entry"));
1490 $logtpl->setVariable("cmi.exit", $lng->txt("cmi.exit"));
1491 $logtpl->setVariable("cmi.interactions._children", $lng->txt("cmi.interactions._children"));
1492 $logtpl->setVariable("cmi.interactions._count", $lng->txt("cmi.interactions._count"));
1493 $logtpl->setVariable("cmi.interactions.n.id", $lng->txt("cmi.interactions.n.id"));
1494 $logtpl->setVariable("cmi.interactions.n.type", $lng->txt("cmi.interactions.n.type"));
1495 $logtpl->setVariable("cmi.interactions.n.objectives._count", $lng->txt("cmi.interactions.n.objectives._count"));
1496 $logtpl->setVariable("cmi.interactions.n.objectives.n.id", $lng->txt("cmi.interactions.n.objectives.n.id"));
1497 $logtpl->setVariable("cmi.interactions.n.timestamp", $lng->txt("cmi.interactions.n.timestamp"));
1498 $logtpl->setVariable("cmi.interactions.n.correct_responses._count", $lng->txt("cmi.interactions.n.correct_responses._count"));
1499 $logtpl->setVariable("cmi.interactions.n.correct_responses.n.pattern", $lng->txt("cmi.interactions.n.correct_responses.n.pattern"));
1500 $logtpl->setVariable("cmi.interactions.n.weighting", $lng->txt("cmi.interactions.n.weighting"));
1501 $logtpl->setVariable("cmi.interactions.n.learner_response", $lng->txt("cmi.interactions.n.learner_response"));
1502 $logtpl->setVariable("cmi.interactions.n.result", $lng->txt("cmi.interactions.n.result"));
1503 $logtpl->setVariable("cmi.interactions.n.latency", $lng->txt("cmi.interactions.n.latency"));
1504 $logtpl->setVariable("cmi.interactions.n.description", $lng->txt("cmi.interactions.n.description"));
1505 $logtpl->setVariable("cmi.launch_data", $lng->txt("cmi.launch_data"));
1506 $logtpl->setVariable("cmi.learner_id", $lng->txt("cmi.learner_id"));
1507 $logtpl->setVariable("cmi.learner_name", $lng->txt("cmi.learner_name"));
1508 $logtpl->setVariable("cmi.learner_preference._children", $lng->txt("cmi.learner_preference._children"));
1509 $logtpl->setVariable("cmi.learner_preference.audio_level", $lng->txt("cmi.learner_preference.audio_level"));
1510 $logtpl->setVariable("cmi.learner_preference.language", $lng->txt("cmi.learner_preference.language"));
1511 $logtpl->setVariable("cmi.learner_preference.delivery_speed", $lng->txt("cmi.learner_preference.delivery_speed"));
1512 $logtpl->setVariable("cmi.learner_preference.audio_captioning", $lng->txt("cmi.learner_preference.audio_captioning"));
1513 $logtpl->setVariable("cmi.location", $lng->txt("cmi.location"));
1514 $logtpl->setVariable("cmi.max_time_allowed", $lng->txt("cmi.max_time_allowed"));
1515 $logtpl->setVariable("cmi.mode", $lng->txt("cmi.mode"));
1516 $logtpl->setVariable("cmi.objectives._children", $lng->txt("cmi.objectives._children"));
1517 $logtpl->setVariable("cmi.objectives._count", $lng->txt("cmi.objectives._count"));
1518 $logtpl->setVariable("cmi.objectives.n.id", $lng->txt("cmi.objectives.n.id"));
1519 $logtpl->setVariable("cmi.objectives.n.score._children", $lng->txt("cmi.objectives.n.score._children"));
1520 $logtpl->setVariable("cmi.objectives.n.score.scaled", $lng->txt("cmi.objectives.n.score.scaled"));
1521 $logtpl->setVariable("cmi.objectives.n.score.raw", $lng->txt("cmi.objectives.n.score.raw"));
1522 $logtpl->setVariable("cmi.objectives.n.score.min", $lng->txt("cmi.objectives.n.score.min"));
1523 $logtpl->setVariable("cmi.objectives.n.score.max", $lng->txt("cmi.objectives.n.score.max"));
1524 $logtpl->setVariable("cmi.objectives.n.success_status", $lng->txt("cmi.objectives.n.success_status"));
1525 $logtpl->setVariable("cmi.objectives.n.completion_status", $lng->txt("cmi.objectives.n.completion_status"));
1526 $logtpl->setVariable("cmi.objectives.n.progress_measure", $lng->txt("cmi.objectives.n.progress_measure"));
1527 $logtpl->setVariable("cmi.objectives.n.description", $lng->txt("cmi.objectives.n.description"));
1528 $logtpl->setVariable("cmi.progress_measure", $lng->txt("cmi.progress_measure"));
1529 $logtpl->setVariable("cmi.scaled_passing_score", $lng->txt("cmi.scaled_passing_score"));
1530 $logtpl->setVariable("cmi.score._children", $lng->txt("cmi.score._children"));
1531 $logtpl->setVariable("cmi.score.scaled", $lng->txt("cmi.score.scaled"));
1532 $logtpl->setVariable("cmi.score.raw", $lng->txt("cmi.score.raw"));
1533 $logtpl->setVariable("cmi.score.min", $lng->txt("cmi.score.min"));
1534 $logtpl->setVariable("cmi.score.max", $lng->txt("cmi.score.max"));
1535 $logtpl->setVariable("cmi.session_time", $lng->txt("cmi.session_time"));
1536 $logtpl->setVariable("cmi.success_status", $lng->txt("cmi.success_status"));
1537 $logtpl->setVariable("cmi.suspend_data", $lng->txt("cmi.suspend_data"));
1538 $logtpl->setVariable("cmi.time_limit_action", $lng->txt("cmi.time_limit_action"));
1539 $logtpl->setVariable("cmi.total_time", $lng->txt("cmi.total_time"));
1540 $logtpl->setVariable("adl.nav.request", $lng->txt("adl.nav.request"));
1541 $logtpl->setVariable("adl.nav.request_valid.continue", $lng->txt("adl.nav.request_valid.continue"));
1542 $logtpl->setVariable("adl.nav.request_valid.previous", $lng->txt("adl.nav.request_valid.previous"));
1543 $logtpl->setVariable("adl.nav.request_valid.choice", $lng->txt("adl.nav.request_valid.choice"));
1544 $logtpl->setVariable("i_green", $lng->txt("i_green"));
1545 $logtpl->setVariable("i_red", $lng->txt("i_red"));
1546 $logtpl->setVariable("i_orange", $lng->txt("i_orange"));
1547 $logtpl->setVariable("i_fuchsia", $lng->txt("i_fuchsia"));
1548 $logtpl->setVariable("i_gray", $lng->txt("i_gray"));
1549 $logtpl->setVariable("error", $lng->txt("error"));
1550 $logtpl->setVariable("strange_error", $lng->txt("strange_error"));
1551 $logtpl->setVariable("strange_API-Call", $lng->txt("strange_API-Call"));
1552 $logtpl->setVariable("unknown", $lng->txt("unknown"));
1553 $logtpl->setVariable("undefined_color", $lng->txt("undefined_color"));
1554 $logtpl->setVariable("description_for", $lng->txt("description_for"));
1555 $logtpl->setVariable("hide", $lng->txt("hide"));
1556 $logtpl->setVariable("all_API-calls_shown", $lng->txt("all_API-calls_shown"));
1557 $logtpl->setVariable("show_only_important_API-calls", $lng->txt("show_only_important_API-calls"));
1558 $logtpl->setVariable("only_important_API-Calls_shown", $lng->txt("only_important_API-Calls_shown"));
1559 $logtpl->setVariable("show_all_API-calls", $lng->txt("show_all_API-calls"));
1560 $logtpl->setVariable("log_for", $lng->txt("log_for"));
1561 $logtpl->setVariable("started", $lng->txt("started"));
1562 $logtpl->setVariable("nr_session", $lng->txt("nr_session"));
1563 $logtpl->setVariable("id_learning_module", $lng->txt("id_learning_module"));
1564 if ($this->slm->getCheck_values() == false) {
1565 $logtpl->setVariable("CHECK_VALUES", $lng->txt("sent_values_not_checked"));
1566 }
1567 $logtpl->parseCurrentBlock();
1568 fwrite($fHandle2, $logtpl->get());
1569 fclose($fHandle2);
1570 }
1571 return $filename;
1572 }
1573
1574 public function getDataDirectory2(): string
1575 {
1576 $webdir = str_replace("/ilias.php", "", $_SERVER["SCRIPT_NAME"]);
1577 //load ressources always with absolute URL..relative URLS fail on innersco navigation on certain browsers
1578 $lm_dir = $webdir . "/" . ILIAS_WEB_DIR . "/" . CLIENT_ID . "/lm_data" . "/lm_" . $this->packageId;
1579 return $lm_dir;
1580 }
1581
1582 private function logDirectory(): string
1583 {
1584 // $logDir=ilUtil::getDataDir()."/SCORMlogs"."/lm_".$this->packageId;
1585 // if (!file_exists($logDir)) ilUtil::makeDirParents($logDir);
1586 $logDir = $this->slm->getDataDirectory() . "/logs";
1587 if (!file_exists($logDir)) {
1588 ilFileUtils::makeDir($logDir);
1589 }
1590 return $logDir;
1591 }
1592
1593 public function openLog(): void
1594 {
1595 global $DIC;
1596 $filename = ilUtil::stripSlashes($DIC->http()->wrapper()->query()->retrieve('logFile', $DIC->refinery()->kindlyTo()->string()));
1597 $filename = str_replace('/', '', $filename);
1598 //Header
1599 header('Content-Type: text/html; charset=UTF-8');
1600 echo file_get_contents($this->logDirectory() . "/" . $filename);
1601 exit;
1602 }
1603
1604 public function downloadLog(): void
1605 {
1606 global $DIC;
1607 $filename = ilUtil::stripSlashes($DIC->http()->wrapper()->query()->retrieve('logFile', $DIC->refinery()->kindlyTo()->string()));
1608 $filename = str_replace('/', '', $filename);
1609 //Header
1610 header("Expires: 0");
1611 header("Cache-Control: private");
1612 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
1613 header("Pragma: cache");
1614 header("Content-Description: File Transfer");
1615 header("Content-Type: application/octet-stream");
1616 header("Content-disposition: attachment; filename=$filename");
1617 echo file_get_contents($this->logDirectory() . "/" . $filename);
1618 exit;
1619 }
1620
1625 private function getLogFileList(string $s_delete, string $s_download, string $s_open): array
1626 {
1627 global $DIC;
1628 $data = array();
1629 foreach (new DirectoryIterator($this->logDirectory()) as $fileInfo) {
1630 if ($fileInfo->isDot()) {
1631 continue;
1632 }
1633 $item['filename'] = $fileInfo->getFilename();
1634 $parts = pathinfo($item['filename']);
1635 $fnameparts = preg_split('/_/', $parts['filename'], -1, PREG_SPLIT_NO_EMPTY);
1636 $deleteUrl = '&nbsp;<a href=#' . " onclick=\"javascript:deleteFile('" . $item['filename'] . "');\">" . $s_delete . "</a>";
1637 //no delete for most recent file
1638 if (isset($fnameparts[1]) && (string) $this->get_actual_attempts() == $fnameparts[1]) {
1639 $deleteUrl = "";
1640 }
1641
1642 $urlDownload = $DIC->ctrl()->getLinkTarget($this, 'downloadLog') . '&logFile=' . $fileInfo->getFilename();
1643 $urlOpen = $DIC->ctrl()->getLinkTarget($this, 'openLog') . '&logFile=' . $fileInfo->getFilename();
1644 $item['date'] = date('Y/m/d H:i:s', $fileInfo->getCTime());
1645 if ($parts['extension'] === "html") {
1646 $item['action'] = $deleteUrl . "&nbsp;<a href=" . $urlDownload . ">" . $s_download . "</a>&nbsp;<a target=_new href=" . $urlOpen . ">" . $s_open . "</a>";
1647 } else {
1648 $item['action'] = $deleteUrl . "&nbsp;<a href=" . $urlDownload . ">" . $s_download . "</a>";
1649 }
1650 if ($parts['extension'] === "html" || $parts['extension'] === "csv") {
1651 $data[] = $item;
1652 }
1653 }
1654 usort($data, "datecmp");
1655 return $data;
1656 }
1657
1658 public function liveLogContent(): void
1659 {
1660 header('Content-Type: text/html; charset=UTF-8');
1661 print file_get_contents($this->logFileName() . ".html");
1662 }
1663
1664 public function debugGUI(): void
1665 {
1666 global $DIC;
1667 $lng = $DIC->language();
1668 $lng->loadLanguageModule("scormdebug");
1669
1670 /* if ($_POST['password'] == $this->slm->getDebugPw()) {
1671 $_SESSION["debug_pw"] = $this->slm->getDebugPw();
1672 }
1673 if ($_SESSION["debug_pw"]!=$this->slm->getDebugPw()) {
1674 $this->tpl = new ilTemplate("tpl.scorm2004.debug_pw.html", false, false, "../components/ILIAS/Scorm2004");
1675 $this->tpl->setVariable('SUBMIT', $lng->txt("debugwindow_submit"));
1676 $this->tpl->setVariable('CANCEL', $lng->txt("debugwindow_cancel"));
1677 $this->tpl->setVariable('PASSWORD_ENTER', $lng->txt("debugwindow_password_enter"));
1678 $this->tpl->setVariable('DEBUG_URL','ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=debugGUI&ref_id='.$this->ref_id);
1679 } else {*/
1680 $this->tpl = new ilGlobalTemplate("tpl.scorm2004.debug.html", false, false, "components/ILIAS/Scorm2004");
1681 $this->tpl->setVariable('CONSOLE', $lng->txt("debugwindow_console"));
1682 $this->tpl->setVariable('LOGS', $lng->txt("debugwindow_logs"));
1683 $this->tpl->setVariable('COMMENT', $lng->txt("debugwindow_comment"));
1684 $this->tpl->setVariable('COMMENT_ENTER', $lng->txt("debugwindow_comment_enter"));
1685 $this->tpl->setVariable('START_RECORDING', $lng->txt("debugwindow_start_recording"));
1686 $this->tpl->setVariable('STOP_RECORDING', $lng->txt("debugwindow_stop_recording"));
1687 $this->tpl->setVariable('DELETE_LOGFILE', $lng->txt("debugwindow_delete_logfile"));
1688 $this->tpl->setVariable('SUBMISSION_FAILED', $lng->txt("debugwindow_submission_failed"));
1689 $this->tpl->setVariable('SUBMIT', $lng->txt("debugwindow_submit"));
1690 $this->tpl->setVariable('CANCEL', $lng->txt("debugwindow_cancel"));
1691 $this->tpl->setVariable('FILENAME', $lng->txt("debugwindow_filename"));
1692 $this->tpl->setVariable('DATE', $lng->txt("debugwindow_date"));
1693 $this->tpl->setVariable('ACTION', $lng->txt("debugwindow_action"));
1694 $this->tpl->setVariable('RECORD_IMG', "components/ILIAS/Scorm2004/templates/default/images/record.png");
1695 $this->tpl->setVariable('STOP_IMG', "components/ILIAS/Scorm2004/templates/default/images/stop.png");
1696 $this->tpl->setVariable('COMMENT_IMG', "components/ILIAS/Scorm2004/templates/default/images/comment.png");
1697 $logfile = $this->logFileName() . ".html";
1698 $this->tpl->setVariable('LOGFILE', $this->logFileName() . ".html");
1699 $this->tpl->setVariable('FILES_DATA', json_encode($this->getLogFileList($lng->txt("debugwindow_delete"), $lng->txt("debugwindow_download"), $lng->txt("debugwindow_open"))));
1700 $this->tpl->setVariable('PATH_YUI', ilYuiUtil::getLocalPath());
1701 //}
1702 echo $this->tpl->get("DEFAULT", true);
1703 }
1704
1705 private function getLogTemplate(): \ilTemplate
1706 {
1707 return new ilTemplate("tpl.scorm2004.debugtxt.txt", true, true, "components/ILIAS/Scorm2004");
1708 }
1709
1713 private function getDebugValues(?bool $test_sco = false): array
1714 {
1715 global $DIC;
1716 $ilDB = $DIC->database();
1717 $ilLog = ilLoggerFactory::getLogger('sc13');
1718 $ini_array = null;
1719 $dvalues = array();
1720 /*
1721 $res = $ilDB->queryF('
1722 SELECT debug_fields
1723 FROM sahs_lm
1724 WHERE id = %s',
1725 array('integer'),
1726 array($this->packageId)
1727 );
1728 $row = $ilDB->fetchAssoc($res);
1729 $debug_fields = $row['debug_fields'];
1730 if ($debug_fields == null) {*/
1731 $debug_fields = parse_ini_file("../components/ILIAS/Scorm2004/scripts/rtemain/debug_default.ini", true);
1732 // }
1733 if ($test_sco) {
1734 $ini_array = $debug_fields['test_sco'];
1735 } else {
1736 $ini_array = $debug_fields['normal_sco'];
1737 }
1738 foreach ($ini_array as $key => $value) {
1739 if ($value == 1) {
1740 $dvalues[] = $key;
1741 }
1742 }
1743 return $dvalues;
1744 }
1745
1746 public function postLogEntry(): void
1747 {
1748 global $DIC;
1749 $ilLog = ilLoggerFactory::getLogger('sc13');
1750 $lng = $DIC->language();
1751 $lng->loadLanguageModule("scormdebug");
1752
1753 $logdata = json_decode(file_get_contents('php://input'));
1754 $filename = $this->logFileName();
1755 $tmp_name = $this->logTmpName();
1756
1757 $fh_txt = fopen($filename . ".html", 'a') or die("can't open txt file");
1758 $fh_csv = fopen($filename . ".csv", 'a') or die("can't open csv file");
1759 $fh_tmp = fopen($tmp_name, 'r') or die("can't open tmp file");
1760
1761 //init tmp file
1762 if (filesize($tmp_name) > 0) {
1763 $tmp_content = unserialize(fread($fh_tmp, filesize($tmp_name)));//Check Options - This may causes security issues if the serialized classess arent specified.
1764 } else {
1765 $tmp_content = null;
1766 }
1767
1768 fclose($fh_tmp);
1769
1770 $timestamp = date("d.m.Y H:i", time());
1771
1772 if ($logdata->action != "SUMMARY") {
1773 //reopen for writing
1774 $fh_tmp2 = fopen($tmp_name, 'w') or die("can't open tmp file");
1775
1776 //write tmp
1777 $tmp_content[$logdata->scoid][$logdata->key]['value'] = $logdata->value;
1778 $tmp_content[$logdata->scoid][$logdata->key]['status'] = $logdata->result;
1779 $tmp_content[$logdata->scoid][$logdata->key]['action'] = $logdata->action;
1780
1781 fwrite($fh_tmp2, serialize($tmp_content));
1782 fclose($fh_tmp2);
1783
1784 $errorcode = (int) $logdata->errorcode;
1785 $fixedFailure = false;
1786 $toleratedFailure = false;
1787 $extraErrorDescription = "";
1788 if ($errorcode == 200000) {
1789 $errorcode = 0;
1790 $toleratedFailure = true;
1791 $extraErrorDescription = "tolerated failure";
1792 }
1793 if ($errorcode > 99999) {
1794 $errorcode -= 100000;
1795 $fixedFailure = true;
1796 $extraErrorDescription = " failure corrected by ILIAS";
1797 }
1798 if (strpos($logdata->action, "ANALYZE") === false) {
1799 $errorDescriptions = array("0" => "",
1800 "101" => "General Exeption",
1801 "102" => "General Initialization Failure",
1802 "103" => "Already Initialized",
1803 "104" => "Content Instance Terminated",
1804 "111" => "General Termination Failure",
1805 "112" => "Termination Before Initialization",
1806 "113" => "Termination After Termination",
1807 "122" => "Retrieve Data Before Initialization",
1808 "123" => "Retrieve Data After Termination",
1809 "132" => "Store Data Before Initialization",
1810 "133" => "Store Data After Termination",
1811 "142" => "Commit Before Initialization",
1812 "143" => "Commit After Termination",
1813 "201" => "General Argument Error",
1814 "301" => "General Get Failure",
1815 "351" => "General Set Failure",
1816 "391" => "General Commit Failure",
1817 "401" => "Undefined Data Model Element",
1818 "402" => "Unimplemented Data Model Element",
1819 "403" => "Data Model Element Value Not Initialized",
1820 "404" => "Data Model Element Is Read Only",
1821 "405" => "Data Model Element Is Write Only",
1822 "406" => "Data Model Element Type Mismatch",
1823 "407" => "Data Model Element Value Out Of Range",
1824 "408" => "Data Model Dependency Not Established"
1825 );
1826 $csv_string = $this->packageId . ';"'
1827 . $logdata->scoid . '";"'
1828 . $logdata->scotitle . '";'
1829 . date("d.m.Y H:i", time()) . ';"'
1830 . $logdata->action . '";"'
1831 . $logdata->key . '";"'
1832 . str_replace("\"", "\"\"", $logdata->value) . '";"'
1833 . str_replace("\"", "\"\"", $logdata->result) . '";'
1834 . $errorcode . ';'
1835 . $logdata->timespan . ';"'
1836 . $errorDescriptions[(string) $errorcode] . $extraErrorDescription . '"' . "\n";
1837 fwrite($fh_csv, $csv_string);
1838 }
1839 }
1840
1841 $sqlwrite = false;
1842 if ($logdata->action === "Commit" || $logdata->action === "Terminate") {
1843 $sqlwrite = true;
1844 $sql_data = $this->getNodeData($logdata->scoid);
1845 if (count($sql_data) != 0) {
1846 foreach ($sql_data as $key => $value) {
1847 $sql_string = $this->packageId . ';"'
1848 . $logdata->scoid . '";"'
1849 . $logdata->scotitle . '";'
1850 . $timestamp . ';"SQL";"'
1851 . $key . '";"'
1852 . str_replace("\"", "\"\"", (string) $value) . '";;;;' . "\n";
1853 fwrite($fh_csv, $sql_string);
1854 }
1855 }
1856 }
1857
1858 //delete files
1859 if ($logdata->action === "DELETE") {
1860 $filename = $logdata->value;
1861 $filename = str_replace('/', '', $filename);
1862 $path = $this->logDirectory() . "/" . $filename;
1863 unlink($path);
1864 return;
1865 }
1866
1867 //write TXT
1868 $logtpl = $this->getLogTemplate();
1869 $color = "red";
1870 $importantkey = 1;
1871 $ArGetValues = array('comments_from_lms','completion_threshold','credit','entry','launch_data','learner_id','learner_name','max_time_allowed','mode','scaled_passing_score','time_limit_action','total_time');
1872
1873 switch ($logdata->action) {
1874 case 'SetValue':
1875 if ($logdata->result === "true" && $errorcode == 0) {
1876 $color = "green";
1877 }
1878 if ($color === "green" && $logdata->key === "cmi.exit" && $logdata->value !== "suspend") {
1879 $color = "orange";
1880 }
1881 if ($fixedFailure == false && $errorcode != 406) {
1882 $logdata->value = '"' . $logdata->value . '"';
1883 }
1884 if ($toleratedFailure == true) {
1885 $color = "fuchsia";
1886 }
1887 if ($fixedFailure == true) {
1888 $color = "gray";
1889 }
1890 break;
1891 case 'GetValue':
1892 if ($errorcode == 0) {
1893 $color = "green";
1894 }
1895 break;
1896 case 'Initialize':
1897 if ($errorcode == 0) {
1898 $color = "green";
1899 $logtpl->setCurrentBlock("InitializeStart");
1900 $logtpl->setVariable("SCO-title", $lng->txt("SCO-title"));
1901 $logtpl->setVariable("SCO_TITLE", $logdata->scotitle);
1902 $logtpl->setVariable("SCO-name", $lng->txt("SCO-name"));
1903 $logtpl->setVariable("SCO_NAME", $logdata->scoid);
1904 $logtpl->setVariable("started", $lng->txt("started"));
1905 $logtpl->setVariable("TIMESTAMP", $timestamp);
1906 $logtpl->setVariable("milliseconds", $lng->txt("milliseconds"));
1907 $logtpl->setVariable("API-call", $lng->txt("API-call"));
1908 $logtpl->setVariable("return_value", $lng->txt("return_value"));
1909 $logtpl->setVariable("error", $lng->txt("error"));
1910 $logtpl->parseCurrentBlock();
1911 }
1912 break;
1913 case 'Commit':
1914 if ($errorcode == 0) {
1915 $color = "green";
1916 }
1917 if ($fixedFailure == true) {
1918 $color = "gray";
1919 }
1920 break;
1921 case 'Terminate':
1922 if ($errorcode == 0) {
1923 $color = "green";
1924 }
1925 break;
1926 case 'GetErrorString':
1927 $importantkey = 0;
1928 if ($errorcode == 0) {
1929 $color = "green";
1930 }
1931 break;
1932 case 'GetLastError':
1933 $logtpl->setCurrentBlock("GetLastError");
1934 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
1935 $logtpl->setVariable("RESULT", $logdata->result);
1936 $logtpl->parseCurrentBlock();
1937 break;
1938 case 'GetDiagnostic':
1939 $logtpl->setCurrentBlock("GetDiagnostic");
1940 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
1941 $logtpl->setVariable("KEY", $logdata->key);
1942 $logtpl->setVariable("RESULT", $logdata->result);
1943 $logtpl->parseCurrentBlock();
1944 break;
1945 case 'INFO':
1946 $logtpl->setCurrentBlock("INFO");
1947 $logtpl->setVariable("hint", $lng->txt("hint"));
1948 $logtpl->setVariable("KEY", $lng->txt($logdata->key));
1949 $logtpl->setVariable("VALUE", $logdata->value);
1950 $logtpl->parseCurrentBlock();
1951 break;
1952 case 'COMMENT':
1953 $logtpl->setCurrentBlock("COMMENT");
1954 $logtpl->setVariable("comment", $lng->txt("comment"));
1955 $logtpl->setVariable("generated", $lng->txt("generated"));
1956 $logtpl->setVariable("TIMESTAMP", $timestamp);
1957 $logtpl->setVariable("VALUE", $logdata->value);
1958 $logtpl->parseCurrentBlock();
1959 break;
1960 case 'ANALYZE':
1961 $logtpl->setCurrentBlock("ANALYZE");
1962 if (count($logdata->value) == 0) {
1963 $color = "green";
1964 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
1965 $logtpl->setVariable("VALUE", "");
1966 } else {
1967 $tmpvalue = "SetValue(\"" . implode("\", ... ),<br/>SetValue(\"", $logdata->value) . "\", ... )";
1968 foreach ($ArGetValues as $value) {
1969 $tmpvalue = str_replace("SetValue(\"cmi." . $value . "\", ... )", "GetValue(\"cmi." . $value . "\")", $tmpvalue);
1970 }
1971 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
1972 $logtpl->setVariable("VALUE", $tmpvalue);
1973 }
1974 $logtpl->setVariable("summary_for_SCO_without_test", $lng->txt("summary_for_SCO_without_test"));
1975 $logtpl->setVariable("generated", $lng->txt("generated"));
1976 $logtpl->setVariable("TIMESTAMP", $timestamp);
1977 $logtpl->setVariable("COLOR", $color);
1978 $logtpl->parseCurrentBlock();
1979 break;
1980 case 'ANALYZETEST':
1981 $logtpl->setCurrentBlock("ANALYZETEST");
1982 if (count($logdata->value) == 0) {
1983 $color = "green";
1984 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
1985 $logtpl->setVariable("VALUE", "");
1986 } else {
1987 $tmpvalue = "SetValue(\"" . implode("\", ... ),<br/>SetValue(\"", $logdata->value) . "\", ... )";
1988 foreach ($ArGetValues as $value) {
1989 $tmpvalue = str_replace("SetValue(\"cmi." . $value . "\", ... )", "GetValue(\"cmi." . $value . "\")", $tmpvalue);
1990 }
1991 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
1992 $logtpl->setVariable("VALUE", $tmpvalue);
1993 }
1994 $logtpl->setVariable("summary_for_SCO_with_test", $lng->txt("summary_for_SCO_with_test"));
1995 $logtpl->setVariable("generated", $lng->txt("generated"));
1996 $logtpl->setVariable("TIMESTAMP", $timestamp);
1997 $logtpl->setVariable("COLOR", $color);
1998 $logtpl->parseCurrentBlock();
1999 break;
2000 case 'SUMMARY':
2001 $logtpl->setCurrentBlock("SUMMARY");
2002 $logtpl->setVariable("summary_csv", $lng->txt("summary_csv"));
2003 $logtpl->setVariable("TIMESTAMP", $timestamp);
2004 $logtpl->setVariable("summary_download", $lng->txt("summary_download"));
2005 $logtpl->parseCurrentBlock();
2006 break;
2007 default:
2008 $importantkey = 0;
2009 $color = "orange";
2010 break;
2011 }
2012 if ($logdata->action === 'SetValue' || $logdata->action === 'GetValue') {
2013 $logtpl->setCurrentBlock($logdata->action);
2014 $logtpl->setVariable("ACTION", $logdata->action);
2015 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2016 $logtpl->setVariable("KEY", $logdata->key);
2017 $logtpl->setVariable("VALUE", $logdata->value);
2018 $logtpl->setVariable("RESULT", $logdata->result);
2019 $logtpl->setVariable("ERRORCODE", $errorcode);
2020 $debugfields = $this->getDebugValues(true);
2021 $importantkey = 0;
2022 foreach ($debugfields as $value) {
2023 if ($logdata->key == $value) {
2024 $importantkey = 1;
2025 }
2026 }
2027 $logtpl->setVariable("IMPORTANTKEY", "" . $importantkey);
2028 $logtpl->setVariable("COLOR", $color);
2029 $logtpl->parseCurrentBlock();
2030 } elseif ($logdata->action !== 'INFO' && $logdata->action !== 'ANALYZE' && $logdata->action !== 'ANALYZETEST' && $logdata->action !== 'SUMMARY' && $logdata->action !== 'COMMENT' && $logdata->action !== 'GetDiagnostic' && $logdata->action !== 'GetLastError') {
2031 $logtpl->setCurrentBlock("defaultCall");
2032 $logtpl->setVariable("ACTION", $logdata->action);
2033 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2034 $logtpl->setVariable("KEY", $logdata->key);
2035 $logtpl->setVariable("VALUE", $logdata->value);
2036 $logtpl->setVariable("RESULT", $logdata->result);
2037 $logtpl->setVariable("ERRORCODE", $errorcode);
2038 $logtpl->setVariable("IMPORTANTKEY", "" . $importantkey);
2039 $logtpl->setVariable("COLOR", $color);
2040 $logtpl->parseCurrentBlock();
2041 }
2042
2043 /*
2044 if ($sqlwrite == true) {
2045 $ilLog->write("SQL WRITE");
2046 $logtpl->setCurrentBlock("SqlLog");
2047 $logtpl->setVariable("SQL_STRING", $sql_text);
2048 $logtpl->parseCurrentBlock();
2049 }
2050 */
2051
2052 //create summary
2053 if ($logdata->action === "SUMMARY") {
2054 $this->createSummary($tmp_content);
2055 }
2056
2057 fwrite($fh_txt, $logtpl->get());
2058 fclose($fh_txt);
2059 fclose($fh_csv);
2060 }
2061
2062 private function getStructureFlat(array $data): void
2063 {
2064 foreach ($data as $i => $value) {
2065 $element = array();
2066 $element['title'] = $value['title'];
2067 $element['id'] = $value['id'];
2068 if ($value['sco'] == 1) {
2069 $element['sco'] = "sco";
2070 } else {
2071 $element['sco'] = "asset";
2072 }
2073 if ($value['href'] != null) {
2074 $this->flat_structure[] = $element;
2075 }
2076 if (isset($value['item']) && $value['item'] != null) {
2077 $this->getStructureFlat($value['item']);
2078 }
2079 }
2080 }
2081
2082 private function createSummary(array $api_data): void
2083 {
2084 global $DIC;
2085 $ilDB = $DIC->database();
2086
2087 $csv_data = null;
2088 //csv columns
2089 $columns_fixed = array('id','title','type','attempted');
2090
2091 $ini_data = parse_ini_file("./components/ILIAS/Scorm2004/scripts/rtemain/debug_default.ini", true);
2092 $ini_array = $ini_data['summary'];
2093 $colums_variable = array();
2094 $api_keys = array();
2095
2096 foreach ($ini_array as $key => $value) {
2097 if ($value == 1) {
2098 $colums_variable[] = $key;
2099 $api_keys[] = $key;
2100 $colums_variable[] = "Status";
2101 }
2102 }
2103
2104 $header_array = array_merge($columns_fixed, $colums_variable);
2105
2106 $csv_header = implode(";", $header_array);
2107
2108 //get strcuture
2109 $res = $ilDB->queryF(
2110 'SELECT jsdata FROM cp_package WHERE obj_id = %s',
2111 array('integer'),
2112 array($this->packageId)
2113 );
2114
2115 $packageData = $ilDB->fetchAssoc($res);
2116
2117 $structure = json_decode($packageData['jsdata'], true);
2118
2119
2120 $this->flat_structure = array(); //used for recursion
2121 if (isset($structure['item']) && isset($structure['item']['item'])) {
2122 $this->getStructureFlat($structure['item']['item']);
2123
2124 foreach ($this->flat_structure as $tree_element) {
2125 $csv_data = $csv_data . $tree_element['id'] . ";" . $tree_element['title'] . ";" . $tree_element['sco'] . ";";
2126 if (isset($api_data[$tree_element['id']])) {
2127 $csv_data = $csv_data . "X" . ";";
2128 } else {
2129 $csv_data = $csv_data . ";";
2130 }
2131
2132 //write api data
2133 $id = $tree_element['id'];
2134 foreach ($api_keys as $api_element) {
2135 if (isset($api_data[$id])) {
2136 if (isset($api_data[$id][$api_element])) {
2137 $csv_data = $csv_data . $api_data[$id][$api_element]['value'] . ";" . $api_data[$id][$api_element]['status'] . ";";
2138 } else {
2139 $csv_data = $csv_data . ";;";
2140 }
2141 }
2142 }
2143 $csv_data = $csv_data . "\n";
2144 }
2145 }
2146 $fh = fopen($this->summaryFileName(), "wb"); //changed from w to wb
2147 fwrite($fh, $csv_header . "\n" . $csv_data);
2148 fclose($fh);
2149 unlink($this->logTmpName());
2150 }
2155 // function get_last_visited($a_obj_id, $a_user_id)
2156 // {
2157 // global $DIC;
2158 // $ilDB = $DIC->database();
2159 // $val_set = $ilDB->queryF('SELECT last_visited FROM sahs_user WHERE obj_id = %s AND user_id = %s',
2160 // array('integer','integer'),
2161 // array($a_obj_id,$a_user_id));
2162
2163 // $val_rec = $ilDB->fetchAssoc($val_set);
2164 // return $val_rec["last_visited"];
2165 // }
2166}
2167
2168function datecmp(array $a, array $b): int
2169{
2170 if (strtotime($a['date']) == strtotime($b['date'])) {
2171 return 0;
2172 }
2173 return (strtotime($a['date']) < strtotime($b['date'])) ? 1 : -1;
2174}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$structure
TOTAL STRUCTURE.
$filename
Definition: buildRTE.php:78
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:70
datecmp(array $a, array $b)
Class ilCtrl provides processing control methods.
static makeDir(string $a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
special template class to simplify handling of ITX/PEAR
language handling
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.
Class ilObjSCORM2004LearningModule.
static getStatus(int $a_packageId, int $a_user_id, bool $auto_last_visited, string $scormType="1.2")
static _lookupObjectId(int $ref_id)
writeSharedData(int $sco_node_id)
getDebugValues(?bool $test_sco=false)
getCMIData(int $userId, int $packageId)
getLogFileList(string $s_delete, string $s_download, string $s_open)
get_actual_attempts()
Get number of actual attempts for the user.
readSharedData(int $sco_node_id)
ilObjSCORM2004LearningModule $slm
increase_attemptAndsave_module_version()
Increases attempts by one and saves module_version for this package.
static persistCMIData(int $packageId, int $refId, string $defaultLessonMode, bool $comments, bool $interactions, bool $objectives, bool $time_from_lms, ?string $data=null, ?int $userId=null)
static scormPlayerUnload(int $packageId, int $refId, bool $time_from_lms, ?int $userId=null)
const int DEFAULT_MIN_IDLE
default value for settings that have not been defined in setup or administration yet
static getIdleValue()
Returns the idle time in seconds.
special template class to simplify handling of ITX/PEAR
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
static getStyleSheetLocation(string $mode="output", string $a_css_name="")
get full style sheet file name (path inclusive) of current user
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
static signFolderOfStartFile(string $start_file_path)
static getCookieMaxLifetimeInSeconds()
static getLocalPath(string $a_name="")
Get local path of a YUI js file.
static getLocaljQueryPath()
const CLIENT_ID
Definition: constants.php:41
const ILIAS_WEB_DIR
Definition: constants.php:45
exit
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
$scope
Definition: ltiregstart.php:51
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
global $lng
Definition: privfeed.php:31
global $ilSetting
Definition: privfeed.php:31
$_SERVER['HTTP_HOST']
Definition: raiseError.php:26
$ilErr
Definition: raiseError.php:33
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:23
$packageId
Definition: storeScorm.php:24