ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
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 // $this->tpl->setVariable('TREE_JS', "../components/ILIAS/UIComponent/NestedList/js/ilNestedList.js");
468 $this->tpl->setVariable('TREE_JS', "components/ILIAS/Scorm2004/scripts/ilNestedList.js");
469 foreach ($langstrings as $key => $value) {
470 $this->tpl->setVariable($key, $value);
471 }
472 $this->tpl->setVariable('DOC_TITLE', 'ILIAS: ' . $this->slm->getTitle());
473 $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
474 $this->tpl->setVariable('INIT_CP_DATA', json_encode(json_decode($this->getCPDataInit())));
475 $this->tpl->setVariable('INIT_CMI_DATA', json_encode($this->getCMIData($this->userId, $this->packageId)));
476 $this->tpl->setVariable('INIT_ADLACT_DATA', json_encode($initAdlactData));
477 $this->tpl->setVariable('INIT_GLOBALOBJ_DATA', json_encode($initGlobalobjData));
478 $this->tpl->setVariable('JS_DATA', json_encode($config));
479 list($tsfrac, $tsint) = explode(' ', microtime());
480 $this->tpl->setVariable('TIMESTAMP', sprintf('%d%03d', $tsint, 1000 * (float) $tsfrac));
481 $this->tpl->setVariable('BASE_DIR', '../components/ILIAS/Scorm2004/');
482 $this->tpl->setVariable('TXT_COLLAPSE', $lng->txt('scplayer_collapsetree'));
483 if ($this->slm->getDebug()) {
484 $this->tpl->setVariable('TXT_DEBUGGER', $lng->txt('scplayer_debugger'));
485 $debug_url = $DIC->ctrl()->getLinkTarget($this, 'debugGUI');
486 $this->tpl->setVariable('DEBUG_URL', "PopupCenter('" . $debug_url . "','Debug',800,600);");
487 } else {
488 $this->tpl->setVariable('TXT_DEBUGGER', '');
489 $this->tpl->setVariable('DEBUG_URL', '');
490 }
491
492 //set icons path
493 $this->tpl->setVariable('INLINE_CSS', ilSCORM13PlayerGUI::getInlineCss());
494
495 //include scripts
496 if ($this->slm->getCacheDeactivated()) {
497 $rtejs_url = $DIC->ctrl()->getLinkTarget($this, 'getRTEjs');
498 $this->tpl->setVariable('JS_SCRIPTS', $rtejs_url);
499 } else {
500 $this->tpl->setVariable('JS_SCRIPTS', 'components/ILIAS/Scorm2004/scripts/buildrte/rte-min.js');
501 }
502
503 //disable top menu
504 if ($this->slm->getNoMenu() === true) {
505 $this->tpl->setVariable("VAL_DISPLAY", "style=\"display:none;\"");
506 } else {
507 $this->tpl->setVariable("VAL_DISPLAY", "");
508 }
509
510
511 //check for max_attempts and raise error if max_attempts is exceeded
512 // if ($this->get_max_attempts() != 0) {
513 // if ($this->get_actual_attempts() >= $this->get_max_attempts()) {
514 // header('Content-Type: text/html; charset=utf-8');
515 // echo($lng->txt("cont_sc_max_attempt_exceed"));
516 // exit;
517 // }
518 // }
519
520 //count attempt
522 $this->resetSharedData();
523
524 $this->tpl->printToStdout("DEFAULT", false);
525 }
526
527 public static function getInlineCSS(): string
528 {
529 $is_tpl = new ilTemplate("tpl.scorm2004.inlinecss.html", true, true, "components/ILIAS/Scorm2004");
530 $is_tpl->setVariable('IC_ASSET', ilUtil::getImagePath("scorm/asset.svg", ""));
531 $is_tpl->setVariable('IC_COMPLETED', ilUtil::getImagePath("scorm/completed.svg", ""));
532 $is_tpl->setVariable('IC_NOTATTEMPTED', ilUtil::getImagePath("scorm/not_attempted.svg", ""));
533 $is_tpl->setVariable('IC_RUNNING', ilUtil::getImagePath("scorm/running.svg", ""));
534 $is_tpl->setVariable('IC_INCOMPLETE', ilUtil::getImagePath("scorm/incomplete.svg", ""));
535 $is_tpl->setVariable('IC_PASSED', ilUtil::getImagePath("scorm/passed.svg", ""));
536 $is_tpl->setVariable('IC_FAILED', ilUtil::getImagePath("scorm/failed.svg", ""));
537 $is_tpl->setVariable('IC_BROWSED', ilUtil::getImagePath("scorm/browsed.svg", ""));
538 return $is_tpl->get();
539 }
540
541 public function getCPData(): void
542 {
543 $jsdata = $this->getCPDataInit();
544 if ($this->jsMode) {
545 header('Content-Type: text/javascript; charset=UTF-8');
546 print($jsdata);
547 } else {
548 header('Content-Type: text/plain; charset=UTF-8');
549 $jsdata = json_decode($jsdata);
550 print_r($jsdata);
551 }
552 }
553
554 public function getCPDataInit(): string
555 {
556 global $DIC;
557 $ilDB = $DIC->database();
558
559 $res = $ilDB->queryF(
560 'SELECT jsdata FROM cp_package WHERE obj_id = %s',
561 array('integer'),
562 array($this->packageId)
563 );
564 $packageData = $ilDB->fetchAssoc($res);
565
566 $jsdata = $packageData['jsdata'] ?? false;
567 if (!$jsdata) {
568 $jsdata = 'null';
569 }
570
571 return $jsdata;
572 }
573
574
575 public function getADLActDataInit(): string
576 {
577 global $DIC;
578 $ilDB = $DIC->database();
579
580 $res = $ilDB->queryF(
581 'SELECT activitytree FROM cp_package WHERE obj_id = %s',
582 array('integer'),
583 array($this->packageId)
584 );
585 $data = $ilDB->fetchAssoc($res);
586
587 $activitytree = $data['activitytree'] ?? false;
588
589 if (!$activitytree) {
590 $activitytree = 'null';
591 }
592 return $activitytree;
593 }
594
595 public function getADLActData(): void
596 {
597 $activitytree = $this->getADLActDataInit();
598 if ($this->jsMode) {
599 header('Content-Type: text/javascript; charset=UTF-8');
600 print($activitytree);
601 } else {
602 header('Content-Type: text/plain; charset=UTF-8');
603 $activitytree = json_decode($activitytree);
604 print_r($activitytree);
605 }
606 }
607
608 public function pingSession(): void
609 {
610 ilWACSignedPath::signFolderOfStartFile($this->getDataDirectory() . '/imsmanifest.xml');
611 //do nothing except returning header
612 header('Content-Type: text/plain; charset=UTF-8');
613 print("");
614 }
615
616 public function getScope(): string
617 {
618 global $DIC;
619 $ilDB = $DIC->database();
620 $ilUser = $DIC->user();
621
622 $res = $ilDB->queryF(
623 'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
624 array('integer'),
625 array($this->packageId)
626 );
627 $data = $ilDB->fetchAssoc($res);
628
629 $gystem = $data['global_to_system'] ?? 1;
630 if ($gystem == 1) {
631 $gsystem = 'null';
632 } else {
633 $gsystem = (string) $this->packageId;
634 }
635
636 return $gsystem;
637 }
638
639 public function getSuspendDataInit(): string
640 {
641 global $DIC;
642 $ilDB = $DIC->database();
643 $ilUser = $DIC->user();
644
645 $res = $ilDB->queryF(
646 'SELECT data FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
647 array('integer', 'integer'),
648 array($this->packageId, $ilUser->getId())
649 );
650 $data = $ilDB->fetchAssoc($res);
651
652 //delete delivered suspend data
653 $ilDB->manipulateF(
654 'DELETE FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
655 array('integer', 'integer'),
656 array($this->packageId, $ilUser->getId())
657 );
658 if (is_array($data)) {
659 return $data['data'];
660 }
661 return "";
662 }
663
664 public function getSuspendData(): void
665 {
666 $suspend_data = $this->getSuspendDataInit();
667 if ($this->jsMode) {
668 header('Content-Type: text/javascript; charset=UTF-8');
669 print($suspend_data);
670 } else {
671 header('Content-Type: text/plain; charset=UTF-8');
672 $suspend_data = json_decode($suspend_data);
673 print_r($suspend_data);
674 }
675 }
676
677 public function suspendADLActData(): void
678 {
679 global $DIC;
680 $ilDB = $DIC->database();
681 $ilUser = $DIC->user();
682
683 $res = $ilDB->queryF(
684 'SELECT * FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
685 array('integer', 'integer'),
686 array($this->packageId, $ilUser->getId())
687 );
688
689 if (!$ilDB->numRows($res)) {
690 $ilDB->insert('cp_suspend', array(
691 'data' => array('clob', file_get_contents('php://input')),
692 'obj_id' => array('integer', $this->packageId),
693 'user_id' => array('integer', $ilUser->getId())
694 ));
695 } else {
696 $ilDB->update(
697 'cp_suspend',
698 array(
699 'data' => array('clob', file_get_contents('php://input'))
700 ),
701 array(
702 'obj_id' => array('integer', $this->packageId),
703 'user_id' => array('integer', $ilUser->getId())
704 )
705 );
706 }
707 }
708
709 public function readGObjectiveInit(): array
710 {
711 global $DIC;
712 $ilDB = $DIC->database();
713 $ilUser = $DIC->user();
714
715 //get json string
716 $g_data = [];
717
718 $global_to_system = 1;
719
720 $res = $ilDB->queryF(
721 'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
722 array('integer'),
723 array($this->packageId)
724 );
725 while ($data = $ilDB->fetchAssoc($res)) {
726 $global_to_system = $data['global_to_system'];
727 }
728
729 $query = 'SELECT objective_id, scope_id, satisfied, measure, user_id,
730 score_min, score_max, score_raw, completion_status,
731 progress_measure '
732 . 'FROM cmi_gobjective, cp_node, cp_mapinfo '
733 . 'WHERE (cmi_gobjective.objective_id <> %s AND cmi_gobjective.status IS NULL '
734 . 'AND cp_node.slm_id = %s AND cp_node.nodename = %s '
735 . 'AND cp_node.cp_node_id = cp_mapinfo.cp_node_id '
736 . 'AND cmi_gobjective.objective_id = cp_mapinfo.targetobjectiveid) '
737 . 'GROUP BY objective_id, scope_id, satisfied, measure, user_id,
738 score_min, score_max, score_raw, completion_status,
739 progress_measure';
740 $res = $ilDB->queryF(
741 $query,
742 array('text', 'integer', 'text'),
743 array('-course_overall_status-', $this->packageId, 'mapInfo')
744 );
745 while ($row = $ilDB->fetchAssoc($res)) {
746 if (($global_to_system == 1 && $row['scope_id'] == 0) || ($global_to_system == 0 && $row['scope_id'] == $this->packageId)) {
747 $learner = $row['user_id'];
748 $objective_id = $row['objective_id'];
749 if ($row['scope_id'] == 0) {
750 $scope = "null";
751 } else {
752 $scope = $row['scope_id'];
753 }
754
755 if ($row['satisfied'] != null) {
756 $toset = $row['satisfied'];
757 $g_data["satisfied"][$objective_id][$learner][$scope] = $toset;
758 }
759
760 if ($row['measure'] != null) {
761 $toset = $row['measure'];
762 $g_data["measure"][$objective_id][$learner][$scope] = $toset;
763 }
764
765 if ($row['score_raw'] != null) {
766 $toset = $row['score_raw'];
767 $g_data["score_raw"][$objective_id][$learner][$scope] = $toset;
768 }
769
770 if ($row['score_min'] != null) {
771 $toset = $row['score_min'];
772 $g_data["score_min"][$objective_id][$learner][$scope] = $toset;
773 }
774
775 if ($row['score_max'] != null) {
776 $toset = $row['score_max'];
777 $g_data["score_max"][$objective_id][$learner][$scope] = $toset;
778 }
779
780 if ($row['progress_measure'] != null) {
781 $toset = $row['progress_measure'];
782 $g_data["progress_measure"][$objective_id][$learner][$scope] = $toset;
783 }
784
785 if ($row['completion_status'] != null) {
786 $toset = $row['completion_status'];
787 $g_data["completion_status"][$objective_id][$learner][$scope] = $toset;
788 }
789 }
790 }
791 return $g_data;
792 }
793
794 public function readGObjective(): void
795 {
796 $gobjective_data = json_encode($this->readGObjectiveInit());
797 if ($this->jsMode) {
798 header('Content-Type: text/javascript; charset=UTF-8');
799 print($gobjective_data);
800 } else {
801 header('Content-Type: text/plain; charset=UTF-8');
802 $gobjective_data = json_decode($gobjective_data);
803 print_r($gobjective_data);
804 }
805 }
806
807
808 //Read the shared datascores for a given SCO
809 public function readSharedData(int $sco_node_id): void
810 {
811 global $DIC;
812 $ilDB = $DIC->database();
813 $ilUser = $DIC->user();
814 $dataStores = array( "data" => array(),
815 "permissions" => array());
816 $readPermissions = array();
817
818 $query = 'SELECT target_id, read_shared_data, write_shared_data '
819 . 'FROM cp_datamap '
820 . 'WHERE slm_id = %s '
821 . 'AND sco_node_id = %s '
822 . 'GROUP BY target_id, read_shared_data, write_shared_data';
823
824
825 $res = $ilDB->queryF(
826 $query,
827 array('integer', 'integer'),
828 array($this->packageId, $sco_node_id)
829 );
830
831 //Pass 1: Get all the shared data target_ids
832 // for this content package
833 while ($row = $ilDB->fetchAssoc($res)) {
834 $storeVal = ($row['read_shared_data'] == 0 && $row['write_shared_data'] == 1)
835 ? 'notWritten'
836 : null;
837
838 $dataStores["data"][$row['target_id']] = array( "store" => $storeVal,
839 "readSharedData" => $row['read_shared_data'],
840 "writeSharedData" => $row['write_shared_data']);
841 $dataStores["readPermissions"][$row['target_id']] = $row['read_shared_data'];
842 }
843
844 if (count($dataStores) < 1) {
845 //If there are no datastores, then return nothing
846 echo "";
847 exit();
848 }
849
850 if (isset($dataStores["readPermissions"]) && $dataStores["readPermissions"] != null && array_sum($dataStores["readPermissions"]) != 0) {
851 //If there exists at least one readSharedData permission, then
852 //fill in the existing values (if any) already in the store.
853
854 //Create the params to add to the Pass 2 query (get existing values)
855 $params = array("types" => array("integer", "integer"),
856 "values" => array($this->userId, $this->packageId));
857
858 $paramTemplate = '';
859
860 //See if readSharedData is set for each datamap.
861 //If set to true, then add it to the search query
862 foreach ($dataStores["data"] as $key => $val) {
863 if ($dataStores["readPermissions"][(string) $key] == 1
864 && $dataStores["data"][(string) $key]["store"] !== 'notWritten') {
865 $params["types"][] = "text";
866 $params["values"][] = $key;
867 $paramTemplate .= '%s, ';
868 }
869 }
870
871 //Get rid of the trailing ', '
872 $paramTemplate = substr($paramTemplate, 0, -2);
873
874 //Pass 2: Query for values previously saved by the user
875 $query = 'SELECT target_id, store '
876 . 'FROM adl_shared_data '
877 . 'WHERE user_id = %s '
878 . 'AND slm_id = %s '
879 . 'AND target_id IN (' . $paramTemplate . ')';
880
881
882 $res = $ilDB->queryF(
883 $query,
884 $params["types"],
885 $params["values"]
886 );
887
888 while ($row = $ilDB->fetchAssoc($res)) {
889 $dataStores["data"][$row['target_id']]["store"] = $row['store'];
890 }
891 }
892
893 header('Content-Type: text/javascript; charset=UTF-8');
894
895 echo json_encode($dataStores["data"]);
896 }
897
898 public function writeSharedData(int $sco_node_id): void
899 {
900 global $DIC;
901 $ilDB = $DIC->database();
902 $ilUser = $DIC->user();
903 $g_data = json_decode(file_get_contents('php://input'));
904
905 if ($g_data == null) {
906 return;
907 }
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 //}
1701 echo $this->tpl->get("DEFAULT", true);
1702 }
1703
1704 private function getLogTemplate(): \ilTemplate
1705 {
1706 return new ilTemplate("tpl.scorm2004.debugtxt.txt", true, true, "components/ILIAS/Scorm2004");
1707 }
1708
1712 private function getDebugValues(?bool $test_sco = false): array
1713 {
1714 global $DIC;
1715 $ilDB = $DIC->database();
1716 $ilLog = ilLoggerFactory::getLogger('sc13');
1717 $ini_array = null;
1718 $dvalues = array();
1719 /*
1720 $res = $ilDB->queryF('
1721 SELECT debug_fields
1722 FROM sahs_lm
1723 WHERE id = %s',
1724 array('integer'),
1725 array($this->packageId)
1726 );
1727 $row = $ilDB->fetchAssoc($res);
1728 $debug_fields = $row['debug_fields'];
1729 if ($debug_fields == null) {*/
1730 $debug_fields = parse_ini_file("../components/ILIAS/Scorm2004/scripts/rtemain/debug_default.ini", true);
1731 // }
1732 if ($test_sco) {
1733 $ini_array = $debug_fields['test_sco'];
1734 } else {
1735 $ini_array = $debug_fields['normal_sco'];
1736 }
1737 foreach ($ini_array as $key => $value) {
1738 if ($value == 1) {
1739 $dvalues[] = $key;
1740 }
1741 }
1742 return $dvalues;
1743 }
1744
1745 public function postLogEntry(): void
1746 {
1747 global $DIC;
1748 $ilLog = ilLoggerFactory::getLogger('sc13');
1749 $lng = $DIC->language();
1750 $lng->loadLanguageModule("scormdebug");
1751
1752 $logdata = json_decode(file_get_contents('php://input'));
1753 $filename = $this->logFileName();
1754 $tmp_name = $this->logTmpName();
1755
1756 $fh_txt = fopen($filename . ".html", 'a') or die("can't open txt file");
1757 $fh_csv = fopen($filename . ".csv", 'a') or die("can't open csv file");
1758 $fh_tmp = fopen($tmp_name, 'r') or die("can't open tmp file");
1759
1760 //init tmp file
1761 if (filesize($tmp_name) > 0) {
1762 $tmp_content = unserialize(fread($fh_tmp, filesize($tmp_name)));//Check Options - This may causes security issues if the serialized classess arent specified.
1763 } else {
1764 $tmp_content = null;
1765 }
1766
1767 fclose($fh_tmp);
1768
1769 $timestamp = date("d.m.Y H:i", time());
1770
1771 if ($logdata->action != "SUMMARY") {
1772 //reopen for writing
1773 $fh_tmp2 = fopen($tmp_name, 'w') or die("can't open tmp file");
1774
1775 //write tmp
1776 $tmp_content[$logdata->scoid][$logdata->key]['value'] = $logdata->value;
1777 $tmp_content[$logdata->scoid][$logdata->key]['status'] = $logdata->result;
1778 $tmp_content[$logdata->scoid][$logdata->key]['action'] = $logdata->action;
1779
1780 fwrite($fh_tmp2, serialize($tmp_content));
1781 fclose($fh_tmp2);
1782
1783 $errorcode = (int) $logdata->errorcode;
1784 $fixedFailure = false;
1785 $toleratedFailure = false;
1786 $extraErrorDescription = "";
1787 if ($errorcode == 200000) {
1788 $errorcode = 0;
1789 $toleratedFailure = true;
1790 $extraErrorDescription = "tolerated failure";
1791 }
1792 if ($errorcode > 99999) {
1793 $errorcode -= 100000;
1794 $fixedFailure = true;
1795 $extraErrorDescription = " failure corrected by ILIAS";
1796 }
1797 if (strpos($logdata->action, "ANALYZE") === false) {
1798 $errorDescriptions = array("0" => "",
1799 "101" => "General Exeption",
1800 "102" => "General Initialization Failure",
1801 "103" => "Already Initialized",
1802 "104" => "Content Instance Terminated",
1803 "111" => "General Termination Failure",
1804 "112" => "Termination Before Initialization",
1805 "113" => "Termination After Termination",
1806 "122" => "Retrieve Data Before Initialization",
1807 "123" => "Retrieve Data After Termination",
1808 "132" => "Store Data Before Initialization",
1809 "133" => "Store Data After Termination",
1810 "142" => "Commit Before Initialization",
1811 "143" => "Commit After Termination",
1812 "201" => "General Argument Error",
1813 "301" => "General Get Failure",
1814 "351" => "General Set Failure",
1815 "391" => "General Commit Failure",
1816 "401" => "Undefined Data Model Element",
1817 "402" => "Unimplemented Data Model Element",
1818 "403" => "Data Model Element Value Not Initialized",
1819 "404" => "Data Model Element Is Read Only",
1820 "405" => "Data Model Element Is Write Only",
1821 "406" => "Data Model Element Type Mismatch",
1822 "407" => "Data Model Element Value Out Of Range",
1823 "408" => "Data Model Dependency Not Established"
1824 );
1825 $csv_string = $this->packageId . ';"'
1826 . $logdata->scoid . '";"'
1827 . $logdata->scotitle . '";'
1828 . date("d.m.Y H:i", time()) . ';"'
1829 . $logdata->action . '";"'
1830 . $logdata->key . '";"'
1831 . str_replace("\"", "\"\"", $logdata->value) . '";"'
1832 . str_replace("\"", "\"\"", $logdata->result) . '";'
1833 . $errorcode . ';'
1834 . $logdata->timespan . ';"'
1835 . $errorDescriptions[(string) $errorcode] . $extraErrorDescription . '"' . "\n";
1836 fwrite($fh_csv, $csv_string);
1837 }
1838 }
1839
1840 $sqlwrite = false;
1841 if ($logdata->action === "Commit" || $logdata->action === "Terminate") {
1842 $sqlwrite = true;
1843 $sql_data = $this->getNodeData($logdata->scoid);
1844 if (count($sql_data) != 0) {
1845 foreach ($sql_data as $key => $value) {
1846 $sql_string = $this->packageId . ';"'
1847 . $logdata->scoid . '";"'
1848 . $logdata->scotitle . '";'
1849 . $timestamp . ';"SQL";"'
1850 . $key . '";"'
1851 . str_replace("\"", "\"\"", (string) $value) . '";;;;' . "\n";
1852 fwrite($fh_csv, $sql_string);
1853 }
1854 }
1855 }
1856
1857 //delete files
1858 if ($logdata->action === "DELETE") {
1859 $filename = $logdata->value;
1860 $filename = str_replace('/', '', $filename);
1861 $path = $this->logDirectory() . "/" . $filename;
1862 unlink($path);
1863 return;
1864 }
1865
1866 //write TXT
1867 $logtpl = $this->getLogTemplate();
1868 $color = "red";
1869 $importantkey = 1;
1870 $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');
1871
1872 switch ($logdata->action) {
1873 case 'SetValue':
1874 if ($logdata->result === "true" && $errorcode == 0) {
1875 $color = "green";
1876 }
1877 if ($color === "green" && $logdata->key === "cmi.exit" && $logdata->value !== "suspend") {
1878 $color = "orange";
1879 }
1880 if ($fixedFailure == false && $errorcode != 406) {
1881 $logdata->value = '"' . $logdata->value . '"';
1882 }
1883 if ($toleratedFailure == true) {
1884 $color = "fuchsia";
1885 }
1886 if ($fixedFailure == true) {
1887 $color = "gray";
1888 }
1889 break;
1890 case 'GetValue':
1891 if ($errorcode == 0) {
1892 $color = "green";
1893 }
1894 break;
1895 case 'Initialize':
1896 if ($errorcode == 0) {
1897 $color = "green";
1898 $logtpl->setCurrentBlock("InitializeStart");
1899 $logtpl->setVariable("SCO-title", $lng->txt("SCO-title"));
1900 $logtpl->setVariable("SCO_TITLE", $logdata->scotitle);
1901 $logtpl->setVariable("SCO-name", $lng->txt("SCO-name"));
1902 $logtpl->setVariable("SCO_NAME", $logdata->scoid);
1903 $logtpl->setVariable("started", $lng->txt("started"));
1904 $logtpl->setVariable("TIMESTAMP", $timestamp);
1905 $logtpl->setVariable("milliseconds", $lng->txt("milliseconds"));
1906 $logtpl->setVariable("API-call", $lng->txt("API-call"));
1907 $logtpl->setVariable("return_value", $lng->txt("return_value"));
1908 $logtpl->setVariable("error", $lng->txt("error"));
1909 $logtpl->parseCurrentBlock();
1910 }
1911 break;
1912 case 'Commit':
1913 if ($errorcode == 0) {
1914 $color = "green";
1915 }
1916 if ($fixedFailure == true) {
1917 $color = "gray";
1918 }
1919 break;
1920 case 'Terminate':
1921 if ($errorcode == 0) {
1922 $color = "green";
1923 }
1924 break;
1925 case 'GetErrorString':
1926 $importantkey = 0;
1927 if ($errorcode == 0) {
1928 $color = "green";
1929 }
1930 break;
1931 case 'GetLastError':
1932 $logtpl->setCurrentBlock("GetLastError");
1933 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
1934 $logtpl->setVariable("RESULT", $logdata->result);
1935 $logtpl->parseCurrentBlock();
1936 break;
1937 case 'GetDiagnostic':
1938 $logtpl->setCurrentBlock("GetDiagnostic");
1939 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
1940 $logtpl->setVariable("KEY", $logdata->key);
1941 $logtpl->setVariable("RESULT", $logdata->result);
1942 $logtpl->parseCurrentBlock();
1943 break;
1944 case 'INFO':
1945 $logtpl->setCurrentBlock("INFO");
1946 $logtpl->setVariable("hint", $lng->txt("hint"));
1947 $logtpl->setVariable("KEY", $lng->txt($logdata->key));
1948 $logtpl->setVariable("VALUE", $logdata->value);
1949 $logtpl->parseCurrentBlock();
1950 break;
1951 case 'COMMENT':
1952 $logtpl->setCurrentBlock("COMMENT");
1953 $logtpl->setVariable("comment", $lng->txt("comment"));
1954 $logtpl->setVariable("generated", $lng->txt("generated"));
1955 $logtpl->setVariable("TIMESTAMP", $timestamp);
1956 $logtpl->setVariable("VALUE", $logdata->value);
1957 $logtpl->parseCurrentBlock();
1958 break;
1959 case 'ANALYZE':
1960 $logtpl->setCurrentBlock("ANALYZE");
1961 if (count($logdata->value) == 0) {
1962 $color = "green";
1963 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
1964 $logtpl->setVariable("VALUE", "");
1965 } else {
1966 $tmpvalue = "SetValue(\"" . implode("\", ... ),<br/>SetValue(\"", $logdata->value) . "\", ... )";
1967 foreach ($ArGetValues as $value) {
1968 $tmpvalue = str_replace("SetValue(\"cmi." . $value . "\", ... )", "GetValue(\"cmi." . $value . "\")", $tmpvalue);
1969 }
1970 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
1971 $logtpl->setVariable("VALUE", $tmpvalue);
1972 }
1973 $logtpl->setVariable("summary_for_SCO_without_test", $lng->txt("summary_for_SCO_without_test"));
1974 $logtpl->setVariable("generated", $lng->txt("generated"));
1975 $logtpl->setVariable("TIMESTAMP", $timestamp);
1976 $logtpl->setVariable("COLOR", $color);
1977 $logtpl->parseCurrentBlock();
1978 break;
1979 case 'ANALYZETEST':
1980 $logtpl->setCurrentBlock("ANALYZETEST");
1981 if (count($logdata->value) == 0) {
1982 $color = "green";
1983 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
1984 $logtpl->setVariable("VALUE", "");
1985 } else {
1986 $tmpvalue = "SetValue(\"" . implode("\", ... ),<br/>SetValue(\"", $logdata->value) . "\", ... )";
1987 foreach ($ArGetValues as $value) {
1988 $tmpvalue = str_replace("SetValue(\"cmi." . $value . "\", ... )", "GetValue(\"cmi." . $value . "\")", $tmpvalue);
1989 }
1990 $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
1991 $logtpl->setVariable("VALUE", $tmpvalue);
1992 }
1993 $logtpl->setVariable("summary_for_SCO_with_test", $lng->txt("summary_for_SCO_with_test"));
1994 $logtpl->setVariable("generated", $lng->txt("generated"));
1995 $logtpl->setVariable("TIMESTAMP", $timestamp);
1996 $logtpl->setVariable("COLOR", $color);
1997 $logtpl->parseCurrentBlock();
1998 break;
1999 case 'SUMMARY':
2000 $logtpl->setCurrentBlock("SUMMARY");
2001 $logtpl->setVariable("summary_csv", $lng->txt("summary_csv"));
2002 $logtpl->setVariable("TIMESTAMP", $timestamp);
2003 $logtpl->setVariable("summary_download", $lng->txt("summary_download"));
2004 $logtpl->parseCurrentBlock();
2005 break;
2006 default:
2007 $importantkey = 0;
2008 $color = "orange";
2009 break;
2010 }
2011 if ($logdata->action === 'SetValue' || $logdata->action === 'GetValue') {
2012 $logtpl->setCurrentBlock($logdata->action);
2013 $logtpl->setVariable("ACTION", $logdata->action);
2014 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2015 $logtpl->setVariable("KEY", $logdata->key);
2016 $logtpl->setVariable("VALUE", $logdata->value);
2017 $logtpl->setVariable("RESULT", $logdata->result);
2018 $logtpl->setVariable("ERRORCODE", $errorcode);
2019 $debugfields = $this->getDebugValues(true);
2020 $importantkey = 0;
2021 foreach ($debugfields as $value) {
2022 if ($logdata->key == $value) {
2023 $importantkey = 1;
2024 }
2025 }
2026 $logtpl->setVariable("IMPORTANTKEY", "" . $importantkey);
2027 $logtpl->setVariable("COLOR", $color);
2028 $logtpl->parseCurrentBlock();
2029 } elseif ($logdata->action !== 'INFO' && $logdata->action !== 'ANALYZE' && $logdata->action !== 'ANALYZETEST' && $logdata->action !== 'SUMMARY' && $logdata->action !== 'COMMENT' && $logdata->action !== 'GetDiagnostic' && $logdata->action !== 'GetLastError') {
2030 $logtpl->setCurrentBlock("defaultCall");
2031 $logtpl->setVariable("ACTION", $logdata->action);
2032 $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2033 $logtpl->setVariable("KEY", $logdata->key);
2034 $logtpl->setVariable("VALUE", $logdata->value);
2035 $logtpl->setVariable("RESULT", $logdata->result);
2036 $logtpl->setVariable("ERRORCODE", $errorcode);
2037 $logtpl->setVariable("IMPORTANTKEY", "" . $importantkey);
2038 $logtpl->setVariable("COLOR", $color);
2039 $logtpl->parseCurrentBlock();
2040 }
2041
2042 /*
2043 if ($sqlwrite == true) {
2044 $ilLog->write("SQL WRITE");
2045 $logtpl->setCurrentBlock("SqlLog");
2046 $logtpl->setVariable("SQL_STRING", $sql_text);
2047 $logtpl->parseCurrentBlock();
2048 }
2049 */
2050
2051 //create summary
2052 if ($logdata->action === "SUMMARY") {
2053 $this->createSummary($tmp_content);
2054 }
2055
2056 fwrite($fh_txt, $logtpl->get());
2057 fclose($fh_txt);
2058 fclose($fh_csv);
2059 }
2060
2061 private function getStructureFlat(array $data): void
2062 {
2063 foreach ($data as $i => $value) {
2064 $element = array();
2065 $element['title'] = $value['title'];
2066 $element['id'] = $value['id'];
2067 if ($value['sco'] == 1) {
2068 $element['sco'] = "sco";
2069 } else {
2070 $element['sco'] = "asset";
2071 }
2072 if ($value['href'] != null) {
2073 $this->flat_structure[] = $element;
2074 }
2075 if (isset($value['item']) && $value['item'] != null) {
2076 $this->getStructureFlat($value['item']);
2077 }
2078 }
2079 }
2080
2081 private function createSummary(array $api_data): void
2082 {
2083 global $DIC;
2084 $ilDB = $DIC->database();
2085
2086 $csv_data = null;
2087 //csv columns
2088 $columns_fixed = array('id','title','type','attempted');
2089
2090 $ini_data = parse_ini_file("./components/ILIAS/Scorm2004/scripts/rtemain/debug_default.ini", true);
2091 $ini_array = $ini_data['summary'];
2092 $colums_variable = array();
2093 $api_keys = array();
2094
2095 foreach ($ini_array as $key => $value) {
2096 if ($value == 1) {
2097 $colums_variable[] = $key;
2098 $api_keys[] = $key;
2099 $colums_variable[] = "Status";
2100 }
2101 }
2102
2103 $header_array = array_merge($columns_fixed, $colums_variable);
2104
2105 $csv_header = implode(";", $header_array);
2106
2107 //get strcuture
2108 $res = $ilDB->queryF(
2109 'SELECT jsdata FROM cp_package WHERE obj_id = %s',
2110 array('integer'),
2111 array($this->packageId)
2112 );
2113
2114 $packageData = $ilDB->fetchAssoc($res);
2115
2116 $structure = json_decode($packageData['jsdata'], true);
2117
2118
2119 $this->flat_structure = array(); //used for recursion
2120 if (isset($structure['item']) && isset($structure['item']['item'])) {
2121 $this->getStructureFlat($structure['item']['item']);
2122
2123 foreach ($this->flat_structure as $tree_element) {
2124 $csv_data = $csv_data . $tree_element['id'] . ";" . $tree_element['title'] . ";" . $tree_element['sco'] . ";";
2125 if (isset($api_data[$tree_element['id']])) {
2126 $csv_data = $csv_data . "X" . ";";
2127 } else {
2128 $csv_data = $csv_data . ";";
2129 }
2130
2131 //write api data
2132 $id = $tree_element['id'];
2133 foreach ($api_keys as $api_element) {
2134 if (isset($api_data[$id])) {
2135 if (isset($api_data[$id][$api_element])) {
2136 $csv_data = $csv_data . $api_data[$id][$api_element]['value'] . ";" . $api_data[$id][$api_element]['status'] . ";";
2137 } else {
2138 $csv_data = $csv_data . ";;";
2139 }
2140 }
2141 }
2142 $csv_data = $csv_data . "\n";
2143 }
2144 }
2145 $fh = fopen($this->summaryFileName(), "wb"); //changed from w to wb
2146 fwrite($fh, $csv_header . "\n" . $csv_data);
2147 fclose($fh);
2148 unlink($this->logTmpName());
2149 }
2154 // function get_last_visited($a_obj_id, $a_user_id)
2155 // {
2156 // global $DIC;
2157 // $ilDB = $DIC->database();
2158 // $val_set = $ilDB->queryF('SELECT last_visited FROM sahs_user WHERE obj_id = %s AND user_id = %s',
2159 // array('integer','integer'),
2160 // array($a_obj_id,$a_user_id));
2161
2162 // $val_rec = $ilDB->fetchAssoc($val_set);
2163 // return $val_rec["last_visited"];
2164 // }
2165}
2166
2167function datecmp(array $a, array $b): int
2168{
2169 if (strtotime($a['date']) == strtotime($b['date'])) {
2170 return 0;
2171 }
2172 return (strtotime($a['date']) < strtotime($b['date'])) ? 1 : -1;
2173}
$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 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:27