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