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