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