ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
ilSCORM13Player.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 require_once("./Services/YUI/classes/class.ilYuiUtil.php");
6 require_once("./Modules/Scorm2004/classes/class.ilObjSCORM2004LearningModule.php");
7 
14 {
15 
16  const ENABLE_GZIP = 0;
17 
18  const NONE = 0;
19  const READONLY = 1;
20  const WRITEONLY = 2;
21  const READWRITE = 3;
22 
23  static private $schema = array // order of entries matters!
24  (
25  'package' => array(
26  'user_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>user_id),
27  'learner_name' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>learner_name),
28  'slm_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>slm_id),
29  'mode' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>c_mode),
30  'credit' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>credit),
31  ),
32  'node' => array(
33  'accesscount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accesscount),
34  'accessduration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accessduration),
35  'accessed' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accessed),
36  'activityAbsoluteDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityabsduration),
37  'activityAttemptCount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityattemptcount),
38  'activityExperiencedDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityexpduration),
39  'activityProgressStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityprogstatus),
40  'attemptAbsoluteDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptabsduration),
41  'attemptCompletionAmount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptcomplamount),
42  'attemptCompletionStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptcomplstatus),
43  'attemptExperiencedDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptexpduration),
44  'attemptProgressStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptprogstatus),
45  'audio_captioning' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>audio_captioning),
46  'audio_level' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>audio_level),
47  'availableChildren' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>availablechildren),
48  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
49  'completion' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion),
50  'completion_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_status),
51  'completion_threshold' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_threshold),
52  'cp_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cp_node_id),
53  'created' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>created),
54  'credit' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>credit),
55  'delivery_speed' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>delivery_speed),
56  'entry' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_entry),
57  'exit' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_exit),
58  'language' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_language),
59  'launch_data' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>launch_data),
60  'learner_name' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>learner_name),
61  'location' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>location),
62  'max' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_max),
63  'min' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_min),
64  'mode' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_mode),
65  'modified' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>modified),
66  'progress_measure' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>progress_measure),
67  'raw' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_raw),
68  'scaled' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled),
69  'scaled_passing_score' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled_passing_score),
70  'session_time' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>session_time),
71  'success_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>success_status),
72  'suspend_data' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>suspend_data),
73  'total_time' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>total_time),
74  'user_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>user_id),
75  ),
76  'comment' => array (
77  'cmi_comment_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_comment_id),
78  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
79  'comment' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_comment),
80  'timestamp' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_timestamp),
81  'location' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>location),
82  'sourceIsLMS' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>sourceislms),
83  ),
84  'correct_response' => array(
85  'cmi_correct_response_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_correct_resp_id),
86  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
87  'pattern' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>pattern),
88  ),
89  'interaction' => array(
90  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
91  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
92  'description' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>description),
93  'id' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>id),
94  'latency' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>latency),
95  'learner_response' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>learner_response),
96  'result' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>result),
97  'timestamp' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_timestamp),
98  'type' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_type),
99  'weighting' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>weighting),
100  ),
101  'objective' => array(
102  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
103  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
104  'cmi_objective_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_objective_id),
105  'completion_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_status),
106  'description' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>description),
107  'id' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>id),
108  'max' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_max),
109  'min' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_min),
110  'raw' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_raw),
111  'scaled' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled),
112  'progress_measure' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>progress_measure),
113  'success_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>success_status),
114  'scope' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scope),
115  ),
116  );
117 
118  private $userId;
119  public $packageId;
120  public $jsMode;
121 
122  var $ilias;
123  var $slm;
124  var $tpl;
125 
126  function __construct()
127  {
128 
129  global $ilias, $tpl, $ilCtrl, $ilUser, $lng;
130 
131  //erase next?
132  if ($_REQUEST['learnerId']) {
133  $this->userId = $_REQUEST['learnerId'];
134  } else {
135  $this->userId = $GLOBALS['USER']['usr_id'];
136  }
137  $this->packageId = (int) $_REQUEST['packageId'];
138  $this->jsMode = strpos($_SERVER['HTTP_ACCEPT'], 'text/javascript')!==false;
139 
140  $this->page = $_REQUEST['page'];
141 
142  $this->slm =& new ilObjSCORM2004LearningModule($_GET["ref_id"], true);
143 
144 
145  $this->ilias =& $ilias;
146  $this->tpl =& $tpl;
147  $this->ctrl =& $ilCtrl;
148 
149  $this->packageId=ilObject::_lookupObjectId($_GET['ref_id']);
150  $this->ref_id = $_GET['ref_id'];
151  $this->userId=$ilUser->getID();
152 
153  if ($_GET['envEditor'] != null) {
154  $this->envEditor = $_GET['envEditor'];
155  } else {
156  $this->envEditor = 0;
157  }
158 
159  }
160 
164  function &executeCommand()
165  {
166  global $ilAccess, $ilLog, $ilUser, $lng, $ilias;
167 
168  $next_class = $this->ctrl->getNextClass($this);
169  $cmd = $this->ctrl->getCmd();
170 
171  if (!$ilAccess->checkAccess("read", "", $_GET["ref_id"]))
172  {
173  $ilias->raiseError($lng->txt("permission_denied"), $ilias->error_obj->WARNING);
174  }
175 
176 //$ilLog->write("SCORM2004 Player cmd: ".$cmd);
177 
178  switch($cmd){
179 
180  case 'getRTEjs':
181  $this->getRTEjs();
182  break;
183 
184  case 'cp':
185  $this->getCPData();
186  break;
187 
188  case 'adlact':
189  $this->getADLActData();
190  break;
191 
192  case 'suspend':
193  $this->suspendADLActData();
194  break;
195 
196  case 'getSuspend':
197  $this->getSuspendData();
198  break;
199 
200  case 'gobjective':
201 // $this->writeGObjective();
202  break;
203 
204  case 'getGobjective':
205  $this->readGObjective();
206  break;
207 
208  case 'getSharedData':
209  $this->readSharedData($_GET['node_id']);
210  break;
211 
212  case 'setSharedData':
213  $this->writeSharedData($_GET['node_id']);
214  break;
215 
216  case 'cmi':
217 
218  if ($_SERVER['REQUEST_METHOD']=='POST') {
219  $this->persistCMIData();
220  //error_log("Saved CMI Data");
221  } else {
222  $this->fetchCMIData();
223  }
224  break;
225 
226  case 'specialPage':
227  $this->specialPage();
228  break;
229 
230  case 'debugGUI':
231  $this->debugGUI();
232  break;
233  case 'postLogEntry':
234  $this->postLogEntry();
235  break;
236  case 'liveLogContent':
237  $this->liveLogContent();
238  break;
239  case 'downloadLog':
240  $this->downloadLog();
241  break;
242  case 'openLog':
243  $this->openLog();
244  break;
245 
246  case 'pingSession':
247  $this->pingSession();
248  break;
249  case 'scormPlayerUnload':
250  $this->scormPlayerUnload();
251  break;
252 
253  default:
254  $this->getPlayer();
255  break;
256  }
257 
258  }
259 
260  function getRTEjs()
261  {
262  $js_data = file_get_contents("./Modules/Scorm2004/scripts/buildrte/rte.js");
263  if (self::ENABLE_GZIP==1) {
264  ob_start("ob_gzhandler");
265  header('Content-Type: text/javascript; charset=UTF-8');
266  } else {
267  header('Content-Type: text/javascript; charset=UTF-8');
268  }
269  echo $js_data;
270  }
271 
272 
273  function getDataDirectory()
274  {
275  $webdir=str_replace("/ilias.php","",$_SERVER["SCRIPT_NAME"]);
276  //load ressources always with absolute URL..relative URLS fail on innersco navigation on certain browsers
277  $lm_dir=$webdir."/".ILIAS_WEB_DIR."/".$this->ilias->client_id ."/lm_data"."/lm_".$this->packageId;
278  return $lm_dir;
279  }
280 
281 
282 
283  public function getPlayer()
284  {
285  global $ilUser,$lng, $ilias, $ilSetting;
286  // player basic config data
287 
288  if ($this->slm->getSession()) {
289  $session_timeout = (int)($ilias->ini->readVariable("session","expire"))/2;
290  } else {
291  $session_timeout = 0;
292  }
293 
294  $config = array
295  (
296  'cp_url' => 'ilias.php?baseClass=ilSAHSPresentationGUI' . '&cmd=cp&ref_id='.$_GET["ref_id"],
297  'cmi_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=cmi&ref_id='.$_GET["ref_id"],
298  'get_adldata_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getSharedData&ref_id='.$_GET["ref_id"],
299  'set_adldata_url' => 'ilias.php?baseClass=ilSAHSPresentationGUI' . '&cmd=setSharedData&ref_id=' . $_GET["ref_id"],
300  'adlact_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=adlact&ref_id='.$_GET["ref_id"],
301  'specialpage_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=specialPage&ref_id='.$_GET["ref_id"],
302  'suspend_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=suspend&ref_id='.$_GET["ref_id"],
303  'get_suspend_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getSuspend&ref_id='.$_GET["ref_id"],
304  //next 2 lines could be deleted later
305  'gobjective_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=gobjective&ref_id='.$_GET["ref_id"],
306  'get_gobjective_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getGobjective&ref_id='.$_GET["ref_id"],
307  'ping_url' =>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=pingSession&ref_id='.$_GET["ref_id"],
308  'scorm_player_unload_url' =>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=scormPlayerUnload&ref_id='.$_GET["ref_id"],
309  'scope'=>$this->getScope(),
310  'learner_id' => (string) $ilUser->getID(),
311  'course_id' => (string) $this->packageId,
312  'learner_name' => $ilUser->getFirstname()." ".$ilUser->getLastname(),
313  'mode' => 'normal',
314  'credit' => 'credit',
315  'auto_review' => $this->slm->getAutoReview(),
316  'hide_navig' => $this->slm->getHideNavig(),
317  'debug' => $this->slm->getDebug(),
318  'package_url' => $this->getDataDirectory()."/",
319  'session_ping' => $session_timeout,
320  'envEditor' => $this->envEditor,
321  'post_log_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=postLogEntry&ref_id='.$_GET["ref_id"],
322  'livelog_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=liveLogContent&ref_id='.$_GET["ref_id"],
323  'debug_fields' => $this->getDebugValues(),
324  'debug_fields_test' => $this->getDebugValues(true),
325  'sequencing_enabled' => $this->slm->getSequencing(),
326  'interactions_storable' => $this->slm->getInteractions(),
327  'objectives_storable' => $this->slm->getObjectives(),
328  'comments_storable' => $this->slm->getComments(),
329  'time_from_lms' => $this->slm->getTime_from_lms(),
330  'auto_last_visited' => $this->slm->getAuto_last_visited(),
331  'checkSetValues' => $this->slm->getCheck_values()
332  );
333 
334  $status['saved_global_status']="";//not yet implemented
335  $status['last_visited']=null;
336  if($this->slm->getAuto_last_visited()) $status['last_visited']=$this->get_last_visited($this->packageId, $ilUser->getID());
337  $config['status'] = $status;
338 
339  //language strings
340  $langstrings['btnStart'] = $lng->txt('scplayer_start');
341  $langstrings['btnExit'] = $lng->txt('scplayer_exit');
342  $langstrings['btnExitAll'] = $lng->txt('scplayer_exitall');
343  $langstrings['btnSuspendAll'] = $lng->txt('scplayer_suspendall');
344  $langstrings['btnPrevious'] = $lng->txt('scplayer_previous');
345  $langstrings['btnContinue'] = $lng->txt('scplayer_continue');
346  $langstrings['btnhidetree']=$lng->txt('scplayer_hidetree');
347  $langstrings['btnshowtree']=$lng->txt('scplayer_showtree');
348  $langstrings['linkexpandTree']=$lng->txt('scplayer_expandtree');
349  $langstrings['linkcollapseTree']=$lng->txt('scplayer_collapsetree');
350  $config['langstrings'] = $langstrings;
351 
352  //template variables
353  //$this->tpl = new ilTemplate("tpl.scorm2004.player.html", false, false, "Modules/Scorm2004");
354  $this->tpl = new ilTemplate("tpl.scorm2004.player.html", true, true, "Modules/Scorm2004");
355 
356  // include ilias rte css, if given
357  $rte_css = $this->slm->getDataDirectory()."/ilias_css_4_2/css/style.css";
358  if (is_file($rte_css))
359  {
360  $this->tpl->setCurrentBlock("rte_css");
361  $this->tpl->setVariable("RTE_CSS", $rte_css);
362  $this->tpl->parseCurrentBlock();
363  }
364 
365  $this->tpl->setVariable('JSON_LANGSTRINGS', json_encode($langstrings));
366  include_once("./Services/YUI/classes/class.ilYuiUtil.php");
367  $this->tpl->setVariable('YUI_PATH', ilYuiUtil::getLocalPath());
368  $this->tpl->setVariable('TREE_JS', "./Services/UIComponent/NestedList/js/ilNestedList.js");
369  $this->tpl->setVariable($langstrings);
370  $this->tpl->setVariable('DOC_TITLE', 'ILIAS SCORM 2004 Player');
371  $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
372  $this->tpl->setVariable('JS_DATA', json_encode($config));
373  list($tsfrac, $tsint) = explode(' ', microtime());
374  $this->tpl->setVariable('TIMESTAMP', sprintf('%d%03d', $tsint, 1000*(float)$tsfrac));
375  $this->tpl->setVariable('BASE_DIR', './Modules/Scorm2004/');
376  $this->tpl->setVariable('TXT_COLLAPSE',$lng->txt('scplayer_collapsetree'));
377  if ($this->slm->getDebug()) {
378  $this->tpl->setVariable('TXT_DEBUGGER',$lng->txt('scplayer_debugger'));
379  $this->tpl->setVariable('DEBUG_URL',"PopupCenter('ilias.php?baseClass=ilSAHSPresentationGUI&cmd=debugGUI&ref_id=".$_GET["ref_id"]."','Debug',800,600);");
380  } else {
381  $this->tpl->setVariable('TXT_DEBUGGER','');
382  $this->tpl->setVariable('DEBUG_URL','');
383  }
384 
385  //set icons path
386  $this->tpl->setVariable('INLINE_CSS', ilSCORM13Player::getInlineCss());
387 
388  //include scripts
389  if ($this->slm->getCacheDeactivated()){
390  $this->tpl->setVariable('JS_SCRIPTS', 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getRTEjs&ref_id='.$_GET["ref_id"]);
391  } else {
392  $this->tpl->setVariable('JS_SCRIPTS', './Modules/Scorm2004/scripts/buildrte/rte-min.js');
393  }
394 
395  //disable top menu
396  if ($this->slm->getNoMenu()=="y") {
397  $this->tpl->setVariable("VAL_DISPLAY", "style=\"display:none;\"");
398  } else {
399  $this->tpl->setVariable("VAL_DISPLAY", "");
400  }
401 
402 
403  //check for max_attempts and raise error if max_attempts is exceeded
404  if ($this->get_max_attempts()!=0) {
405  if ($this->get_actual_attempts() >= $this->get_max_attempts()) {
406  header('Content-Type: text/html; charset=utf-8');
407  echo($lng->txt("cont_sc_max_attempt_exceed"));
408  exit;
409  }
410  }
411 
412  //count attempt
413  //Cause there is no way to check if the Java-Applet is sucessfully loaded, an attempt equals opening the SCORM player
414 
415  $this->increase_attempt();
416  $this->resetSharedData();
417  $this->save_module_version();
418 
419  $this->tpl->show("DEFAULT", false);
420  }
421 
425  function getInlineCSS()
426  {
427  $is_tpl = new ilTemplate("tpl.scorm2004.inlinecss.html", true, true, "Modules/Scorm2004");
428  $is_tpl->setVariable('IC_ASSET', ilUtil::getImagePath("scorm/asset_s.png",false));
429  $is_tpl->setVariable('IC_COMPLETED', ilUtil::getImagePath("scorm/completed_s.png",false));
430  $is_tpl->setVariable('IC_NOTATTEMPTED', ilUtil::getImagePath("scorm/not_attempted_s.png",false));
431  $is_tpl->setVariable('IC_RUNNING', ilUtil::getImagePath("scorm/running_s.png",false));
432  $is_tpl->setVariable('IC_INCOMPLETE', ilUtil::getImagePath("scorm/incomplete_s.png",false));
433  $is_tpl->setVariable('IC_PASSED', ilUtil::getImagePath("scorm/passed_s.png",false));
434  $is_tpl->setVariable('IC_FAILED', ilUtil::getImagePath("scorm/failed_s.png",false));
435  $is_tpl->setVariable('IC_BROWSED', ilUtil::getImagePath("scorm/browsed.png",false));
436  return $is_tpl->get();
437  }
438 
439  public function getCPData()
440  {
441  global $ilDB;
442 
443  $res = $ilDB->queryF(
444  'SELECT jsdata FROM cp_package WHERE obj_id = %s',
445  array('integer'),
446  array($this->packageId)
447  );
448  $packageData = $ilDB->fetchAssoc($res);
449 
450  $jsdata = $packageData['jsdata'];
451  if (!$jsdata) $jsdata = 'null';
452  if ($this->jsMode)
453  {
454  header('Content-Type: text/javascript; charset=UTF-8');
455  print($jsdata);
456  }
457  else
458  {
459  header('Content-Type: text/plain; charset=UTF-8');
460  $jsdata = json_decode($jsdata);
461  print_r($jsdata);
462  }
463  }
464 
465  public function getADLActData()
466  {
467  global $ilDB;
468 
469  $res = $ilDB->queryF(
470  'SELECT activitytree FROM cp_package WHERE obj_id = %s',
471  array('integer'),
472  array($this->packageId)
473  );
474  $data = $ilDB->fetchAssoc($res);
475 
476  $activitytree = $data['activitytree'];
477 
478  if(!$activitytree)
479  {
480  $activitytree = 'null';
481  }
482  if($this->jsMode)
483  {
484  header('Content-Type: text/javascript; charset=UTF-8');
485  print($activitytree);
486  }
487  else
488  {
489  header('Content-Type: text/plain; charset=UTF-8');
490  $activitytree = json_decode($activitytree);
491  print_r($activitytree);
492  }
493  }
494 
495  public function pingSession()
496  {
497  //do nothing except returning header
498  header('Content-Type: text/plain; charset=UTF-8');
499  print("");
500  }
501 
502  public function scormPlayerUnload()
503  {
504  global $ilUser;
505  $data = json_decode(is_string($data) ? $data : file_get_contents('php://input'));
506  if($data && is_string($data) && $data!="")
507  $this->set_last_visited($this->packageId, $this->userId, $data);
508 
509  include_once("./Modules/Scorm2004/classes/class.ilSCORM2004Tracking.php");
510  ilSCORM2004Tracking::_syncReadEvent($this->packageId, $this->userId, "sahs", $this->ref_id);
511 
512  header('Content-Type: text/plain; charset=UTF-8');
513  print("");
514  }
515  public function getScope()
516  {
517  global $ilDB, $ilUser;
518 
519  $res = $ilDB->queryF(
520  'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
521  array('integer'),
522  array($this->packageId)
523  );
524  $data = $ilDB->fetchAssoc($res);
525 
526  $gystem = $data['global_to_system'];
527  if($gystem == 1)
528  $gsystem = 'null';
529  else
530  $gsystem = $this->packageId;
531 
532  return $gsystem;
533  }
534 
535  public function getSuspendData()
536  {
537  global $ilDB, $ilUser;
538 
539  $res = $ilDB->queryF(
540  'SELECT data FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
541  array('integer', 'integer'),
542  array($this->packageId, $ilUser->getId())
543  );
544  $data = $ilDB->fetchAssoc($res);
545 
546  $suspend_data = $data['data'];
547  if($this->jsMode)
548  {
549  header('Content-Type: text/javascript; charset=UTF-8');
550  print($suspend_data);
551  }
552  else
553  {
554  header('Content-Type: text/plain; charset=UTF-8');
555  $suspend_data = json_decode($suspend_data);
556  print_r($suspend_data);
557  }
558 
559  //delete delivered suspend data
560  $ilDB->manipulateF(
561  'DELETE FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
562  array('integer', 'integer'),
563  array($this->packageId, $ilUser->getId())
564  );
565  }
566 
567  public function suspendADLActData()
568  {
569  global $ilDB, $ilUser;
570 
571  $res = $ilDB->queryF(
572  'SELECT * FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
573  array('integer', 'integer'),
574  array($this->packageId, $ilUser->getId())
575  );
576 
577  if(!$ilDB->numRows($res))
578  {
579  $ilDB->insert('cp_suspend', array(
580  'data' => array('clob', file_get_contents('php://input')),
581  'obj_id' => array('integer', $this->packageId),
582  'user_id' => array('integer', $ilUser->getId())
583  ));
584  }
585  else
586  {
587  $ilDB->update('cp_suspend',
588  array(
589  'data' => array('clob', file_get_contents('php://input'))
590  ),
591  array(
592  'obj_id' => array('integer', $this->packageId),
593  'user_id' => array('integer', $ilUser->getId())
594  )
595  );
596  }
597  }
598 
599  public function readGObjective()
600  {
601  global $ilDB, $ilUser, $ilLog;
602 
603  //get json string
604  $g_data = new stdClass();
605 
606  $global_to_system = 1;
607 
608  $res = $ilDB->queryF('SELECT global_to_system FROM cp_package WHERE obj_id = %s',
609  array('integer'),
610  array($this->packageId)
611  );
612  while($data = $ilDB->fetchAssoc($res))
613  {
614  $global_to_system = $data['global_to_system'];
615  }
616 
617  $query = 'SELECT objective_id, scope_id, satisfied, measure, user_id,
618  score_min, score_max, score_raw, completion_status,
619  progress_measure '
620  . 'FROM cmi_gobjective, cp_node, cp_mapinfo '
621  . 'WHERE (cmi_gobjective.objective_id <> %s AND cmi_gobjective.status IS NULL '
622  . 'AND cp_node.slm_id = %s AND cp_node.nodename = %s '
623  . 'AND cp_node.cp_node_id = cp_mapinfo.cp_node_id '
624  . 'AND cmi_gobjective.objective_id = cp_mapinfo.targetobjectiveid) '
625  . 'GROUP BY objective_id, scope_id, satisfied, measure, user_id,
626  score_min, score_max, score_raw, completion_status,
627  progress_measure';
628  $res = $ilDB->queryF(
629  $query,
630  array('text', 'integer', 'text'),
631  array('-course_overall_status-', $this->packageId, 'mapInfo')
632  );
633  while($row = $ilDB->fetchAssoc($res))
634  {
635  if (($global_to_system == 1 && $row['scope_id'] == 0) || ($global_to_system == 0 && $row['scope_id'] == $this->packageId))
636  {
637  $learner = $row['user_id'];
638  $objective_id = $row['objective_id'];
639  if($row['scope_id'] == 0)
640  {
641  $scope = "null";
642  }
643  else
644  {
645  $scope = $row['scope_id'];
646  }
647 
648  if($row['satisfied'] != NULL)
649  {
650  $toset = $row['satisfied'];
651  $g_data->{"satisfied"}->{$objective_id}->{$learner}->{$scope} = $toset;
652  }
653 
654  if($row['measure'] != NULL)
655  {
656  $toset = $row['measure'];
657  $g_data->{"measure"}->{$objective_id}->{$learner}->{$scope} = $toset;
658  }
659 
660  if($row['score_raw'] != NULL)
661  {
662  $toset = $row['score_raw'];
663  $g_data->{"score_raw"}->{$objective_id}->{$learner}->{$scope} = $toset;
664  }
665 
666  if($row['score_min'] != NULL)
667  {
668  $toset = $row['score_min'];
669  $g_data->{"score_min"}->{$objective_id}->{$learner}->{$scope} = $toset;
670  }
671 
672  if($row['score_max'] != NULL)
673  {
674  $toset = $row['score_max'];
675  $g_data->{"score_max"}->{$objective_id}->{$learner}->{$scope} = $toset;
676  }
677 
678  if($row['progress_measure'] != NULL)
679  {
680  $toset = $row['progress_measure'];
681  $g_data->{"progress_measure"}->{$objective_id}->{$learner}->{$scope} = $toset;
682  }
683 
684  if($row['completion_status'] != NULL)
685  {
686  $toset = $row['completion_status'];
687  $g_data->{"completion_status"}->{$objective_id}->{$learner}->{$scope} = $toset;
688  }
689 
690  }
691  }
692  $gobjective_data = json_encode($g_data);
693  $ilLog->write("SCORM2004 gobjective_data=".$gobjective_data);
694  if ($this->jsMode)
695  {
696  header('Content-Type: text/javascript; charset=UTF-8');
697  print($gobjective_data);
698  }
699  else
700  {
701  header('Content-Type: text/plain; charset=UTF-8');
702  $gobjective_data = json_decode($gobjective_data);
703  print_r($gobjective_data);
704  }
705  }
706 
707  //saves global_objectives to database
708  public function writeGObjective($g_data)
709  {
710  global $ilDB, $ilUser, $ilLog;
711  $ilLog->write("SCORM2004 writeGObjective");
712  $user = $ilUser->getId();
713  $package = $this->packageId;
714 
715  //get json string
716 // $g_data = json_decode(file_get_contents('php://input'));
717 
718  //iterate over assoziative array
719  if($g_data == null)
720  return null;
721 
722  $rows_to_insert = Array();
723 
724  foreach($g_data as $key => $value)
725  {
726  $ilLog->write("SCORM2004 writeGObjective -key: ".$key);
727  //objective
728  //learner = ilias learner id
729  //scope = null / course
730  foreach($value as $skey => $svalue)
731  {
732  $ilLog->write("SCORM2004 writeGObjective -skey: ".$skey);
733  //we always have objective and learner id
734  if($g_data->$key->$skey->$user->$package)
735  {
736  $o_value = $g_data->$key->$skey->$user->$package;
737  $scope = $package;
738  }
739  else //UK: is this okay? can $scope=0 and $user->{"null"}; when is $scope used?
740  {
741  //scope 0
742  $o_value = $g_data->$key->$skey->$user->{"null"};
743  //has to be converted to NULL in JS Later
744  $scope = 0;
745  }
746 
747  //insert into database
748  $objective_id = $skey;
749  $toset = $o_value;
750  $dbuser = $ilUser->getId();
751 
752 
753  if($key == "status")
754  {
755  //special handling for status
756  $completed = $g_data->$key->$skey->$user->{completed};
757  $measure = $g_data->$key->$skey->$user->{measure};
758  $satisfied = $g_data->$key->$skey->$user->{satisfied};
759  $obj = '-course_overall_status-';
760  $pkg_id = $this->packageId;
761 
762  $res = $ilDB->queryF('
763  SELECT user_id FROM cmi_gobjective
764  WHERE objective_id =%s
765  AND user_id = %s
766  AND scope_id = %s',
767  array('text', 'integer', 'integer'),
768  array($obj, $dbuser, $pkg_id)
769  );
770  $ilLog->write("SCORM2004 Count is: ".$ilDB->numRows($res));
771  if(!$ilDB->numRows($res))
772  {
773  $ilDB->manipulateF('
774  INSERT INTO cmi_gobjective
775  (user_id, status, scope_id, measure, satisfied, objective_id)
776  VALUES (%s, %s, %s, %s, %s, %s)',
777  array('integer', 'text', 'integer', 'text', 'text', 'text'),
778  array($dbuser, $completed, $pkg_id, $measure, $satisfied, $obj)
779  );
780  $ilLog->write("SCORM2004 cmi_gobjective Insert status=".$completed." scope_id=".$pkg_id." measure=".$measure." satisfied=".$satisfied." objective_id=".$obj);
781  }
782  else
783  {
784  $ilDB->manipulateF('
785  UPDATE cmi_gobjective
786  SET status = %s,
787  measure = %s,
788  satisfied = %s
789  WHERE objective_id = %s
790  AND user_id = %s
791  AND scope_id = %s',
792  array('text', 'text', 'text', 'text', 'integer', 'integer'),
793  array($completed, $measure, $satisfied, $obj, $dbuser, $pkg_id)
794  );
795  $ilLog->write("SCORM2004 cmi_gobjective Update status=".$completed." scope_id=".$pkg_id." measure=".$measure." satisfied=".$satisfied." objective_id=".$obj);
796  }
797  } else //add it to the rows_to_insert
798  {
799  //create the row if this is the first time it has been found
800  if($rows_to_insert[$objective_id] == NULL)
801  {
802  $rows_to_insert[$objective_id] = Array();
803  }
804  $rows_to_insert[$objective_id][$key] = $toset;
805  }
806 
807  }
808  }
809 
810  //Get the scope for all the global objectives!!!
811  $res = $ilDB->queryF("SELECT global_to_system
812  FROM cp_package
813  WHERE obj_id = %s",
814  array('text'),
815  array($this->packageId)
816  );
817 
818  $scope_id = ($ilDB->fetchObject($res)->global_to_system) ? 0 : $this->packageId;
819 
820  //build up the set to look for in the query
821  $existing_key_template = "";
822  foreach(array_keys($rows_to_insert) as $obj_id)
823  {
824  $existing_key_template .= "'{$obj_id}',";
825  }
826  //remove trailing ','
827  $existing_key_template = substr($existing_key_template, 0, strlen($existing_key_template) - 1);
828  $existing_keys = Array();
829 
830  if($existing_key_template != "")
831  {
832  //Get the ones that need to be updated in a single query
833  $res = $ilDB->queryF("SELECT objective_id
834  FROM cmi_gobjective
835  WHERE user_id = %s
836  AND scope_id = %s
837  AND objective_id IN ($existing_key_template)",
838  array('integer', 'integer'),
839  array($this->userId, $scope_id)
840  );
841 
842  while($row = $ilDB->fetchAssoc($res))
843  {
844  $existing_keys[] = $row['objective_id'];
845  }
846  }
847 
848  foreach($rows_to_insert as $obj_id => $vals)
849  {
850  if(in_array($obj_id, $existing_keys))
851  {
852  $ilDB->manipulateF("UPDATE cmi_gobjective
853  SET satisfied=%s,
854  measure=%s,
855  score_raw=%s,
856  score_min=%s,
857  score_max=%s,
858  completion_status=%s,
859  progress_measure=%s
860  WHERE objective_id = %s
861  AND user_id = %s
862  AND scope_id = %s",
863 
864  array('text','text', 'text', 'text', 'text', 'text',
865  'text', 'text', 'integer', 'integer'),
866 
867  array($vals['satisfied'], $vals["measure"], $vals["score_raw"],
868  $vals["score_min"], $vals["score_max"],
869  $vals["completion_status"], $vals["progress_measure"],
870  $obj_id, $this->userId, $scope_id)
871  );
872  } else
873  {
874  $ilDB->manipulateF("INSERT INTO cmi_gobjective
875  (user_id, satisfied, measure, scope_id, status, objective_id,
876  score_raw, score_min, score_max, progress_measure, completion_status)
877  VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
878 
879 
880  array('integer', 'text', 'text', 'integer', 'text', 'text',
881  'text', 'text', 'text', 'text', 'text'),
882 
883  array($this->userId, $vals['satisfied'], $vals['measure'],
884  $scope_id, NULL, $obj_id, $vals['score_raw'],
885  $vals['score_min'], $vals['score_max'],
886  $vals['progress_measure'], $vals['completion_status'])
887  );
888  }
889  }
890 
891  // update learning progress here not necessary because integrated in setCMIdata
892  // check _updateStatus for cmi_gobjective
893 // include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
894 // ilLPStatusWrapper::_updateStatus($package, $user);
895 
896  return true;
897  }
898 
899 
900  //Read the shared datascores for a given SCO
901  public function readSharedData($sco_node_id)
902  {
903 
904  global $ilDB, $ilUser;
905  $dataStores = array( "data" => array(),
906  "permissions" => array());
907  $readPermissions = array();
908 
909  $query = 'SELECT target_id, read_shared_data, write_shared_data '
910  . 'FROM cp_datamap '
911  . 'WHERE slm_id = %s '
912  . 'AND sco_node_id = %s '
913  . 'GROUP BY target_id, read_shared_data, write_shared_data';
914 
915 
916  $res = $ilDB->queryF(
917  $query,
918  array('integer', 'integer'),
919  array($this->packageId, $sco_node_id)
920  );
921 
922  //Pass 1: Get all the shared data target_ids
923  // for this content package
924  while($row = $ilDB->fetchAssoc($res))
925  {
926  $storeVal = ($row['read_shared_data'] == 0 && $row['write_shared_data'] == 1 )
927  ? 'notWritten'
928  : null;
929 
930  $dataStores["data"][$row['target_id']] = array( "store" => $storeVal,
931  "readSharedData" => $row['read_shared_data'],
932  "writeSharedData" => $row['write_shared_data']);
933  $dataStores["readPermissions"][$row['target_id']] = $row['read_shared_data'];
934  }
935 
936  if(count($dataStores) < 1)
937  {
938  //If there are no datastores, then return nothing
939  echo "";
940  exit();
941  }
942  else if ($dataStores["readPermissions"] != null && array_sum($dataStores["readPermissions"]) != 0)
943  {
944 
945  //If there exists at least one readSharedData permission, then
946  //fill in the existing values (if any) already in the store.
947 
948  //Create the params to add to the Pass 2 query (get existing values)
949  $params = array("types" => array("integer", "integer"),
950  "values" => array($this->userId, $this->packageId));
951 
952  $paramTemplate = '';
953 
954  //See if readSharedData is set for each datamap.
955  //If set to true, then add it to the search query
956  foreach($dataStores["data"] as $key => $val)
957  {
958  if($dataStores["readPermissions"][$key] == 1
959  && $dataStores["data"][$key]["store"] != 'notWritten')
960  {
961  $params["types"][] = "text";
962  $params["values"][] = $key;
963  $paramTemplate .= '%s, ';
964  }
965  }
966 
967  //Get rid of the trailing ', '
968  $paramTemplate = substr($paramTemplate, 0, strlen($paramTemplate) - 2);
969 
970  //Pass 2: Query for values previously saved by the user
971  $query = 'SELECT target_id, store '
972  . 'FROM adl_shared_data '
973  . 'WHERE user_id = %s '
974  . 'AND slm_id = %s '
975  . 'AND target_id IN (' . $paramTemplate . ')';
976 
977 
978  $res = $ilDB->queryF(
979  $query,
980  $params["types"],
981  $params["values"]
982  );
983 
984  while($row = $ilDB->fetchAssoc($res))
985  {
986  $dataStores["data"][$row['target_id']]["store"] = $row['store'];
987  }
988  }
989 
990  header('Content-Type: text/javascript; charset=UTF-8');
991 
992  echo json_encode($dataStores["data"]);
993  }
994 
995  public function writeSharedData($sco_node_id)
996  {
997  global $ilDB, $ilUser;
998  $g_data = json_decode(file_get_contents('php://input'));
999 
1000  //Step 1: Get the writeable stores for this SCO that already have values
1001  $query = 'SELECT dm.target_id, sd.store '
1002  . 'FROM cp_datamap dm '
1003  . 'LEFT JOIN adl_shared_data sd '
1004  . 'ON(dm.slm_id = sd.slm_id AND dm.target_id = sd.target_id) '
1005  . 'WHERE sco_node_id = %s '
1006  . 'AND dm.slm_id = %s '
1007  . 'AND write_shared_data = 1 '
1008  . 'AND user_id = %s';
1009 
1010  $res = $ilDB->QueryF(
1011  $query,
1012  array('integer', 'integer', 'integer'),
1013  array($sco_node_id, $this->packageId, $this->userId)
1014  );
1015 
1016  $dataStores = array();
1017  $originalVals = array();
1018  while($row = $ilDB->fetchAssoc($res))
1019  {
1020  $id = $row['target_id'];
1021  $dataStores[$id] = $g_data->{$id};
1022  $originalVals[$id] = $row['store'];
1023  }
1024 
1025 
1026  //Step 2: Add the writeable stores
1027  foreach($g_data as $key => $obj)
1028  {
1029  //If it's already created in adl_shared_data, we
1030  //need to update it.
1031  if(array_key_exists($key, $dataStores) )
1032  {
1033  if($obj == 'notWritten') continue;
1034 
1035  $query = 'UPDATE adl_shared_data '
1036  . 'SET store = %s '
1037  . 'WHERE user_id = %s '
1038  . 'AND target_id = %s '
1039  . 'AND slm_id = %s ';
1040 
1041  $ilDB->manipulateF(
1042  $query,
1043  array('text', 'integer', 'text', 'integer'),
1044  array($dataStores[$key], $this->userId, $key, $this->packageId)
1045  );
1046  } else
1047  {
1048  //Check for writability
1049  $res = $ilDB->queryF(
1050  'SELECT write_shared_data '
1051  . 'FROM cp_datamap '
1052  . 'WHERE target_id = %s '
1053  . 'AND slm_id = %s '
1054  . 'AND sco_node_id = %s',
1055  array('text', 'integer', 'integer'),
1056  array($key, $this->packageId, $sco_node_id));
1057 
1058  $row = $ilDB->fetchAssoc($res);
1059  if($row["write_shared_data"] != 1)
1060  {
1061  continue;
1062  }
1063 
1064  //If it's writeable, then add the new value into the database
1065  $res = $ilDB->manipulateF(
1066  'INSERT INTO adl_shared_data VALUES (%s, %s, %s, %s)',
1067  array('integer', 'integer', 'text', 'text'),
1068  array($this->packageId, $this->userId, $key, $obj));
1069  }
1070  }
1071  echo "1";
1072  exit();
1073 
1074  }
1075 
1076  public function specialPage() {
1077 
1078  global $lng;
1079 
1080  $specialpages = array (
1081  "_COURSECOMPLETE_" => "seq_coursecomplete",
1082  "_ENDSESSION_" => "seq_endsession",
1083  "_SEQBLOCKED_" => "seq_blocked",
1084  "_NOTHING_" => "seq_nothing",
1085  "_ERROR_" => "seq_error",
1086  "_DEADLOCK_" => "seq_deadlock",
1087  "_INVALIDNAVREQ_" => "seq_invalidnavreq",
1088  "_SEQABANDON_" => "seq_abandon",
1089  "_SEQABANDONALL_" => "seq_abandonall",
1090  "_TOC_" => "seq_toc"
1091  );
1092 
1093  $this->tpl = new ilTemplate("tpl.scorm2004.specialpages.html", false, false, "Modules/Scorm2004");
1094  $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
1095  $this->tpl->setVariable('TXT_SPECIALPAGE',$lng->txt($specialpages[$this->page]));
1096  if ($this->page!="_TOC_" && $this->page!="_SEQABANDON_" && $this->page!="_SEQABANDONALL_" ) {
1097  $this->tpl->setVariable('CLOSE_WINDOW',$lng->txt('seq_close'));
1098  } else {
1099  $this->tpl->setVariable('CLOSE_WINDOW',"");
1100  }
1101  $this->tpl->show("DEFAULT", false);
1102  }
1103 
1104 
1105  public function fetchCMIData()
1106  {
1107  $data = $this->getCMIData($this->userId, $this->packageId);
1108  if ($this->jsMode)
1109  {
1110  header('Content-Type: text/javascript; charset=UTF-8');
1111  print(json_encode($data));
1112  }
1113  else
1114  {
1115  header('Content-Type: text/plain; charset=UTF-8');
1116  print(var_export($data, true));
1117  }
1118  }
1119 
1120  public function persistCMIData($data = null)
1121  {
1122  global $ilLog;
1123 
1124  if ($this->slm->getDefaultLessonMode() == "browse") {return;}
1125 
1126  $data = json_decode(is_string($data) ? $data : file_get_contents('php://input'));
1127  $ilLog->write("SCORM2004 Got data:". file_get_contents('php://input'));
1128 
1129  $return = $this->setCMIData($this->userId, $this->packageId, $data, $this->ref_id);
1130 
1131  $ilLog->write("SCORM2004 return of persistCMIData: ".json_encode($return));
1132 
1133  if ($this->jsMode)
1134  {
1135  header('Content-Type: text/javascript; charset=UTF-8');
1136  print(json_encode($return));
1137  }
1138  else
1139  {
1140  header('Content-Type: text/html; charset=UTF-8');
1141  print(var_export($return, true));
1142  }
1143  }
1144 
1149  private function normalizeFields($table, &$node)
1150  {
1151  return;
1152  foreach (self::$schema[$table] as $k => $v)
1153  {
1154  $value = $node->$k;
1155  if (isset($value) && is_string($v) && !preg_match($v, $value))
1156  {
1157  unset($node->$k);
1158  }
1159  }
1160  }
1161 
1162  private function getCMIData($userId, $packageId)
1163  {
1164  global $ilDB;
1165 
1166  $i_check=0;
1167  $result = array(
1168  'schema' => array(),
1169  'data' => array()
1170  );
1171 
1172  foreach(self::$schema as $k => &$v)
1173  {
1174  $result['schema'][$k] = array_keys($v);
1175  $q = '';
1176  switch ($k)
1177  {
1178  case "node":
1179  $q = 'SELECT cmi_node.*
1180  FROM cmi_node
1181  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
1182  WHERE cmi_node.user_id = %s
1183  AND cp_node.slm_id = %s';
1184 
1185  break;
1186 
1187  case "comment":
1188  if ($i_check>7) {
1189  $i_check-=8;
1190  if ($this->slm->getComments()) $q = 'SELECT
1191  cmi_comment.cmi_comment_id,
1192  cmi_comment.cmi_node_id,
1193  cmi_comment.c_comment,
1194  cmi_comment.c_timestamp,
1195  cmi_comment.location,
1196  cmi_comment.sourceislms
1197  FROM cmi_comment
1198  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_comment.cmi_node_id
1199  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1200  WHERE cmi_node.user_id = %s
1201  AND cp_node.slm_id = %s
1202  ORDER BY cmi_comment.cmi_comment_id';
1203  }
1204 
1205  break;
1206 
1207  case "correct_response":
1208  if ($i_check>3) {
1209  $i_check-=4;
1210  if ($this->slm->getInteractions()) $q = 'SELECT cmi_correct_response.*
1211  FROM cmi_correct_response
1212  INNER JOIN cmi_interaction
1213  ON cmi_interaction.cmi_interaction_id = cmi_correct_response.cmi_interaction_id
1214  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
1215  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1216  WHERE cmi_node.user_id = %s
1217  AND cp_node.slm_id = %s
1218  ORDER BY cmi_correct_response.cmi_correct_resp_id';
1219  }
1220  break;
1221 
1222  case "interaction":
1223  if ($i_check>1) {
1224  $i_check-=2;
1225  if ($this->slm->getInteractions()) $q = 'SELECT
1226  cmi_interaction.cmi_interaction_id,
1227  cmi_interaction.cmi_node_id,
1228  cmi_interaction.description,
1229  cmi_interaction.id,
1230  cmi_interaction.latency,
1231  cmi_interaction.learner_response,
1232  cmi_interaction.result,
1233  cmi_interaction.c_timestamp,
1234  cmi_interaction.c_type,
1235  cmi_interaction.weighting
1236  FROM cmi_interaction
1237  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
1238  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1239  WHERE cmi_node.user_id = %s
1240  AND cp_node.slm_id = %s
1241  ORDER BY cmi_interaction.cmi_interaction_id';
1242  }
1243  break;
1244 
1245  case "objective":
1246  if ($i_check>0) {
1247  if ($this->slm->getObjectives()) $q = 'SELECT
1248  cmi_objective.cmi_interaction_id,
1249  cmi_objective.cmi_node_id,
1250  cmi_objective.cmi_objective_id,
1251  cmi_objective.completion_status,
1252  cmi_objective.description,
1253  cmi_objective.id,
1254  cmi_objective.c_max,
1255  cmi_objective.c_min,
1256  cmi_objective.c_raw,
1257  cmi_objective.scaled,
1258  cmi_objective.progress_measure,
1259  cmi_objective.success_status,
1260  cmi_objective.scope
1261  FROM cmi_objective
1262  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_objective.cmi_node_id
1263  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
1264  WHERE cmi_node.user_id = %s
1265  AND cp_node.slm_id = %s
1266  ORDER BY cmi_objective.cmi_objective_id';
1267  }
1268  break;
1269 
1270  case "package":
1271  $q = 'SELECT usr_data.usr_id user_id,
1272  CONCAT(CONCAT(COALESCE(usr_data.firstname, \'\'), \' \'), COALESCE(usr_data.lastname, \'\')) learner_name,
1273  sahs_lm.id slm_id , sahs_lm.default_lesson_mode "mode", sahs_lm.credit
1274  FROM usr_data, cp_package
1275  INNER JOIN sahs_lm ON cp_package.obj_id = sahs_lm.id
1276  WHERE usr_data.usr_id = %s
1277  AND sahs_lm.id = %s';
1278 
1279  break;
1280 
1281  }
1282 
1283  $result['data'][$k] = array();
1284  if ($q != '') {
1285  $types = array('integer', 'integer');
1286  $values = array($userId, $packageId);
1287  $res = $ilDB->queryF($q, $types, $values);
1288 
1289  while($row = $ilDB->fetchAssoc($res))
1290  {
1291  $tmp_result = array();
1292  foreach($row as $key => $value)
1293  {
1294  if ($k == "comment" && $key == "c_timestamp" && strpos($value,' ')==10) $value = str_replace(' ','T',$value);
1295  $tmp_result[] = $value;
1296  if($k=="node" && $key=="additional_tables" && $i_check<$value){
1297  $i_check=$value;
1298 // $GLOBALS['ilLog']->write($i_check);
1299  }
1300  }
1301  $result['data'][$k][] = $tmp_result;
1302  }
1303  }
1304  }
1305  return $result;
1306  }
1307 
1308  private function setCMIData($userId, $packageId, $data)
1309  {
1310  global $ilDB, $ilLog;
1311 
1312  $result = array();
1313 
1314  if (!$data) return;
1315 
1316  $i_check=$data->i_check;
1317  $i_set=$data->i_set;
1318  $b_node_update=false;
1319  $cmi_node_id=null;
1320  $a_map_cmi_interaction_id=array();
1321 
1322  $tables = array('node', 'comment', 'interaction', 'objective', 'correct_response');
1323 
1324  foreach($tables as $table)
1325  {
1326  if (!is_array($data->$table)) continue;
1327 
1328  $ilLog->write("SCORM: setCMIData, table -".$table."-");
1329 
1330  // now iterate through data rows from input
1331  foreach($data->$table as &$row)
1332  {
1333  $ilLog->write("Checking table: ".$table);
1334 
1335 
1336 
1337 
1338 
1339  switch($table)
1340  {
1341  case 'node': //is always first and has only 1 row
1342 
1343  $res = $ilDB->queryF(
1344  'SELECT cmi_node_id FROM cmi_node WHERE cp_node_id = %s and user_id = %s',
1345  array('integer','integer'),
1346  array($row[19],$userId)
1347  );
1348  $rowtmp=$ilDB->fetchAssoc($res);
1349  $cmi_node_id=$rowtmp['cmi_node_id'];
1350  if ($cmi_node_id!=null) $b_node_update=true;
1351  else {
1352  $cmi_node_id = $ilDB->nextId('cmi_node');
1353  $b_node_update=false;
1354  }
1355  $ilLog->write("setCMIdata with cmi_node_id = ".$cmi_node_id);
1356  $a_data=array(
1357  'accesscount' => array('integer', $row[0]),
1358  'accessduration' => array('text', $row[1]),
1359  'accessed' => array('text', $row[2]),
1360  'activityabsduration' => array('text', $row[3]),
1361  'activityattemptcount' => array('integer', $row[4]),
1362  'activityexpduration' => array('text', $row[5]),
1363  'activityprogstatus' => array('integer', $row[6]),
1364  'attemptabsduration' => array('text', $row[7]),
1365  'attemptcomplamount' => array('float', $row[8]),
1366  'attemptcomplstatus' => array('integer', $row[9]),
1367  'attemptexpduration' => array('text', $row[10]),
1368  'attemptprogstatus' => array('integer', $row[11]),
1369  'audio_captioning' => array('integer', $row[12]),
1370  'audio_level' => array('float', $row[13]),
1371  'availablechildren' => array('text', $row[14]),
1372  'cmi_node_id' => array('integer', $cmi_node_id),
1373  'completion' => array('float', $row[16]),
1374  'completion_status' => array('text', $row[17]),
1375  'completion_threshold' => array('text', $row[18]),
1376  'cp_node_id' => array('integer', $row[19]),
1377  'created' => array('text', $row[20]),
1378  'credit' => array('text', $row[21]),
1379  'delivery_speed' => array('float', $row[22]),
1380  'c_entry' => array('text', $row[23]),
1381  'c_exit' => array('text', $row[24]),
1382  'c_language' => array('text', $row[25]),
1383  'launch_data' => array('clob', $row[26]),
1384  'learner_name' => array('text', $row[27]),
1385  'location' => array('text', $row[28]),
1386  'c_max' => array('float', $row[29]),
1387  'c_min' => array('float', $row[30]),
1388  'c_mode' => array('text', $row[31]),
1389  'modified' => array('text', $row[32]),
1390  'progress_measure' => array('float', $row[33]),
1391  'c_raw' => array('float', $row[34]),
1392  'scaled' => array('float', $row[35]),
1393  'scaled_passing_score' => array('float', $row[36]),
1394  'session_time' => array('text', $row[37]),
1395  'success_status' => array('text', $row[38]),
1396  'suspend_data' => array('clob', $row[39]),
1397  'total_time' => array('text', $row[40]),
1398  'user_id' => array('integer', $userId),
1399  'c_timestamp' => array('timestamp', date('Y-m-d H:i:s')),
1400  'additional_tables' => array('integer', $i_check)
1401  );
1402 
1403  if($b_node_update==false) {
1404  $ilLog->write("Want to insert row: ".count($row) );
1405  $ilDB->insert('cmi_node', $a_data);
1406  } else {
1407  $ilDB->update('cmi_node', $a_data, array('cmi_node_id' => array('integer', $cmi_node_id)));
1408  $ilLog->write("updated");
1409  }
1410 
1411  if($b_node_update==true) {
1412  //remove
1413  if ($i_set>7) {
1414  $i_set-=8;
1415  if ($this->slm->getComments()) {
1416  $q = 'DELETE FROM cmi_comment WHERE cmi_node_id = %s';
1417  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
1418  }
1419  }
1420  if ($i_set>3) {
1421  $i_set-=4;
1422  if ($this->slm->getInteractions()) {
1423  $q = 'DELETE FROM cmi_correct_response
1424  WHERE cmi_interaction_id IN (
1425  SELECT cmi_interaction.cmi_interaction_id FROM cmi_interaction WHERE cmi_interaction.cmi_node_id = %s)';
1426  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
1427  }
1428  }
1429  if ($i_set>1) {
1430  $i_set-=2;
1431  if ($this->slm->getInteractions()) {
1432  $q = 'DELETE FROM cmi_interaction WHERE cmi_node_id = %s';
1433  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
1434  }
1435  }
1436  if ($i_set>0) {
1437  $i_set=0;
1438  if ($this->slm->getObjectives()) {
1439  $q = 'DELETE FROM cmi_objective WHERE cmi_node_id = %s';
1440  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
1441  }
1442  }
1443  //end remove
1444  }
1445  //to send to client
1446  $result[(string)$row[19]] = $cmi_node_id;
1447  break;
1448 
1449  case 'comment':
1450  $row[0] = $ilDB->nextId('cmi_comment');
1451 
1452  $ilDB->insert('cmi_comment', array(
1453  'cmi_comment_id' => array('integer', $row[0]),
1454  'cmi_node_id' => array('integer', $cmi_node_id),
1455  'c_comment' => array('clob', $row[2]),
1456  'c_timestamp' => array('text', $row[3]),
1457  'location' => array('text', $row[4]),
1458  'sourceislms' => array('integer', $row[5])
1459  ));
1460  break;
1461 
1462  case 'interaction':
1463  $cmi_interaction_id = $ilDB->nextId('cmi_interaction');
1464  $a_map_cmi_interaction_id[]=array($row[0],$cmi_interaction_id);
1465  $ilDB->insert('cmi_interaction', array(
1466  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
1467  'cmi_node_id' => array('integer', $cmi_node_id),
1468  'description' => array('clob', $row[2]),
1469  'id' => array('text', $row[3]),
1470  'latency' => array('text', $row[4]),
1471  'learner_response' => array('clob', $row[5]),
1472  'result' => array('text', $row[6]),
1473  'c_timestamp' => array('text', $row[7]),
1474  'c_type' => array('text', $row[8]),
1475  'weighting' => array('float', $row[9])
1476  ));
1477  break;
1478 
1479  case 'objective':
1480  $row[2] = $ilDB->nextId('cmi_objective');
1481  $cmi_interaction_id = null;
1482  if ($row[0] != null) {
1483  for($i=0;$i<count($a_map_cmi_interaction_id);$i++)
1484  if ($row[0] == $a_map_cmi_interaction_id[$i][0]) $cmi_interaction_id=$a_map_cmi_interaction_id[$i][1];
1485  }
1486  $ilDB->insert('cmi_objective', array(
1487  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
1488  'cmi_node_id' => array('integer', $cmi_node_id),
1489  'cmi_objective_id' => array('integer', $row[2]),
1490  'completion_status' => array('text', $row[3]),
1491  'description' => array('clob', $row[4]),
1492  'id' => array('text', $row[5]),
1493  'c_max' => array('float', $row[6]),
1494  'c_min' => array('float', $row[7]),
1495  'c_raw' => array('float', $row[8]),
1496  'scaled' => array('float', $row[9]),
1497  'progress_measure' => array('float', $row[10]),
1498  'success_status' => array('text', $row[11]),
1499  'scope' => array('text', $row[12])
1500  ));
1501  break;
1502 
1503  case 'correct_response':
1504  $cmi_interaction_id = null;
1505  if ($row[1] !== null) {
1506  for($i=0;$i<count($a_map_cmi_interaction_id);$i++)
1507  if ($row[1] == $a_map_cmi_interaction_id[$i][0]) $cmi_interaction_id=$a_map_cmi_interaction_id[$i][1];
1508  $row[0] = $ilDB->nextId('cmi_correct_response');
1509  $ilDB->insert('cmi_correct_response', array(
1510  'cmi_correct_resp_id' => array('integer', $row[0]),
1511  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
1512  'pattern' => array('text', $row[2])
1513  ));
1514  }
1515  break;
1516  }
1517  }
1518  }
1519 
1520 
1521  $changed_seq_utilities=$data->changed_seq_utilities;
1522  $ilLog->write("SCORM2004 adl_seq_utilities changed: ".$changed_seq_utilities);
1523  if ($changed_seq_utilities == 1) {
1524  $this->writeGObjective($data->adl_seq_utilities);
1525  }
1526 
1527 
1528 
1529  //ATTENTION not at commit - do at unload!
1530  // sync access number and time in read event table
1531  //include_once("./Modules/Scorm2004/classes/class.ilSCORM2004Tracking.php");
1532  //ilSCORM2004Tracking::_syncReadEvent($packageId, $userId, "sahs", $a_ref_id);
1533 
1534  // update learning progress status
1535  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
1536  ilLPStatusWrapper::_updateStatus($packageId, $userId);
1537 // include_once './Modules/Scorm2004/classes/class.ilSCORM2004Tracking.php';
1538 // $new_global_status = ilSCORM2004Tracking::updateGlobalStatus($userId, $packageId,$completed, $satisfied, $measure);
1539 // $ilLog->write("new_global_status=".$new_global_status);
1540 // $saved_global_status=$data->saved_global_status;
1541 // $ilLog->write("saved_global_status=".$saved_global_status);
1542 // $result["new_global_status"]=$new_global_status;
1543 
1544 // here put code for soap to MaxCMS e.g. when if($saved_global_status != $new_global_status)
1545 
1546  $result["new_global_status"]="";
1547  return $result;
1548  }
1549 
1550  function quoteJSONArray($a_array)
1551  {
1552  global $ilDB;
1553 
1554  if(!is_array($a_array) or !count($a_array))
1555  {
1556  return array("''");
1557  }
1558 
1559  foreach($a_array as $k => $item)
1560  {
1561  if ($item != null) {
1562  $a_array[$k] = $ilDB->quote($item);
1563  } else {
1564  $a_array[$k] = "NULL";
1565  }
1566  }
1567 
1568  return $a_array;
1569  }
1570 
1578  public function getMimetype($filename)
1579  {
1580  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1582  }
1583 
1589  public function getCookie()
1590  {
1591  return unserialize(base64_decode($_COOKIE[IL_OP_COOKIE_NAME]));
1592  }
1593 
1594  public function setCookie($cook)
1595  {
1596  setCookie(IL_OP_COOKIE_NAME, base64_encode(serialize($cook)));
1597  }
1598 
1605  public function readFile($path)
1606  {
1607  if (headers_sent())
1608  {
1609  die('Error: Cookie could not be established');
1610  }
1611 
1612  $SAHS_LM_POSITION = 1; // index position of sahs_lm id in splitted path_info
1613 
1614  $comp = explode('/', (string) $path);
1615  $sahs = $comp[$SAHS_LM_POSITION];
1616  $cook = $this->getCookie();
1617  $perm = $cook[$sahs];
1618 
1619  if (!$perm)
1620  {
1621  // check login an package access
1622  // TODO add rbac check function here
1623  $perm = 1;
1624  if (!$perm)
1625  {
1626  header('HTTP/1.0 401 Unauthorized');
1627  die('/* Unauthorized */');
1628  }
1629  // write cookie
1630  $cook[$sahs] = $perm;
1631  $this->setCookie($cook);
1632  }
1633 
1634  $path = '.' . $path;
1635  if (!is_file($path))
1636  {
1637  header('HTTP/1.0 404 Not Found');
1638  die('/* Not Found ' . $path . '*/');
1639  }
1640 
1641  // send mimetype to client
1642  header('Content-Type: ' . $this->getMimetype($path));
1643 
1644  // let page be cached in browser for session duration
1645  header('Expires: ' . gmdate('D, d M Y H:i:s', time() + session_cache_expire()*60) . ' GMT');
1646  header('Cache-Control: private');
1647 
1648  // now show it to the user and be fine
1649  readfile($path);
1650  die();
1651  }
1652 
1656  function get_max_attempts()
1657  {
1658  global $ilDB;
1659 
1660  $res = $ilDB->queryF(
1661  'SELECT max_attempt FROM sahs_lm WHERE id = %s',
1662  array('integer'),
1663  array($this->packageId)
1664  );
1665  $row = $ilDB->fetchAssoc($res);
1666 
1667  return $row['max_attempt'];
1668  }
1669 
1670  function get_Module_Version()
1671  {
1672  global $ilDB;
1673 
1674  $res = $ilDB->queryF(
1675  'SELECT module_version FROM sahs_lm WHERE id = %s',
1676  array('integer'),
1677  array($this->packageId));
1678  $row = $ilDB->fetchAssoc($res);
1679 
1680  return $row['module_version'];
1681  }
1682 
1686  function get_actual_attempts()
1687  {
1688  global $ilDB, $ilUser;
1689 
1690  $res = $ilDB->queryF('
1691  SELECT rvalue FROM cmi_custom
1692  WHERE user_id = %s AND sco_id = %s
1693  AND lvalue = %s AND obj_id = %s',
1694  array('integer', 'integer', 'text', 'integer'),
1695  array($this->userId, 0, 'package_attempts', $this->packageId)
1696  );
1697  $row = $ilDB->fetchAssoc($res);
1698 
1699  $row['rvalue'] = str_replace("\r\n", "\n", $row['rvalue']);
1700  if($row['rvalue'] == null)
1701  {
1702  $row['rvalue'] = 0;
1703  }
1704  return $row['rvalue'];
1705  }
1706 
1710  function increase_attempt()
1711  {
1712  global $ilDB, $ilUser;
1713 
1714  //get existing account - sco id is always 0
1715  $res = $ilDB->queryF('
1716  SELECT rvalue FROM cmi_custom
1717  WHERE user_id = %s
1718  AND sco_id = %s
1719  AND lvalue = %s
1720  AND obj_id = %s',
1721  array('integer', 'integer','text', 'integer'),
1722  array($this->userId, 0, 'package_attempts', $this->packageId)
1723  );
1724  $row = $ilDB->fetchAssoc($res);
1725 
1726  $tmp_row = $row;
1727 
1728  $row['rvalue'] = str_replace("\r\n", "\n", $row['rvalue']);
1729  if($row['rvalue'] == null)
1730  {
1731  $row['rvalue'] = 0;
1732  }
1733  $new_rec = $row['rvalue'] + 1;
1734 
1735  //increase attempt by 1
1736  if(!is_array($tmp_row) || !count($tmp_row))
1737  {
1738  $ilDB->manipulateF('
1739  INSERT INTO cmi_custom (rvalue, user_id, sco_id, obj_id, lvalue, c_timestamp)
1740  VALUES(%s, %s, %s, %s, %s, %s)',
1741  array('text', 'integer', 'integer', 'integer', 'text', 'timestamp'),
1742  array($new_rec, $this->userId, 0, $this->packageId, 'package_attempts', date('Y-m-d H:i:s'))
1743  );
1744  }
1745  else
1746  {
1747  $ilDB->manipulateF('
1748  UPDATE cmi_custom
1749  SET rvalue = %s,
1750  c_timestamp = %s
1751  WHERE user_id = %s
1752  AND sco_id = %s
1753  AND obj_id = %s
1754  AND lvalue = %s',
1755  array('text', 'timestamp', 'integer', 'integer', 'integer','text'),
1756  array($new_rec, date('Y-m-d H:i:s'), $this->userId, 0, $this->packageId, 'package_attempts')
1757  );
1758  }
1759 
1760 
1761  }
1762 
1763  function resetSharedData()
1764  {
1765  global $ilDB;
1766  //Reset the shared data stores if sharedDataGlobalToSystem is false
1767  $res = $ilDB->queryF('
1768  SELECT shared_data_global_to_system
1769  FROM cp_package
1770  WHERE obj_id = %s',
1771  array('integer'),
1772  array($this->packageId)
1773  );
1774 
1775  $shared_global_to_sys = $ilDB->fetchObject($res)->shared_data_global_to_system;
1776 
1777  $res = $ilDB->queryF('
1778  SELECT data
1779  FROM cp_suspend
1780  WHERE obj_id = %s
1781  AND user_id = %s',
1782  array('integer', 'integer'),
1783  array($this->packageId, $this->userId)
1784  );
1785 
1786  $suspended = false;
1787 
1788  $dat = $ilDB->fetchObject($res)->data;
1789  if($dat != null && $dat != '' ) $suspended = true;
1790 
1791  if($shared_global_to_sys == 0 && !$suspended)
1792  {
1793  $ilDB->manipulateF('
1794  DELETE FROM adl_shared_data
1795  WHERE slm_id = %s
1796  AND user_id = %s',
1797  array('integer', 'integer'),
1798  array($this->packageId, $this->userId)
1799  );
1800  }
1801  }
1805  function save_module_version()
1806  {
1807  global $ilDB, $ilUser;
1808 
1809  $res = $ilDB->queryF('
1810  SELECT rvalue FROM cmi_custom
1811  WHERE user_id = %s
1812  AND sco_id = %s
1813  AND lvalue = %s
1814  AND obj_id = %s',
1815  array('integer', 'integer', 'text', 'integer'),
1816  array($this->userId, 0, 'module_version', $this->packageId)
1817  );
1818  if(!$ilDB->numRows($res))
1819  {
1820  $ilDB->manipulateF('
1821  INSERT INTO cmi_custom (rvalue, user_id, sco_id, obj_id, lvalue, c_timestamp)
1822  VALUES(%s, %s, %s, %s, %s, %s)',
1823  array('text', 'integer', 'integer', 'integer', 'text', 'timestamp'),
1824  array($this->get_Module_Version(), $this->userId, 0, $this->packageId, 'module_version', date('Y-m-d H:i:s'))
1825  );
1826  }
1827  else
1828  {
1829  //optimize: check first if $this->get_Module_Version() = module_version
1830  $ilDB->manipulateF('
1831  UPDATE cmi_custom
1832  SET rvalue = %s,
1833  c_timestamp = %s
1834  WHERE user_id = %s
1835  AND sco_id = %s
1836  AND obj_id = %s
1837  AND lvalue = %s',
1838  array('text', 'timestamp', 'integer', 'integer', 'integer', 'text'),
1839  array($this->get_Module_Version(), date('Y-m-d H:i:s'), $this->userId, 0, $this->packageId, 'module_version')
1840  );
1841  }
1842  }
1843 
1844  //debug extentions
1845 
1846  private function getNodeData($sco_id,$fh)
1847  {
1848  global $ilDB,$ilLog;
1849 
1850  $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,".
1851  "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";
1852 
1853 
1854  $res = $ilDB->queryF('
1855  SELECT '.$fieldList.'
1856  FROM cmi_node,cp_node,cp_item
1857  WHERE cp_node.slm_id = %s
1858  AND cp_node.cp_node_id = cp_item.cp_node_id
1859  AND cp_item.id = %s
1860  AND cmi_node.cp_node_id = cp_item.cp_node_id
1861  AND cmi_node.user_id = %s',
1862  array('integer','text','integer'),
1863  array($this->packageId, $sco_id, $this->userId)
1864  );
1865  $row = $ilDB->fetchAssoc($res);
1866  $ilLog->write("DEBUG SQL".$row);
1867  return $row;
1868  }
1869 
1870  private function logTmpName()
1871  {
1872  $filename = $this->logDirectory()."/".$this->packageId.".tmp";
1873  if (!file_exists($filename)) {
1874  umask(0000);
1875  $fHandle = fopen($filename, 'a') or die("can't open file");
1876  fwrite($fHandle, $string);
1877  fclose($fHandle);
1878  }
1879  return $filename;
1880  }
1881 
1882  private function summaryFileName()
1883  {
1884  $filename = $this->logDirectory()."/".$this->packageId."_summary_".$this->get_actual_attempts();
1885  $adder = "0";
1886  $suffix = ".csv";
1887  $i = 0;
1888  while (file_exists($filename."_".$adder.$suffix)) {
1889  $i++;
1890  $adder = (string) $i;
1891  }
1892  $retname = $filename."_".$adder.$suffix;
1893 
1894  if (!file_exists($retname)) {
1895  umask(0000);
1896  $fHandle = fopen($retname, 'a') or die("can't open file");
1897  fwrite($fHandle, $string);
1898  fclose($fHandle);
1899  }
1900  return $retname;
1901  }
1902 
1903  private function logFileName()
1904  {
1905  global $lng;
1906  $lng->loadLanguageModule("scormdebug");
1907 
1908  $filename = $this->logDirectory()."/".$this->packageId."_".$this->get_actual_attempts();
1909  $path_csv = $filename.".csv";
1910  $path_txt = $filename.".html";
1911  if (!file_exists($path_csv)) {
1912  umask(0000);
1913  $fHandle = fopen($path_csv, 'a') or die("can't open file");
1914  $string = '"CourseId";"ScoId";"ScoTitle";"Timestamp";"Action";"Key";"Value";"Return Value";"Errorcode";"Timespan";"ErrorDescription"'."\n";
1915  fwrite($fHandle, $string);
1916  fclose($fHandle);
1917  }
1918  if (!file_exists($path_txt)) {
1919  if (file_exists($this->logTmpName())) {
1920  unlink($this->logTmpName());
1921  }
1922  umask(0000);
1923  $fHandle2 = fopen($path_txt, 'a') or die("can't open file");
1924  $logtpl = $this->getLogTemplate();
1925  $logtpl->setCurrentBlock('NewLog');
1926  $logtpl->setVariable("COURSETITLE", $this->slm->getTitle());
1927  $logtpl->setVariable("COURSEID", $this->packageId);
1928  $logtpl->setVariable("TIMESTAMP", date("d.m.Y H:i",time()));
1929  $logtpl->setVariable("SESSION", $this->get_actual_attempts());
1930  $logtpl->setVariable("error0", $lng->txt("error0"));
1931  $logtpl->setVariable("error101", $lng->txt("error101"));
1932  $logtpl->setVariable("error102", $lng->txt("error102"));
1933  $logtpl->setVariable("error103", $lng->txt("error103"));
1934  $logtpl->setVariable("error104", $lng->txt("error104"));
1935  $logtpl->setVariable("error111", $lng->txt("error111"));
1936  $logtpl->setVariable("error112", $lng->txt("error112"));
1937  $logtpl->setVariable("error113", $lng->txt("error113"));
1938  $logtpl->setVariable("error122", $lng->txt("error122"));
1939  $logtpl->setVariable("error123", $lng->txt("error123"));
1940  $logtpl->setVariable("error132", $lng->txt("error132"));
1941  $logtpl->setVariable("error133", $lng->txt("error133"));
1942  $logtpl->setVariable("error142", $lng->txt("error142"));
1943  $logtpl->setVariable("error143", $lng->txt("error143"));
1944  $logtpl->setVariable("error201", $lng->txt("error201"));
1945  $logtpl->setVariable("error301", $lng->txt("error301"));
1946  $logtpl->setVariable("error351", $lng->txt("error351"));
1947  $logtpl->setVariable("error391", $lng->txt("error391"));
1948  $logtpl->setVariable("error401", $lng->txt("error401"));
1949  $logtpl->setVariable("error402", $lng->txt("error402"));
1950  $logtpl->setVariable("error403", $lng->txt("error403"));
1951  $logtpl->setVariable("error404", $lng->txt("error404"));
1952  $logtpl->setVariable("error405", $lng->txt("error405"));
1953  $logtpl->setVariable("error406", $lng->txt("error406"));
1954  $logtpl->setVariable("error407", $lng->txt("error407"));
1955  $logtpl->setVariable("error408", $lng->txt("error408"));
1956  $logtpl->setVariable("SetValue", $lng->txt("SetValue"));
1957  $logtpl->setVariable("GetValue", $lng->txt("GetValue"));
1958  $logtpl->setVariable("Commit", $lng->txt("Commit"));
1959  $logtpl->setVariable("Initialize", $lng->txt("Initialize"));
1960  $logtpl->setVariable("Terminate", $lng->txt("Terminate"));
1961  $logtpl->setVariable("GetErrorString", $lng->txt("GetErrorString"));
1962  $logtpl->setVariable("GetLastError", $lng->txt("GetLastError"));
1963  $logtpl->setVariable("GetDiagnostic", $lng->txt("GetDiagnostic"));
1964  $logtpl->setVariable("cmi._version", $lng->txt("cmi._version"));
1965  $logtpl->setVariable("cmi.comments_from_learner._children", $lng->txt("cmi.comments_from_learner._children"));
1966  $logtpl->setVariable("cmi.comments_from_learner._count", $lng->txt("cmi.comments_from_learner._count"));
1967  $logtpl->setVariable("cmi.comments_from_learner.n.comment", $lng->txt("cmi.comments_from_learner.n.comment"));
1968  $logtpl->setVariable("cmi.comments_from_learner.n.location", $lng->txt("cmi.comments_from_learner.n.location"));
1969  $logtpl->setVariable("cmi.comments_from_learner.n.timestamp", $lng->txt("cmi.comments_from_learner.n.timestamp"));
1970  $logtpl->setVariable("cmi.comments_from_lms._children", $lng->txt("cmi.comments_from_lms._children"));
1971  $logtpl->setVariable("cmi.comments_from_lms._count", $lng->txt("cmi.comments_from_lms._count"));
1972  $logtpl->setVariable("cmi.comments_from_lms.n.comment", $lng->txt("cmi.comments_from_lms.n.comment"));
1973  $logtpl->setVariable("cmi.comments_from_lms.n.location", $lng->txt("cmi.comments_from_lms.n.location"));
1974  $logtpl->setVariable("cmi.comments_from_lms.n.timestamp", $lng->txt("cmi.comments_from_lms.n.timestamp"));
1975  $logtpl->setVariable("cmi.completion_status", $lng->txt("cmi.completion_status"));
1976  $logtpl->setVariable("cmi.completion_threshold", $lng->txt("cmi.completion_threshold"));
1977  $logtpl->setVariable("cmi.credit", $lng->txt("cmi.credit"));
1978  $logtpl->setVariable("cmi.entry", $lng->txt("cmi.entry"));
1979  $logtpl->setVariable("cmi.exit", $lng->txt("cmi.exit"));
1980  $logtpl->setVariable("cmi.interactions._children", $lng->txt("cmi.interactions._children"));
1981  $logtpl->setVariable("cmi.interactions._count", $lng->txt("cmi.interactions._count"));
1982  $logtpl->setVariable("cmi.interactions.n.id", $lng->txt("cmi.interactions.n.id"));
1983  $logtpl->setVariable("cmi.interactions.n.type", $lng->txt("cmi.interactions.n.type"));
1984  $logtpl->setVariable("cmi.interactions.n.objectives._count", $lng->txt("cmi.interactions.n.objectives._count"));
1985  $logtpl->setVariable("cmi.interactions.n.objectives.n.id", $lng->txt("cmi.interactions.n.objectives.n.id"));
1986  $logtpl->setVariable("cmi.interactions.n.timestamp", $lng->txt("cmi.interactions.n.timestamp"));
1987  $logtpl->setVariable("cmi.interactions.n.correct_responses._count", $lng->txt("cmi.interactions.n.correct_responses._count"));
1988  $logtpl->setVariable("cmi.interactions.n.correct_responses.n.pattern", $lng->txt("cmi.interactions.n.correct_responses.n.pattern"));
1989  $logtpl->setVariable("cmi.interactions.n.weighting", $lng->txt("cmi.interactions.n.weighting"));
1990  $logtpl->setVariable("cmi.interactions.n.learner_response", $lng->txt("cmi.interactions.n.learner_response"));
1991  $logtpl->setVariable("cmi.interactions.n.result", $lng->txt("cmi.interactions.n.result"));
1992  $logtpl->setVariable("cmi.interactions.n.latency", $lng->txt("cmi.interactions.n.latency"));
1993  $logtpl->setVariable("cmi.interactions.n.description", $lng->txt("cmi.interactions.n.description"));
1994  $logtpl->setVariable("cmi.launch_data", $lng->txt("cmi.launch_data"));
1995  $logtpl->setVariable("cmi.learner_id", $lng->txt("cmi.learner_id"));
1996  $logtpl->setVariable("cmi.learner_name", $lng->txt("cmi.learner_name"));
1997  $logtpl->setVariable("cmi.learner_preference._children", $lng->txt("cmi.learner_preference._children"));
1998  $logtpl->setVariable("cmi.learner_preference.audio_level", $lng->txt("cmi.learner_preference.audio_level"));
1999  $logtpl->setVariable("cmi.learner_preference.language", $lng->txt("cmi.learner_preference.language"));
2000  $logtpl->setVariable("cmi.learner_preference.delivery_speed", $lng->txt("cmi.learner_preference.delivery_speed"));
2001  $logtpl->setVariable("cmi.learner_preference.audio_captioning", $lng->txt("cmi.learner_preference.audio_captioning"));
2002  $logtpl->setVariable("cmi.location", $lng->txt("cmi.location"));
2003  $logtpl->setVariable("cmi.max_time_allowed", $lng->txt("cmi.max_time_allowed"));
2004  $logtpl->setVariable("cmi.mode", $lng->txt("cmi.mode"));
2005  $logtpl->setVariable("cmi.objectives._children", $lng->txt("cmi.objectives._children"));
2006  $logtpl->setVariable("cmi.objectives._count", $lng->txt("cmi.objectives._count"));
2007  $logtpl->setVariable("cmi.objectives.n.id", $lng->txt("cmi.objectives.n.id"));
2008  $logtpl->setVariable("cmi.objectives.n.score._children", $lng->txt("cmi.objectives.n.score._children"));
2009  $logtpl->setVariable("cmi.objectives.n.score.scaled", $lng->txt("cmi.objectives.n.score.scaled"));
2010  $logtpl->setVariable("cmi.objectives.n.score.raw", $lng->txt("cmi.objectives.n.score.raw"));
2011  $logtpl->setVariable("cmi.objectives.n.score.min", $lng->txt("cmi.objectives.n.score.min"));
2012  $logtpl->setVariable("cmi.objectives.n.score.max", $lng->txt("cmi.objectives.n.score.max"));
2013  $logtpl->setVariable("cmi.objectives.n.success_status", $lng->txt("cmi.objectives.n.success_status"));
2014  $logtpl->setVariable("cmi.objectives.n.completion_status", $lng->txt("cmi.objectives.n.completion_status"));
2015  $logtpl->setVariable("cmi.objectives.n.progress_measure", $lng->txt("cmi.objectives.n.progress_measure"));
2016  $logtpl->setVariable("cmi.objectives.n.description", $lng->txt("cmi.objectives.n.description"));
2017  $logtpl->setVariable("cmi.progress_measure", $lng->txt("cmi.progress_measure"));
2018  $logtpl->setVariable("cmi.scaled_passing_score", $lng->txt("cmi.scaled_passing_score"));
2019  $logtpl->setVariable("cmi.score._children", $lng->txt("cmi.score._children"));
2020  $logtpl->setVariable("cmi.score.scaled", $lng->txt("cmi.score.scaled"));
2021  $logtpl->setVariable("cmi.score.raw", $lng->txt("cmi.score.raw"));
2022  $logtpl->setVariable("cmi.score.min", $lng->txt("cmi.score.min"));
2023  $logtpl->setVariable("cmi.score.max", $lng->txt("cmi.score.max"));
2024  $logtpl->setVariable("cmi.session_time", $lng->txt("cmi.session_time"));
2025  $logtpl->setVariable("cmi.success_status", $lng->txt("cmi.success_status"));
2026  $logtpl->setVariable("cmi.suspend_data", $lng->txt("cmi.suspend_data"));
2027  $logtpl->setVariable("cmi.time_limit_action", $lng->txt("cmi.time_limit_action"));
2028  $logtpl->setVariable("cmi.total_time", $lng->txt("cmi.total_time"));
2029  $logtpl->setVariable("adl.nav.request", $lng->txt("adl.nav.request"));
2030  $logtpl->setVariable("adl.nav.request_valid.continue", $lng->txt("adl.nav.request_valid.continue"));
2031  $logtpl->setVariable("adl.nav.request_valid.previous", $lng->txt("adl.nav.request_valid.previous"));
2032  $logtpl->setVariable("adl.nav.request_valid.choice", $lng->txt("adl.nav.request_valid.choice"));
2033  $logtpl->setVariable("i_green", $lng->txt("i_green"));
2034  $logtpl->setVariable("i_red", $lng->txt("i_red"));
2035  $logtpl->setVariable("i_orange", $lng->txt("i_orange"));
2036  $logtpl->setVariable("i_fuchsia", $lng->txt("i_fuchsia"));
2037  $logtpl->setVariable("i_gray", $lng->txt("i_gray"));
2038  $logtpl->setVariable("error", $lng->txt("error"));
2039  $logtpl->setVariable("strange_error", $lng->txt("strange_error"));
2040  $logtpl->setVariable("strange_API-Call", $lng->txt("strange_API-Call"));
2041  $logtpl->setVariable("unknown", $lng->txt("unknown"));
2042  $logtpl->setVariable("undefined_color", $lng->txt("undefined_color"));
2043  $logtpl->setVariable("description_for", $lng->txt("description_for"));
2044  $logtpl->setVariable("hide", $lng->txt("hide"));
2045  $logtpl->setVariable("all_API-calls_shown", $lng->txt("all_API-calls_shown"));
2046  $logtpl->setVariable("show_only_important_API-calls", $lng->txt("show_only_important_API-calls"));
2047  $logtpl->setVariable("only_important_API-Calls_shown", $lng->txt("only_important_API-Calls_shown"));
2048  $logtpl->setVariable("show_all_API-calls", $lng->txt("show_all_API-calls"));
2049  $logtpl->setVariable("log_for", $lng->txt("log_for"));
2050  $logtpl->setVariable("started", $lng->txt("started"));
2051  $logtpl->setVariable("nr_session", $lng->txt("nr_session"));
2052  $logtpl->setVariable("id_learning_module", $lng->txt("id_learning_module"));
2053  if($this->slm->getCheck_values()==false) $logtpl->setVariable("CHECK_VALUES", $lng->txt("sent_values_not_checked"));
2054  $logtpl->parseCurrentBlock();
2055  fwrite($fHandle2,$logtpl->get());
2056  fclose($fHandle2);
2057  }
2058  return $filename;
2059  }
2060 
2061  function getDataDirectory2()
2062  {
2063  $webdir=str_replace("/ilias.php","",$_SERVER["SCRIPT_NAME"]);
2064  //load ressources always with absolute URL..relative URLS fail on innersco navigation on certain browsers
2065  $lm_dir=$webdir."/".ILIAS_WEB_DIR."/".$this->ilias->client_id ."/lm_data"."/lm_".$this->packageId;
2066  return $lm_dir;
2067  }
2068 
2069  private function logDirectory()
2070  {
2071 // $logDir=ilUtil::getDataDir()."/SCORMlogs"."/lm_".$this->packageId;
2072 // if (!file_exists($logDir)) ilUtil::makeDirParents($logDir);
2073  $logDir=$this->slm->getDataDirectory()."/logs";
2074  if (!file_exists($logDir)) {
2075  ilUtil::makeDir($logDir);
2076  }
2077  return $logDir;
2078  }
2079 
2080  public function openLog(){
2081  $filename = $_GET['logFile'];
2082  //Header
2083  header('Content-Type: text/html; charset=UTF-8');
2084  echo file_get_contents($this->logDirectory()."/".$filename);
2085  exit;
2086  }
2087 
2088  public function downloadLog(){
2089  $filename = $_GET['logFile'];
2090  //Header
2091  header("Expires: 0");
2092  header("Cache-Control: private");
2093  header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
2094  header("Pragma: cache");
2095  header("Content-Description: File Transfer");
2096  header("Content-Type: application/octet-stream");
2097  header("Content-disposition: attachment; filename=$filename");
2098  echo file_get_contents($this->logDirectory()."/".$filename);
2099  exit;
2100  }
2101 
2102  private function getLogFileList($s_delete,$s_download,$s_open)
2103  {
2104  $data = array();
2105  foreach (new DirectoryIterator($this->logDirectory()) as $fileInfo) {
2106  if ($fileInfo->isDot()) {
2107  continue;
2108  }
2109  $item['filename'] = $fileInfo->getFilename();
2110  $parts = pathinfo($item['filename']);
2111  $fnameparts = preg_split('/_/', $parts['filename'], -1, PREG_SPLIT_NO_EMPTY);
2112  $deleteUrl = '&nbsp;<a href=#'." onclick=\"javascript:deleteFile('".$item['filename']."');\">".$s_delete."</a>";
2113  //no delete for most recent file
2114  if ($this->get_actual_attempts()==$fnameparts[1]) {$deleteUrl="";}
2115 
2116  $urlDownload = 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=downloadLog&ref_id='.$_GET["ref_id"].'&logFile='.$fileInfo->getFilename();
2117  $urlOpen = 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=openLog&ref_id='.$_GET["ref_id"].'&logFile='.$fileInfo->getFilename();
2118  $item['date'] = date('Y/m/d H:i:s', $fileInfo->getCTime());
2119  if ($parts['extension'] == "html") {
2120  $item['action'] =$deleteUrl."&nbsp;<a href=".$urlDownload.">".$s_download."</a>&nbsp;<a target=_new href=".$urlOpen.">".$s_open."</a>";
2121  } else {
2122  $item['action'] =$deleteUrl."&nbsp;<a href=".$urlDownload.">".$s_download."</a>";
2123  }
2124  if ($parts['extension'] == "html" || $parts['extension'] == "csv") {
2125  array_push($data,$item);
2126  }
2127  }
2128  usort($data,"datecmp");
2129  return $data;
2130  }
2131 
2132  public function liveLogContent()
2133  {
2134  header('Content-Type: text/html; charset=UTF-8');
2135  print file_get_contents($this->logFileName().".html");
2136  }
2137 
2138  public function debugGUI()
2139  {
2140  global $lng;
2141  $lng->loadLanguageModule("scormdebug");
2142 
2143 /* if ($_POST['password'] == $this->slm->getDebugPw()) {
2144  $_SESSION["debug_pw"] = $this->slm->getDebugPw();
2145  }
2146  if ($_SESSION["debug_pw"]!=$this->slm->getDebugPw()) {
2147  $this->tpl = new ilTemplate("tpl.scorm2004.debug_pw.html", false, false, "./Modules/Scorm2004");
2148  $this->tpl->setVariable('SUBMIT', $lng->txt("debugwindow_submit"));
2149  $this->tpl->setVariable('CANCEL', $lng->txt("debugwindow_cancel"));
2150  $this->tpl->setVariable('PASSWORD_ENTER', $lng->txt("debugwindow_password_enter"));
2151  $this->tpl->setVariable('DEBUG_URL','ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=debugGUI&ref_id='.$_GET["ref_id"]);
2152  } else {*/
2153  $this->tpl = new ilTemplate("tpl.scorm2004.debug.html", false, false, "./Modules/Scorm2004");
2154  $this->tpl->setVariable('CONSOLE', $lng->txt("debugwindow_console"));
2155  $this->tpl->setVariable('LOGS', $lng->txt("debugwindow_logs"));
2156  $this->tpl->setVariable('COMMENT', $lng->txt("debugwindow_comment"));
2157  $this->tpl->setVariable('COMMENT_ENTER', $lng->txt("debugwindow_comment_enter"));
2158  $this->tpl->setVariable('START_RECORDING', $lng->txt("debugwindow_start_recording"));
2159  $this->tpl->setVariable('STOP_RECORDING', $lng->txt("debugwindow_stop_recording"));
2160  $this->tpl->setVariable('DELETE_LOGFILE', $lng->txt("debugwindow_delete_logfile"));
2161  $this->tpl->setVariable('SUBMISSION_FAILED', $lng->txt("debugwindow_submission_failed"));
2162  $this->tpl->setVariable('SUBMIT', $lng->txt("debugwindow_submit"));
2163  $this->tpl->setVariable('CANCEL', $lng->txt("debugwindow_cancel"));
2164  $this->tpl->setVariable('FILENAME', $lng->txt("debugwindow_filename"));
2165  $this->tpl->setVariable('DATE', $lng->txt("debugwindow_date"));
2166  $this->tpl->setVariable('ACTION', $lng->txt("debugwindow_action"));
2167  $this->tpl->setVariable('RECORD_IMG', ilUtil::getImagePath("record.png","./Modules/Scorm2004"));
2168  $this->tpl->setVariable('STOP_IMG', ilUtil::getImagePath("stop.png","./Modules/Scorm2004"));
2169  $this->tpl->setVariable('COMMENT_IMG', ilUtil::getImagePath("comment.png","./Modules/Scorm2004"));
2170  $logfile = $this->logFileName().".html";
2171  $this->tpl->setVariable('LOGFILE',$this->logFileName().".html");
2172  $this->tpl->setVariable('FILES_DATA', json_encode($this->getLogFileList($lng->txt("debugwindow_delete"), $lng->txt("debugwindow_download"), $lng->txt("debugwindow_open"))));
2173 
2174  // path to latest yui distribution
2175  include_once "Services/YUI/classes/class.ilYuiUtil.php";
2176  $this->tpl->setVariable('PATH_YUI', ilYuiUtil::getLocalPath());
2177  //}
2178  echo $this->tpl->get("DEFAULT", true);
2179  }
2180 
2181  private function getLogTemplate()
2182  {
2183  return new ilTemplate("tpl.scorm2004.debugtxt.txt", true, true, "Modules/Scorm2004");
2184  }
2185 
2186  private function getDebugValues($test_sco = false)
2187  {
2188  global $ilDB,$ilLog;
2189  $ini_array = null;
2190  $dvalues = array();
2191 /*
2192  $res = $ilDB->queryF('
2193  SELECT debug_fields
2194  FROM sahs_lm
2195  WHERE id = %s',
2196  array('integer'),
2197  array($this->packageId)
2198  );
2199  $row = $ilDB->fetchAssoc($res);
2200  $debug_fields = $row['debug_fields'];
2201  if ($debug_fields == null) {*/
2202  $debug_fields = parse_ini_file("./Modules/Scorm2004/scripts/rtemain/debug_default.ini",true);
2203 // }
2204  if ($test_sco) {
2205  $ini_array = $debug_fields['test_sco'];
2206  } else {
2207  $ini_array = $debug_fields['normal_sco'];
2208  }
2209  foreach ($ini_array as $key => $value) {
2210  if ($value == 1) {
2211  array_push($dvalues,$key);
2212  }
2213  }
2214  return $dvalues;
2215  }
2216 
2217  public function postLogEntry()
2218  {
2219  global $ilLog,$lng;
2220  $lng->loadLanguageModule("scormdebug");
2221 
2222  $logdata = json_decode(file_get_contents('php://input'));
2223  $filename = $this->logFileName();
2224  $tmp_name = $this->logTmpName();
2225 
2226  $fh_txt = fopen($filename.".html", 'a') or die("can't open txt file");
2227  $fh_csv = fopen($filename.".csv", 'a') or die("can't open csv file");
2228  $fh_tmp = fopen($tmp_name, 'r') or die("can't open tmp file");
2229 
2230  //init tmp file
2231  if (filesize($tmp_name)>0) {
2232  $tmp_content = unserialize(fread($fh_tmp,filesize($tmp_name)));
2233  } else {
2234  $tmp_content = null;
2235  }
2236 
2237  fclose($fh_tmp);
2238 
2239  //reopen for writing
2240  $fh_tmp2 = fopen($tmp_name, 'w') or die("can't open tmp file");
2241 
2242 
2243  //write tmp
2244  $tmp_content[$logdata->scoid][$logdata->key]['value'] = $logdata->value;
2245  $tmp_content[$logdata->scoid][$logdata->key]['status'] = $logdata->result;
2246  $tmp_content[$logdata->scoid][$logdata->key]['action'] = $logdata->action;
2247 
2248  fwrite($fh_tmp2,serialize($tmp_content));
2249  fclose($fh_tmp2);
2250 
2251  $timestamp = date("d.m.Y H:i",time());
2252 
2253 
2254  $errorcode = $logdata->errorcode;
2255  $fixedFailure = false;
2256  $toleratedFailure = false;
2257  $extraErrorDescription = "";
2258  if ($errorcode == 200000) {
2259  $errorcode = 0;
2260  $toleratedFailure = true;
2261  $extraErrorDescription = "tolerated failure";
2262  }
2263  if ($errorcode>99999) {
2264  $errorcode-=100000;
2265  $fixedFailure = true;
2266  $extraErrorDescription = " failure corrected by ILIAS";
2267  }
2268  if (strpos($logdata->action,"ANALYZE")===false)
2269  {
2270  $errorDescriptions = array("0" => "",
2271  "101" => "General Exeption",
2272  "102" => "General Initialization Failure",
2273  "103" => "Already Initialized",
2274  "104" => "Content Instance Terminated",
2275  "111" => "General Termination Failure",
2276  "112" => "Termination Before Initialization",
2277  "113" => "Termination After Termination",
2278  "122" => "Retrieve Data Before Initialization",
2279  "123" => "Retrieve Data After Termination",
2280  "132" => "Store Data Before Initialization",
2281  "133" => "Store Data After Termination",
2282  "142" => "Commit Before Initialization",
2283  "143" => "Commit After Termination",
2284  "201" => "General Argument Error",
2285  "301" => "General Get Failure",
2286  "351" => "General Set Failure",
2287  "391" => "General Commit Failure",
2288  "401" => "Undefined Data Model Element",
2289  "402" => "Unimplemented Data Model Element",
2290  "403" => "Data Model Element Value Not Initialized",
2291  "404" => "Data Model Element Is Read Only",
2292  "405" => "Data Model Element Is Write Only",
2293  "406" => "Data Model Element Type Mismatch",
2294  "407" => "Data Model Element Value Out Of Range",
2295  "408" => "Data Model Dependency Not Established");
2296  $csv_string = $this->packageId.';"'
2297  .$logdata->scoid.'";"'
2298  .$logdata->scotitle.'";'
2299  .date("d.m.Y H:i",time()).';"'
2300  .$logdata->action.'";"'
2301  .$logdata->key.'";"'
2302  .str_replace("\"","\"\"",$logdata->value).'";"'
2303  .str_replace("\"","\"\"",$logdata->result).'";'
2304  .$errorcode.';'
2305  .$logdata->timespan.';"'
2306  .$errorDescriptions[$errorcode].$extraErrorDescription.'"'."\n";
2307  fwrite($fh_csv,$csv_string);
2308  }
2309 
2310  $sqlwrite = false;
2311  if($logdata->action == "Commit" || $logdata->action == "Terminate")
2312  {
2313  $sqlwrite = true;
2314  $sql_data = $this->getNodeData($logdata->scoid,$fh_csv);
2315  foreach ($sql_data as $key => $value) {
2316  $sql_string = $this->packageId.';"'
2317  .$logdata->scoid.'";"'
2318  .$logdata->scotitle.'";'
2319  .$timestamp.';"SQL";"'
2320  .$key.'";"'
2321  .str_replace("\"","\"\"",$value).'";;;;'."\n";
2322  fwrite($fh_csv,$sql_string);
2323  }
2324  }
2325 
2326  //delete files
2327  if ($logdata->action == "DELETE")
2328  {
2329  $filename = $logdata->value;
2330  $path = $this->logDirectory()."/".$filename;
2331  unlink($path);
2332  return;
2333  }
2334 
2335  //write TXT
2336  $logtpl = $this->getLogTemplate();
2337  $color = "red";
2338  $importantkey=1;
2339  $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');
2340 
2341  switch ($logdata->action) {
2342  case 'SetValue':
2343  if ($logdata->result == "true" && $errorcode == 0) $color = "green";
2344  if ($color=="green" && $logdata->key == "cmi.exit" && $logdata->value!="suspend") $color = "orange";
2345  if ($fixedFailure == false && $errorcode!=406) $logdata->value = '"'.$logdata->value.'"';
2346  if ($toleratedFailure == true) $color = "fuchsia";
2347  if ($fixedFailure == true) $color = "gray";
2348  break;
2349  case 'GetValue':
2350  if ($errorcode == 0) $color = "green";
2351  break;
2352  case 'Initialize':
2353  if ($errorcode == 0)
2354  {
2355  $color = "green";
2356  $logtpl->setCurrentBlock("InitializeStart");
2357  $logtpl->setVariable("SCO-title", $lng->txt("SCO-title"));
2358  $logtpl->setVariable("SCO_TITLE", $logdata->scotitle);
2359  $logtpl->setVariable("SCO-name", $lng->txt("SCO-name"));
2360  $logtpl->setVariable("SCO_NAME", $logdata->scoid);
2361  $logtpl->setVariable("started", $lng->txt("started"));
2362  $logtpl->setVariable("TIMESTAMP", $timestamp);
2363  $logtpl->setVariable("milliseconds", $lng->txt("milliseconds"));
2364  $logtpl->setVariable("API-call", $lng->txt("API-call"));
2365  $logtpl->setVariable("return_value", $lng->txt("return_value"));
2366  $logtpl->setVariable("error", $lng->txt("error"));
2367  $logtpl->parseCurrentBlock();
2368  }
2369  break;
2370  case 'Commit':
2371  if ($errorcode == 0) $color = "green";
2372  if ($fixedFailure == true) $color = "gray";
2373  break;
2374  case 'Terminate':
2375  if ($errorcode == 0) $color = "green";
2376  break;
2377  case 'GetErrorString':
2378  $importantkey=0;
2379  if ($errorcode == 0) $color = "green";
2380  break;
2381  case 'GetLastError':
2382  $logtpl->setCurrentBlock("GetLastError");
2383  $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2384  $logtpl->setVariable("RESULT", $logdata->result);
2385  $logtpl->parseCurrentBlock();
2386  break;
2387  case 'GetDiagnostic':
2388  $logtpl->setCurrentBlock("GetDiagnostic");
2389  $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2390  $logtpl->setVariable("KEY", $logdata->key);
2391  $logtpl->setVariable("RESULT", $logdata->result);
2392  $logtpl->parseCurrentBlock();
2393  break;
2394  case 'INFO':
2395  $logtpl->setCurrentBlock("INFO");
2396  $logtpl->setVariable("hint", $lng->txt("hint"));
2397  $logtpl->setVariable("KEY", $lng->txt($logdata->key));
2398  $logtpl->setVariable("VALUE", $logdata->value);
2399  $logtpl->parseCurrentBlock();
2400  break;
2401  case 'COMMENT':
2402  $logtpl->setCurrentBlock("COMMENT");
2403  $logtpl->setVariable("comment", $lng->txt("comment"));
2404  $logtpl->setVariable("generated", $lng->txt("generated"));
2405  $logtpl->setVariable("TIMESTAMP", $timestamp);
2406  $logtpl->setVariable("VALUE", $logdata->value);
2407  $logtpl->parseCurrentBlock();
2408  break;
2409  case 'ANALYZE':
2410  $logtpl->setCurrentBlock("ANALYZE");
2411  if (count($logdata->value) == 0) {
2412  $color = "green";
2413  $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
2414  $logtpl->setVariable("VALUE", "");
2415  } else {
2416  $tmpvalue = "SetValue(\"".implode("\", ... ),<br/>SetValue(\"",$logdata->value)."\", ... )";
2417  for ($i=0; $i <count($ArGetValues); $i++){
2418  $tmpvalue = str_replace("SetValue(\"cmi.".$ArGetValues[$i]."\", ... )","GetValue(\"cmi.".$ArGetValues[$i]."\")",$tmpvalue);
2419  }
2420  $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
2421  $logtpl->setVariable("VALUE", $tmpvalue);
2422  }
2423  $logtpl->setVariable("summary_for_SCO_without_test", $lng->txt("summary_for_SCO_without_test"));
2424  $logtpl->setVariable("generated", $lng->txt("generated"));
2425  $logtpl->setVariable("TIMESTAMP", $timestamp);
2426  $logtpl->setVariable("COLOR", $color);
2427  $logtpl->parseCurrentBlock();
2428  break;
2429  case 'ANALYZETEST':
2430  $logtpl->setCurrentBlock("ANALYZETEST");
2431  if (count($logdata->value) == 0) {
2432  $color = "green";
2433  $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("no_missing_API-calls"));
2434  $logtpl->setVariable("VALUE", "");
2435  } else {
2436  $tmpvalue = "SetValue(\"".implode("\", ... ),<br/>SetValue(\"",$logdata->value)."\", ... )";
2437  for ($i=0; $i <count($ArGetValues); $i++){
2438  $tmpvalue = str_replace("SetValue(\"cmi.".$ArGetValues[$i]."\", ... )","GetValue(\"cmi.".$ArGetValues[$i]."\")",$tmpvalue);
2439  }
2440  $logtpl->setVariable("ANALYZE_SUMMARY", $lng->txt("missing_API-calls"));
2441  $logtpl->setVariable("VALUE", $tmpvalue);
2442  }
2443  $logtpl->setVariable("summary_for_SCO_with_test", $lng->txt("summary_for_SCO_with_test"));
2444  $logtpl->setVariable("generated", $lng->txt("generated"));
2445  $logtpl->setVariable("TIMESTAMP", $timestamp);
2446  $logtpl->setVariable("COLOR", $color);
2447  $logtpl->parseCurrentBlock();
2448  break;
2449  case 'SUMMARY':
2450  $logtpl->setCurrentBlock("SUMMARY");
2451  $logtpl->setVariable("summary_csv", $lng->txt("summary_csv"));
2452  $logtpl->setVariable("TIMESTAMP", $timestamp);
2453  $logtpl->setVariable("summary_download", $lng->txt("summary_download"));
2454  $logtpl->parseCurrentBlock();
2455  break;
2456  default:
2457  $importantkey=0;
2458  $color = "orange";
2459  break;
2460  }
2461  if ($logdata->action == 'SetValue' || $logdata->action == 'GetValue')
2462  {
2463  $logtpl->setCurrentBlock($logdata->action);
2464  $logtpl->setVariable("ACTION", $logdata->action);
2465  $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2466  $logtpl->setVariable("KEY", $logdata->key);
2467  $logtpl->setVariable("VALUE", $logdata->value);
2468  $logtpl->setVariable("RESULT", $logdata->result);
2469  $logtpl->setVariable("ERRORCODE", $errorcode);
2470  $debugfields=$this->getDebugValues(true);
2471  $importantkey=0;
2472  for ($i=0; $i <count($debugfields) ; $i++){
2473  if ($logdata->key == $debugfields[$i]) $importantkey=1;
2474  }
2475  $logtpl->setVariable("IMPORTANTKEY", "".$importantkey);
2476  $logtpl->setVariable("COLOR", $color);
2477  $logtpl->parseCurrentBlock();
2478  }
2479  else if ($logdata->action != 'INFO' && $logdata->action != 'ANALYZE' && $logdata->action != 'ANALYZETEST' && $logdata->action != 'SUMMARY' && $logdata->action != 'COMMENT' && $logdata->action != 'GetDiagnostic' && $logdata->action != 'GetLastError')
2480  {
2481  $logtpl->setCurrentBlock("defaultCall");
2482  $logtpl->setVariable("ACTION", $logdata->action);
2483  $logtpl->setVariable("TIMESPAN", $logdata->timespan);
2484  $logtpl->setVariable("KEY", $logdata->key);
2485  $logtpl->setVariable("VALUE", $logdata->value);
2486  $logtpl->setVariable("RESULT", $logdata->result);
2487  $logtpl->setVariable("ERRORCODE", $errorcode);
2488  $logtpl->setVariable("IMPORTANTKEY", "".$importantkey);
2489  $logtpl->setVariable("COLOR", $color);
2490  $logtpl->parseCurrentBlock();
2491  }
2492 
2493  /*
2494  if ($sqlwrite == true) {
2495  $ilLog->write("SQL WRITE");
2496  $logtpl->setCurrentBlock("SqlLog");
2497  $logtpl->setVariable("SQL_STRING", $sql_text);
2498  $logtpl->parseCurrentBlock();
2499  }
2500  */
2501 
2502  //create summary
2503  if ($logdata->action == "SUMMARY") {
2504  $this->createSummary($tmp_content);
2505  }
2506 
2507  fwrite($fh_txt,$logtpl->get());
2508  fclose($fh_txt);
2509  fclose($fh_csv);
2510  }
2511 
2512  private function getStructureFlat($data)
2513  {
2514  for ($i=0; $i <count($data) ; $i++) {
2515  $element = array();
2516  $element['title'] = $data[$i]['title'];
2517  $element['id'] = $data[$i]['id'];
2518  if ($data[$i]['sco'] == 1) {
2519  $element['sco'] = "sco";
2520  } else {
2521  $element['sco'] = "assset";
2522  }
2523  if ( $data[$i]['href'] !=null ) {
2524  array_push($this->flat_structure,$element);
2525  }
2526  if ($data[$i]['item']!=null) {
2527  $this->getStructureFlat($data[$i]['item']);
2528  }
2529  }
2530  }
2531 
2532  private function createSummary($api_data)
2533  {
2534  global $ilDB;
2535 
2536  $csv_data = null;
2537  //csv columns
2538  $columns_fixed = array('id','title','type','attempted');
2539 
2540  $ini_data = parse_ini_file("./Modules/Scorm2004/scripts/rtemain/debug_default.ini",true);
2541  $ini_array = $ini_data['summary'];
2542  $colums_variable = array();
2543  $api_keys = array();
2544 
2545  foreach ($ini_array as $key => $value) {
2546  if ($value == 1) {
2547  array_push($colums_variable,$key);
2548  array_push($api_keys,$key);
2549  array_push($colums_variable,"Status");
2550  }
2551  }
2552 
2553  $header_array = array_merge($columns_fixed, $colums_variable);
2554 
2555  $csv_header = implode(";",$header_array);
2556 
2557  //get strcuture
2558  $res = $ilDB->queryF(
2559  'SELECT jsdata FROM cp_package WHERE obj_id = %s',
2560  array('integer'),
2561  array($this->packageId)
2562  );
2563 
2564  $packageData = $ilDB->fetchAssoc($res);
2565 
2566  $structure = json_decode($packageData['jsdata'],true);
2567 
2568 
2569  $this->flat_structure = array(); //used for recursion
2570  $this->getStructureFlat($structure['item']['item']);
2571 
2572  foreach ($this->flat_structure as $tree_element) {
2573 
2574  $csv_data = $csv_data.$tree_element['id'].";".$tree_element['title'].";".$tree_element['sco'].";";
2575  if ($api_data[$tree_element['id']] != null) {
2576  $csv_data = $csv_data."X".";";
2577  } else {
2578  $csv_data = $csv_data.";";
2579  }
2580 
2581  //write api data
2582  $id = $tree_element['id'];
2583  foreach ($api_keys as $api_element) {
2584  if ($api_data[$id]!=null) {
2585  if ($api_data[$id][$api_element]!=null) {
2586  $csv_data = $csv_data.$api_data[$id][$api_element]['value'].";".$api_data[$id][$api_element]['status'].";";
2587  } else {
2588  $csv_data = $csv_data.";;";
2589  }
2590  }
2591  }
2592  $csv_data = $csv_data."\n";
2593  }
2594 
2595  $fh = fopen($this->summaryFileName(),"w");
2596  fwrite($fh,$csv_header."\n".$csv_data);
2597  fclose($fh);
2598  unlink($this->logTmpName());
2599  }
2604  function get_last_visited($a_obj_id, $a_user_id)
2605  {
2606  global $ilDB;
2607 
2608  $val_set = $ilDB->queryF('
2609  SELECT rvalue FROM cmi_custom
2610  WHERE user_id = %s
2611  AND sco_id = %s
2612  AND lvalue = %s
2613  AND obj_id = %s',
2614  array('integer','integer', 'text','integer'),
2615  array($a_user_id, 0,'last_visited',$a_obj_id));
2616 
2617  $val_rec = $ilDB->fetchAssoc($val_set);
2618  return $val_rec["rvalue"];
2619  }
2620 
2621  function set_last_visited($a_obj_id, $a_user_id, $last_visited)
2622  {
2623  global $ilDB;
2624  $pre_last_visited=$this->get_last_visited($a_obj_id, $a_user_id);
2625 
2626  if ($pre_last_visited == $last_visited) return;
2627  if ($pre_last_visited == null) {
2628  $ilDB->manipulateF('
2629  INSERT INTO cmi_custom (rvalue, user_id, sco_id, obj_id, lvalue, c_timestamp)
2630  VALUES(%s, %s, %s, %s, %s, %s)',
2631  array('text', 'integer', 'integer', 'integer', 'text', 'timestamp'),
2632  array($last_visited, $a_user_id, 0, $a_obj_id, 'last_visited', date('Y-m-d H:i:s'))
2633  );
2634  }
2635  else
2636  {
2637  $ilDB->manipulateF('
2638  UPDATE cmi_custom
2639  SET rvalue = %s,
2640  c_timestamp = %s
2641  WHERE user_id = %s
2642  AND sco_id = %s
2643  AND obj_id = %s
2644  AND lvalue = %s',
2645  array('text', 'timestamp', 'integer', 'integer', 'integer', 'text'),
2646  array($last_visited, date('Y-m-d H:i:s'), $a_user_id, 0, $a_obj_id, 'last_visited')
2647  );
2648  }
2649  }
2650 
2651 }
2652 
2653 function datecmp($a, $b){
2654  if (strtotime($a['date']) == strtotime($b['date'])) {
2655  return 0;
2656  }
2657  return (strtotime($a['date']) < strtotime($b['date'])) ? 1 :-1;
2658 }
2659 
2660 ?>