ILIAS  eassessment Revision 61809
 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 ENABLE_JS_DEBUG = 1;
19 
20  const NONE = 0;
21  const READONLY = 1;
22  const WRITEONLY = 2;
23  const READWRITE = 3;
24 
25  static private $schema = array // order of entries matters!
26  (
27  'package' => array(
28  'user_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>user_id),
29  'learner_name' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>learner_name),
30  'slm_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>slm_id),
31  'mode' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>c_mode),
32  'credit' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>credit),
33  ),
34  'node' => array(
35  'accesscount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accesscount),
36  'accessduration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accessduration),
37  'accessed' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>accessed),
38  'activityAbsoluteDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityabsduration),
39  'activityAttemptCount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityattemptcount),
40  'activityExperiencedDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityexpduration),
41  'activityProgressStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>activityprogstatus),
42  'attemptAbsoluteDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptabsduration),
43  'attemptCompletionAmount' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptcomplamount),
44  'attemptCompletionStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptcomplstatus),
45  'attemptExperiencedDuration' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptexpduration),
46  'attemptProgressStatus' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>attemptprogstatus),
47  'audio_captioning' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>audio_captioning),
48  'audio_level' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>audio_level),
49  'availableChildren' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>availablechildren),
50  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
51  'completion' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion),
52  'completion_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_status),
53  'completion_threshold' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_threshold),
54  'cp_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cp_node_id),
55  'created' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>created),
56  'credit' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>credit),
57  'delivery_speed' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>delivery_speed),
58  'entry' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_entry),
59  'exit' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_exit),
60  'language' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_language),
61  'launch_data' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>launch_data),
62  'learner_name' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>learner_name),
63  'location' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>location),
64  'max' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_max),
65  'min' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_min),
66  'mode' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_mode),
67  'modified' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>modified),
68  'progress_measure' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>progress_measure),
69  'raw' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_raw),
70  'scaled' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled),
71  'scaled_passing_score' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled_passing_score),
72  'session_time' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>session_time),
73  'success_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>success_status),
74  'suspend_data' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>suspend_data),
75  'total_time' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>total_time),
76  'user_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>user_id),
77  ),
78  'comment' => array (
79  'cmi_comment_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_comment_id),
80  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
81  'comment' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_comment),
82  'timestamp' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_timestamp),
83  'location' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>location),
84  'sourceIsLMS' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>sourceislms),
85  ),
86  'correct_response' => array(
87  'cmi_correct_response_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_correct_resp_id),
88  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
89  'pattern' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>pattern),
90  ),
91  'interaction' => array(
92  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
93  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
94  'description' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>description),
95  'id' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>id),
96  'latency' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>latency),
97  'learner_response' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>learner_response),
98  'result' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>result),
99  'timestamp' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_timestamp),
100  'type' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_type),
101  'weighting' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>weighting),
102  ),
103  'objective' => array(
104  'cmi_interaction_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_interaction_id),
105  'cmi_node_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_node_id),
106  'cmi_objective_id' => array('pattern'=>null, 'permission' => self::NONE, 'default'=>null, 'dbfield'=>cmi_objective_id),
107  'completion_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>completion_status),
108  'description' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>description),
109  'id' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>id),
110  'max' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_max),
111  'min' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_min),
112  'raw' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>c_raw),
113  'scaled' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scaled),
114  'progress_measure' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>progress_measure),
115  'success_status' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>success_status),
116  'scope' => array('pattern'=>null, 'permission' => self::READWRITE, 'default'=>null, 'dbfield'=>scope),
117  ),
118  );
119 
120  private $userId;
121  public $packageId;
122  public $jsMode;
123 
124  var $ilias;
125  var $slm;
126  var $tpl;
127 
128  function __construct()
129  {
130 
131  global $ilias, $tpl, $ilCtrl, $ilUser, $lng;
132 
133  if ($_REQUEST['learnerId']) {
134  $this->userId = $_REQUEST['learnerId'];
135  } else {
136  $this->userId = $GLOBALS['USER']['usr_id'];
137  }
138  $this->packageId = (int) $_REQUEST['packageId'];
139  $this->jsMode = strpos($_SERVER['HTTP_ACCEPT'], 'text/javascript')!==false;
140 
141  $this->page = $_REQUEST['page'];
142 
143  $this->slm =& new ilObjSCORM2004LearningModule($_GET["ref_id"], true);
144 
145 
146  $this->ilias =& $ilias;
147  $this->tpl =& $tpl;
148  $this->ctrl =& $ilCtrl;
149 
150  $this->packageId=ilObject::_lookupObjectId($_GET['ref_id']);
151  $this->ref_id = $_GET['ref_id'];
152  $this->userId=$ilUser->getID();
153 
154  if ($_GET['envEditor'] != null) {
155  $this->envEditor = $_GET['envEditor'];
156  } else {
157  $this->envEditor = 0;
158  }
159 
160  }
161 
165  function &executeCommand()
166  {
167  global $ilAccess, $ilLog, $ilUser, $lng, $ilias;
168 
169  $next_class = $this->ctrl->getNextClass($this);
170  $cmd = $this->ctrl->getCmd();
171 
172  if (!$ilAccess->checkAccess("read", "", $_GET["ref_id"]))
173  {
174  $ilias->raiseError($lng->txt("permission_denied"), $ilias->error_obj->WARNING);
175  }
176 
177 //$ilLog->write("SCORM: Player cmd: ".$cmd);
178 
179  switch($cmd){
180 
181  case 'getRTEjs':
182  $this->getRTEjs();
183  break;
184 
185  case 'cp':
186  $this->getCPData();
187  break;
188 
189  case 'adlact':
190  $this->getADLActData();
191  break;
192 
193  case 'suspend':
194  $this->suspendADLActData();
195  break;
196 
197  case 'getSuspend':
198  $this->getSuspendData();
199  break;
200 
201  case 'gobjective':
202  $this->writeGObjective();
203  break;
204 
205  case 'getGobjective':
206  $this->readGObjective();
207  break;
208 
209  case 'cmi':
210 
211  if ($_SERVER['REQUEST_METHOD']=='POST') {
212  $this->persistCMIData();
213  //error_log("Saved CMI Data");
214  } else {
215  $this->fetchCMIData();
216  }
217  break;
218 
219  case 'specialPage':
220  $this->specialPage();
221  break;
222 
223 
224  default:
225  $this->getPlayer();
226  break;
227  }
228 
229  }
230 
231  function getRTEjs()
232  {
233  $filename = "rte-min.js";
234  if (self::ENABLE_JS_DEBUG==1) {
235  $filename = "rte.js";
236  }
237  $js_data = file_get_contents("./Modules/Scorm2004/scripts/buildrte/".$filename);
238  if (self::ENABLE_GZIP==1) {
239  ob_start("ob_gzhandler");
240  header('Content-Type: text/javascript; charset=UTF-8');
241  } else {
242  header('Content-Type: text/javascript; charset=UTF-8');
243  }
244 
245 
246  echo $js_data;
247  }
248 
249 
250  function getDataDirectory()
251  {
252  $webdir=str_replace("/ilias.php","",$_SERVER["SCRIPT_NAME"]);
253  //load ressources always with absolute URL..relative URLS fail on innersco navigation on certain browsers
254  $lm_dir=$webdir."/".ILIAS_WEB_DIR."/".$this->ilias->client_id ."/lm_data"."/lm_".$this->packageId;
255  return $lm_dir;
256  }
257 
258 
259 
260  public function getPlayer()
261  {
262  global $ilUser,$lng, $ilias, $ilSetting;
263  // player basic config data
264 
265  if ($this->slm->getSession()) {
266  $session_timeout = (int)($ilias->ini->readVariable("session","expire"))/2;
267  } else {
268  $session_timeout = 0;
269  }
270 
271  $config = array
272  (
273  'cp_url' => 'ilias.php?baseClass=ilSAHSPresentationGUI' . '&cmd=cp&ref_id='.$_GET["ref_id"],
274  'cmi_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=cmi&ref_id='.$_GET["ref_id"],
275  'adlact_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=adlact&ref_id='.$_GET["ref_id"],
276  'specialpage_url'=> 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=specialPage&ref_id='.$_GET["ref_id"],
277  'suspend_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=suspend&ref_id='.$_GET["ref_id"],
278  'get_suspend_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getSuspend&ref_id='.$_GET["ref_id"],
279  'gobjective_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=gobjective&ref_id='.$_GET["ref_id"],
280  'get_gobjective_url'=>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getGobjective&ref_id='.$_GET["ref_id"],
281  'ping_url' =>'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=pingSession&ref_id='.$_GET["ref_id"],
282  'scope'=>$this->getScope(),
283  'learner_id' => (string) $ilUser->getID(),
284  'course_id' => (string) $this->packageId,
285  'learner_name' => $ilUser->getFirstname()." ".$ilUser->getLastname(),
286  'mode' => 'normal',
287  'credit' => 'credit',
288  'auto_review' => $this->slm->getAutoReview(),
289  'hide_navig' => $this->slm->getHideNavig(),
290  'debug' => $this->slm->getDebug(),
291  'package_url' => $this->getDataDirectory()."/",
292  'session_ping' => $session_timeout,
293  'envEditor' => $this->envEditor
294  );
295 
296 
297  //language strings
298  $langstrings['btnStart'] = $lng->txt('scplayer_start');
299  $langstrings['btnExit'] = $lng->txt('scplayer_exit');
300  $langstrings['btnExitAll'] = $lng->txt('scplayer_exitall');
301  $langstrings['btnSuspendAll'] = $lng->txt('scplayer_suspendall');
302  $langstrings['btnPrevious'] = $lng->txt('scplayer_previous');
303  $langstrings['btnContinue'] = $lng->txt('scplayer_continue');
304  $langstrings['btnhidetree']=$lng->txt('scplayer_hidetree');
305  $langstrings['btnshowtree']=$lng->txt('scplayer_showtree');
306  $config['langstrings'] = $langstrings;
307 
308  //template variables
309  $this->tpl = new ilTemplate("tpl.scorm2004.player.html", false, false, "Modules/Scorm2004");
310  $this->tpl->setVariable('JSON_LANGSTRINGS', json_encode($langstrings));
311  include_once("./Services/YUI/classes/class.ilYuiUtil.php");
312  $this->tpl->setVariable('YUI_PATH', ilYuiUtil::getLocalPath());
313  $this->tpl->setVariable($langstrings);
314  $this->tpl->setVariable('DOC_TITLE', 'ILIAS SCORM 2004 Player');
315  $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
316  $this->tpl->setVariable('JS_DATA', json_encode($config));
317  list($tsfrac, $tsint) = explode(' ', microtime());
318  $this->tpl->setVariable('TIMESTAMP', sprintf('%d%03d', $tsint, 1000*(float)$tsfrac));
319  $this->tpl->setVariable('BASE_DIR', './Modules/Scorm2004/');
320 
321  //set icons path
322  $this->tpl->setVariable('IC_ASSET', ilUtil::getImagePath("scorm/asset_s.gif",false));
323  $this->tpl->setVariable('IC_COMPLETED', ilUtil::getImagePath("scorm/completed_s.gif",false));
324  $this->tpl->setVariable('IC_NOTATTEMPTED', ilUtil::getImagePath("scorm/not_attempted_s.gif",false));
325  $this->tpl->setVariable('IC_RUNNING', ilUtil::getImagePath("scorm/running_s.gif",false));
326  $this->tpl->setVariable('IC_INCOMPLETE', ilUtil::getImagePath("scorm/incomplete_s.gif",false));
327  $this->tpl->setVariable('IC_PASSED', ilUtil::getImagePath("scorm/passed_s.gif",false));
328  $this->tpl->setVariable('IC_FAILED', ilUtil::getImagePath("scorm/failed_s.gif",false));
329  $this->tpl->setVariable('IC_BROWSED', ilUtil::getImagePath("scorm/browsed.gif",false));
330 
331  //include scripts
332  $this->tpl->setVariable('JS_SCRIPTS', 'ilias.php?baseClass=ilSAHSPresentationGUI' .'&cmd=getRTEjs&ref_id='.$_GET["ref_id"]);
333 
334  //disable top menu
335  if ($this->slm->getNoMenu()=="y") {
336  $this->tpl->setVariable("VAL_DISPLAY", "style=\"display:none;\"");
337  }
338 
339 
340  //check for max_attempts and raise error if max_attempts is exceeded
341  if ($this->get_max_attempts()!=0) {
342  if ($this->get_actual_attempts() >= $this->get_max_attempts()) {
343  header('Content-Type: text/html; charset=utf-8');
344  echo($lng->txt("cont_sc_max_attempt_exceed"));
345  exit;
346  }
347  }
348 
349  //count attempt
350  //Cause there is no way to check if the Java-Applet is sucessfully loaded, an attempt equals opening the SCORM player
351 
352  $this->increase_attempt();
353  $this->save_module_version();
354 
355  $this->tpl->show("DEFAULT", false);
356  }
357 
358  public function getCPData()
359  {
360  global $ilDB;
361 
362  $res = $ilDB->queryF(
363  'SELECT jsdata FROM cp_package WHERE obj_id = %s',
364  array('integer'),
365  array($this->packageId)
366  );
367  $packageData = $ilDB->fetchAssoc($res);
368 
369  $jsdata = $packageData['jsdata'];
370  if (!$jsdata) $jsdata = 'null';
371  if ($this->jsMode)
372  {
373  header('Content-Type: text/javascript; charset=UTF-8');
374  print($jsdata);
375  }
376  else
377  {
378  header('Content-Type: text/plain; charset=UTF-8');
379  $jsdata = json_decode($jsdata);
380  print_r($jsdata);
381  }
382  }
383 
384  public function getADLActData()
385  {
386  global $ilDB;
387 
388  $res = $ilDB->queryF(
389  'SELECT activitytree FROM cp_package WHERE obj_id = %s',
390  array('integer'),
391  array($this->packageId)
392  );
393  $data = $ilDB->fetchAssoc($res);
394 
395  $activitytree = $data['activitytree'];
396 
397  if(!$activitytree)
398  {
399  $activitytree = 'null';
400  }
401  if($this->jsMode)
402  {
403  header('Content-Type: text/javascript; charset=UTF-8');
404  print($activitytree);
405  }
406  else
407  {
408  header('Content-Type: text/plain; charset=UTF-8');
409  $activitytree = json_decode($activitytree);
410  print_r($activitytree);
411  }
412  }
413 
414  public function pingSession()
415  {
416  //do nothing except returning header
417  header('Content-Type: text/plain; charset=UTF-8');
418  print("");
419  }
420 
421  public function getScope()
422  {
423  global $ilDB, $ilUser;
424 
425  $res = $ilDB->queryF(
426  'SELECT global_to_system FROM cp_package WHERE obj_id = %s',
427  array('integer'),
428  array($this->packageId)
429  );
430  $data = $ilDB->fetchAssoc($res);
431 
432  $gystem = $data['global_to_system'];
433  if($gystem == 1)
434  $gsystem = 'null';
435  else
436  $gsystem = $this->packageId;
437 
438  return $gsystem;
439  }
440 
441  public function getSuspendData()
442  {
443  global $ilDB, $ilUser;
444 
445  $res = $ilDB->queryF(
446  'SELECT data FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
447  array('integer', 'integer'),
448  array($this->packageId, $ilUser->getId())
449  );
450  $data = $ilDB->fetchAssoc($res);
451 
452  $suspend_data = $data['data'];
453  if($this->jsMode)
454  {
455  header('Content-Type: text/javascript; charset=UTF-8');
456  print($suspend_data);
457  }
458  else
459  {
460  header('Content-Type: text/plain; charset=UTF-8');
461  $suspend_data = json_decode($suspend_data);
462  print_r($suspend_data);
463  }
464 
465  //delete delivered suspend data
466  $ilDB->manipulateF(
467  'DELETE FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
468  array('integer', 'integer'),
469  array($this->packageId, $ilUser->getId())
470  );
471  }
472 
473  public function suspendADLActData()
474  {
475  global $ilDB, $ilUser;
476 
477  $res = $ilDB->queryF(
478  'SELECT * FROM cp_suspend WHERE obj_id = %s AND user_id = %s',
479  array('integer', 'integer'),
480  array($this->packageId, $ilUser->getId())
481  );
482 
483  if(!$ilDB->numRows($res))
484  {
485  $ilDB->insert('cp_suspend', array(
486  'data' => array('clob', file_get_contents('php://input')),
487  'obj_id' => array('integer', $this->packageId),
488  'user_id' => array('integer', $ilUser->getId())
489  ));
490  }
491  else
492  {
493  $ilDB->update('cp_suspend',
494  array(
495  'data' => array('clob', file_get_contents('php://input'))
496  ),
497  array(
498  'obj_id' => array('integer', $this->packageId),
499  'user_id' => array('integer', $ilUser->getId())
500  )
501  );
502  }
503  }
504 
505  public function readGObjective()
506  {
507  global $ilDB, $ilUser;
508 
509  //get json string
510  $g_data = new stdClass();
511 
512  $query = 'SELECT objective_id, scope_id, satisfied, measure, user_id '
513  . 'FROM cmi_gobjective, cp_node, cp_mapinfo '
514  . 'WHERE (cmi_gobjective.objective_id <> %s AND cmi_gobjective.status IS NULL '
515  . 'AND cp_node.slm_id = %s AND cp_node.nodename = %s '
516  . 'AND cp_node.cp_node_id = cp_mapinfo.cp_node_id '
517  . 'AND cmi_gobjective.objective_id = cp_mapinfo.targetobjectiveid) '
518  . 'GROUP BY objective_id, scope_id, satisfied, measure, user_id';
519  $res = $ilDB->queryF(
520  $query,
521  array('text', 'integer', 'text'),
522  array('-course_overall_status-', $this->packageId, 'mapInfo')
523  );
524  while($row = $ilDB->fetchAssoc($res))
525  {
526  $learner = $row['user_id'];
527  $objective_id = $row['objective_id'];
528  if($row['scope_id'] == 0)
529  {
530  $scope = "null";
531  }
532  else
533  {
534  $scope = $row['scope_id'];
535  }
536 
537  if($row['satisfied'] != NULL)
538  {
539  $toset = $row['satisfied'];
540  $g_data->{"satisfied"}->{$objective_id}->{$learner}->{$scope} = $toset;
541  }
542 
543  if($row['measure'] != NULL)
544  {
545  $toset = $row['measure'];
546  $g_data->{"measure"}->{$objective_id}->{$learner}->{$scope} = $toset;
547  }
548  }
549  $gobjective_data = json_encode($g_data);
550  if ($this->jsMode)
551  {
552  header('Content-Type: text/javascript; charset=UTF-8');
553  print($gobjective_data);
554  }
555  else
556  {
557  header('Content-Type: text/plain; charset=UTF-8');
558  $gobjective_data = json_decode($gobjective_data);
559  print_r($gobjective_data);
560  }
561  }
562 
563  //saves global_objectives to database
564  public function writeGObjective()
565  {
566  global $ilDB, $ilUser, $ilLog;
567 
568  $user = $ilUser->getId();
569  $package = $this->packageId;
570 
571  //get json string
572  $g_data = json_decode(file_get_contents('php://input'));
573 
574  //iterate over assoziative array
575  if($g_data == null)
576  return null;
577 
578  foreach($g_data as $key => $value)
579  {
580  //objective
581  //learner = ilias learner id
582  //scope = null / course
583  foreach($value as $skey => $svalue)
584  {
585  //we always have objective and learner id
586  if($g_data->$key->$skey->$user->$package)
587  {
588  $o_value = $g_data->$key->$skey->$user->$package;
589  $scope = $package;
590  }
591  else
592  {
593  //scope 0
594  $o_value = $g_data->$key->$skey->$user->{"null"};
595  //has to be converted to NULL in JS Later
596  $scope = 0;
597  }
598 
599  //insert into database
600  $objective_id = $skey;
601  $toset = $o_value;
602  $dbuser = $ilUser->getId();
603 
604  //check for existence (if not, create)
605  if($key == "satisfied")
606  {
607  $res = $ilDB->queryF('
608  SELECT * FROM cmi_gobjective
609  WHERE objective_id = %s
610  AND user_id = %s
611  AND scope_id = %s',
612  array('text', 'integer', 'integer'),
613  array($objective_id, $dbuser, $scope)
614  );
615  $ilLog->write("Count is: ".$ilDB->numRows($res));
616  if(!$ilDB->numRows($res))
617  {
618  $ilDB->manipulateF('
619  INSERT INTO cmi_gobjective
620  (objective_id, user_id, satisfied, scope_id)
621  VALUES (%s, %s, %s, %s)',
622  array('text', 'integer', 'text', 'integer'),
623  array($objective_id, $dbuser, $toset, $scope)
624  );
625  }
626  else
627  {
628  $ilDB->manipulateF('
629  UPDATE cmi_gobjective
630  SET satisfied = %s
631  WHERE objective_id = %s
632  AND user_id = %s
633  AND scope_id = %s',
634  array('text', 'text', 'integer', 'integer'),
635  array($toset, $objective_id, $dbuser, $scope)
636  );
637  }
638  }
639  if($key == "measure")
640  {
641  $res = $ilDB->queryF('
642  SELECT * FROM cmi_gobjective
643  WHERE objective_id = %s
644  AND user_id = %s
645  AND scope_id = %s',
646  array('text', 'integer', 'integer'),
647  array($objective_id, $dbuser, $scope)
648  );
649  $ilLog->write("Count is: ".$ilDB->numRows($res));
650  if(!$ilDB->numRows($res))
651  {
652  $ilDB->manipulateF('
653  INSERT INTO cmi_gobjective
654  (objective_id, user_id, measure, scope_id)
655  VALUES (%s, %s, %s, %s)',
656  array('text', 'integer', 'text', 'integer'),
657  array($objective_id, $dbuser, $toset, $scope)
658  );
659  }
660  else
661  {
662  $ilDB->manipulateF('
663  UPDATE cmi_gobjective
664  SET measure = %s
665  WHERE objective_id =%s
666  AND user_id = %s
667  AND scope_id = %s',
668  array('text', 'text', 'integer', 'integer'),
669  array($toset, $objective_id, $dbuser, $scope)
670  );
671  }
672  }
673  if($key == "status")
674  {
675  //special handling for status
676  $completed = $g_data->$key->$skey->$user->{completed};
677  $measure = $g_data->$key->$skey->$user->{measure};
678  $satisfied = $g_data->$key->$skey->$user->{satisfied};
679  $obj = '-course_overall_status-';
680  $pkg_id = $this->packageId;
681 
682  $res = $ilDB->queryF('
683  SELECT * FROM cmi_gobjective
684  WHERE objective_id =%s
685  AND user_id = %s
686  AND scope_id = %s',
687  array('text', 'integer', 'integer'),
688  array($obj, $dbuser, $pkg_id)
689  );
690  $ilLog->write("Count is: ".$ilDB->numRows($res));
691  if(!$ilDB->numRows($res))
692  {
693  $ilDB->manipulateF('
694  INSERT INTO cmi_gobjective
695  (user_id, status, scope_id, measure, satisfied, objective_id)
696  VALUES (%s, %s, %s, %s, %s, %s)',
697  array('integer', 'text', 'integer', 'text', 'text', 'text'),
698  array($dbuser, $completed, $pkg_id, $measure, $satisfied, $obj)
699  );
700  }
701  else
702  {
703  $ilDB->manipulateF('
704  UPDATE cmi_gobjective
705  SET status = %s,
706  measure = %s,
707  satisfied = %s
708  WHERE objective_id = %s
709  AND user_id = %s
710  AND scope_id = %s',
711  array('text', 'text', 'text', 'text', 'integer', 'integer'),
712  array($completed, $measure, $satisfied, $obj, $dbuser, $pkg_id)
713  );
714  }
715  }
716  }
717  }
718 
719  // update learning progress
720  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
721  ilLPStatusWrapper::_updateStatus($package, $user);
722  }
723 
724  public function specialPage() {
725 
726  global $lng;
727 
728  $specialpages = array (
729  "_COURSECOMPLETE_" => "seq_coursecomplete",
730  "_ENDSESSION_" => "seq_endsession",
731  "_SEQBLOCKED_" => "seq_blocked",
732  "_NOTHING_" => "seq_nothing",
733  "_ERROR_" => "seq_error",
734  "_DEADLOCK_" => "seq_deadlock",
735  "_INVALIDNAVREQ_" => "seq_invalidnavreq",
736  "_SEQABANDON_" => "seq_abandon",
737  "_SEQABANDONALL_" => "seq_abandonall",
738  "_TOC_" => "seq_toc"
739  );
740 
741  $this->tpl = new ilTemplate("tpl.scorm2004.specialpages.html", false, false, "Modules/Scorm2004");
742  $this->tpl->setVariable("LOCATION_STYLESHEET", ilUtil::getStyleSheetLocation());
743  $this->tpl->setVariable('TXT_SPECIALPAGE',$lng->txt($specialpages[$this->page]));
744  if ($this->page!="_TOC_" && $this->page!="_SEQABANDON_" && $this->page!="_SEQABANDONALL_" ) {
745  $this->tpl->setVariable('CLOSE_WINDOW',$lng->txt('seq_close'));
746  } else {
747  $this->tpl->setVariable('CLOSE_WINDOW',"");
748  }
749  $this->tpl->show("DEFAULT", false);
750  }
751 
752 
753  public function fetchCMIData()
754  {
755  $data = $this->getCMIData($this->userId, $this->packageId);
756  if ($this->jsMode)
757  {
758  header('Content-Type: text/javascript; charset=UTF-8');
759  print(json_encode($data));
760  }
761  else
762  {
763  header('Content-Type: text/plain; charset=UTF-8');
764  print(var_export($data, true));
765  }
766  }
767 
768  public function persistCMIData($data = null)
769  {
770  global $ilLog;
771 
772  if ($this->slm->getDefaultLessonMode() == "browse") {return;}
773 
774  $data = json_decode(is_string($data) ? $data : file_get_contents('php://input'));
775  $ilLog->write("Got data:". file_get_contents('php://input'));
776 
777  $return = $this->setCMIData($this->userId, $this->packageId, $data, $this->ref_id);
778 
779  if ($this->jsMode)
780  {
781  header('Content-Type: text/javascript; charset=UTF-8');
782  print(json_encode($return));
783  }
784  else
785  {
786  header('Content-Type: text/html; charset=UTF-8');
787  print(var_export($return, true));
788  }
789  }
790 
795  private function normalizeFields($table, &$node)
796  {
797  return;
798  foreach (self::$schema[$table] as $k => $v)
799  {
800  $value = $node->$k;
801  if (isset($value) && is_string($v) && !preg_match($v, $value))
802  {
803  unset($node->$k);
804  }
805  }
806  }
807 
808  private function getCMIData($userId, $packageId)
809  {
810  global $ilDB;
811 
812  $result = array(
813  'schema' => array(),
814  'data' => array()
815  );
816 
817  foreach(self::$schema as $k => &$v)
818  {
819  $result['schema'][$k] = array_keys($v);
820 
821  switch ($k)
822  {
823  case "node":
824  $q = 'SELECT cmi_node.*
825  FROM cmi_node
826  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
827  WHERE cmi_node.user_id = %s
828  AND cp_node.slm_id = %s';
829 
830  break;
831 
832  case "comment":
833  $q = 'SELECT cmi_comment.*
834  FROM cmi_comment
835  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_comment.cmi_node_id
836  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
837  WHERE cmi_node.user_id = %s
838  AND cp_node.slm_id = %s';
839 
840  break;
841 
842  case "correct_response":
843  $q = 'SELECT cmi_correct_response.*
844  FROM cmi_correct_response
845  INNER JOIN cmi_interaction
846  ON cmi_interaction.cmi_interaction_id = cmi_correct_response.cmi_interaction_id
847  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
848  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
849  WHERE cmi_node.user_id = %s
850  AND cp_node.slm_id = %s';
851 
852  break;
853 
854  case "interaction":
855  $q = 'SELECT cmi_interaction.*
856  FROM cmi_interaction
857  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
858  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
859  WHERE cmi_node.user_id = %s
860  AND cp_node.slm_id = %s';
861 
862  break;
863 
864  case "objective":
865  $q = 'SELECT cmi_objective.*
866  FROM cmi_objective
867  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_objective.cmi_node_id
868  INNER JOIN cp_node ON cp_node.cp_node_id = cmi_node.cp_node_id
869  WHERE cmi_node.user_id = %s
870  AND cp_node.slm_id = %s';
871 
872  break;
873 
874  case "package":
875  $q = 'SELECT usr_data.usr_id user_id,
876  CONCAT(CONCAT(COALESCE(usr_data.firstname, \'\'), \' \'), COALESCE(usr_data.lastname, \'\')) learner_name,
877  sahs_lm.id slm_id , sahs_lm.default_lesson_mode "mode", sahs_lm.credit
878  FROM usr_data, cp_package
879  INNER JOIN sahs_lm ON cp_package.obj_id = sahs_lm.id
880  WHERE usr_data.usr_id = %s
881  AND sahs_lm.id = %s';
882 
883  break;
884 
885  }
886 
887  $types = array('integer', 'integer');
888  $values = array($userId, $packageId);
889  $res = $ilDB->queryF($q, $types, $values);
890 
891  $result['data'][$k] = array();
892 
893  while($row = $ilDB->fetchAssoc($res))
894  {
895  $tmp_result = array();
896  foreach($row as $key => $value)
897  {
898  $tmp_result[] = $value;
899  }
900  $result['data'][$k][] = $tmp_result;
901  }
902  }
903  return $result;
904  }
905 
906  private function removeCMIData($userId, $packageId, $cp_node_id=null)
907  {
908  global $ilDB, $ilLog;
909 
910 //$ilLog->write("Remove CMI Data");
911 
912  $delorder = array('correct_response', 'objective', 'interaction', 'comment', 'node');
913  //error_log("Delete, User:".$userId."Package".$packageId."Node: ".$cp_node_id);
914  foreach($delorder as $k)
915  {
916  if(is_null($cp_node_id))
917  {
918  switch($k)
919  {
920  case "response":
921  $q = 'DELETE FROM
922  cmi_correct_response WHERE cmi_interaction_id IN (
923  SELECT cmi_interaction.cmi_interaction_id FROM cmi_interaction
924  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
925  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
926  WHERE cmi_node.user_id = %s
927  AND cp_node.slm_id = %s)';
928  break;
929 
930  case "interaction":
931  $q = 'DELETE FROM cmi_interaction
932  WHERE cmi_node_id IN (
933  SELECT cmi_node.cmi_node_id FROM cmi_node
934  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
935  WHERE cmi_node.user_id = %s
936  AND cp_node.slm_id = %s)';
937  break;
938 
939  case "comment":
940  $q = 'DELETE FROM cmi_comment
941  WHERE cmi_node_id IN (
942  SELECT cmi_node.cmi_node_id FROM cmi_node
943  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
944  WHERE cmi_node.user_id = %s
945  AND cp_node.slm_id = %s)';
946  break;
947 
948  case "objective":
949  $q = 'DELETE FROM cmi_objective
950  WHERE cmi_node_id IN (
951  SELECT cmi_node.cmi_node_id FROM cmi_node
952  INNER JOIN cp_node ON cmi_node.cp_node_id = cp_node.cp_node_id
953  WHERE cmi_node.user_id = %s
954  AND cp_node.slm_id = %s)';
955  break;
956 
957  case "node":
958  $q = 'DELETE FROM cmi_node
959  WHERE user_id = %s AND cp_node_id IN (
960  SELECT cp_node_id FROM cp_node
961  WHERE slm_id = %s)';
962  break;
963  }
964 
965  $types = array('integer', 'integer');
966  $values = array($userId, $packageId);
967  $ilDB->manipulateF($q, $types, $values);
968  }
969  else
970  {
971  switch($k)
972  {
973  case "correct_response":
974  $q = 'DELETE FROM cmi_correct_response
975  WHERE cmi_interaction_id IN (
976  SELECT cmi_interaction.cmi_interaction_id FROM cmi_interaction
977  INNER JOIN cmi_node ON cmi_node.cmi_node_id = cmi_interaction.cmi_node_id
978  WHERE cmi_node.cp_node_id = %s
979  AND cmi_node.user_id = %s)';
980  break;
981 
982  case "interaction":
983  $q = 'DELETE FROM cmi_interaction
984  WHERE cmi_node_id IN (
985  SELECT cmi_node.cmi_node_id FROM cmi_node
986  WHERE cmi_node.cp_node_id = %s
987  AND cmi_node.user_id = %s)';
988  break;
989 
990  case "comment":
991  $q = 'DELETE FROM cmi_comment
992  WHERE cmi_node_id IN (
993  SELECT cmi_node.cmi_node_id FROM cmi_node
994  WHERE cmi_node.cp_node_id = %s
995  AND cmi_node.user_id = %s)';
996  break;
997 
998  case "objective":
999  $q = 'DELETE FROM cmi_objective
1000  WHERE cmi_node_id IN (
1001  SELECT cmi_node.cmi_node_id FROM cmi_node
1002  WHERE cmi_node.cp_node_id = %s
1003  AND cmi_node.user_id = %s)';
1004  break;
1005 
1006  case "node":
1007  $q = 'DELETE FROM cmi_node WHERE cp_node_id = %s
1008  AND cmi_node.user_id = %s';
1009  break;
1010  }
1011 
1012  $types = array('integer', 'integer');
1013  $values = array($cp_node_id, $userId);
1014  $ilDB->manipulateF($q, $types, $values);
1015  }
1016  }
1017 
1018  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
1019  ilLPStatusWrapper::_updateStatus($packageId, $userId);
1020 
1021  }
1022 
1023  private function setCMIData($userId, $packageId, $data, $a_ref_id)
1024  {
1025  global $ilDB, $ilLog;
1026 
1027  $result = array();
1028  $map = array();
1029 
1030 //$ilLog->write("Set CMI Data");
1031 
1032  if (!$data) return;
1033 
1034  $tables = array('node', 'comment', 'interaction', 'objective', 'correct_response');
1035 
1036  foreach($tables as $table)
1037  {
1038  $schem = & self::$schema[$table];
1039 // $ilLog->write("SCORM: setCMIData, table -".$table."-".$data->objective);
1040 
1041  if (!is_array($data->$table)) continue;
1042 
1043 //$ilLog->write("SCORM: setCMIData, table -".$table."-");
1044 
1045  // build up numerical index for schema fields
1046  $i = 0;
1047  foreach($schem as &$field)
1048  {
1049  $field['no'] = $i++;
1050  }
1051  // now iterate through data rows from input
1052  foreach($data->$table as &$row)
1053  {
1054  // first fill some fields that could not be set from client side
1055  // namely the database id's depending on which table is processed
1056  switch ($table)
1057  {
1058  case 'correct_response':
1059  $no = $schem['cmi_interaction_id']['no'];
1060  $ilLog->write("correct_response no: ".$no);
1061  $ilLog->write("The Row: ".count($row));
1062  $row[$no] = $map['interaction'][$row[$no]];
1063  $ilLog->write("Value: ".print_r($map['interaction'],true));
1064  case 'comment':
1065  case 'interaction':
1066  $no = $schem['cmi_node_id']['no'];
1067  $row[$no] = $map['node'][$row[$no]];
1068  break;
1069  case 'objective':
1070  $no = $schem['cmi_interaction_id']['no'];
1071  $row[$no] = $map['interaction'][$row[$no]];
1072  $no = $schem['cmi_node_id']['no'];
1073  $row[$no] = $map['node'][$row[$no]];
1074  break;
1075  case 'node':
1076  $no = $schem['user_id']['no'];
1077  $row[$no] = $userId;
1078  break;
1079 
1080  }
1081 
1082 //$ilLog->write("SCORM: setCMIData, row b");
1083  $cp_no = $schem['cp_' . $table . '_id']['no'];
1084  $cmi_no = $schem['cmi_' . $table . '_id']['no'];
1085 
1086  // get current id for later use
1087  // this is either a real db id or document unique string generated by client
1088  $cmi_id = $row[$cmi_no];
1089 
1090  // set if field to null, so it will be filled up by autoincrement
1091  $row[$cmi_no] = null;
1092 
1093  $keys = array();
1094  foreach(array_keys($schem) as $key)
1095  {
1096  $keys[] = $key;
1097  }
1098 //$ilLog->write("SCORM: setCMIData, row c");
1099  if($table === 'node')
1100  {
1101  $this->removeCMIData($userId, $packageId, $row[$cp_no]);
1102  }
1103 
1104  $ret = false;
1105 
1106  $ilLog->write("Checking table: ".$table);
1107 
1108  switch($table)
1109  {
1110  case 'correct_response':
1111  $row[$cmi_no] = $ilDB->nextId('cmi_correct_response');
1112  // Alex: 11 Nov 2010: During investigation of bug
1113  // 6799 I realised that the $row variable contains
1114  // sometimes only one value or up to four values
1115  // this makes the manipulateF fail.
1116  // I added the following if statement, but
1117  // the cause of the problem needs further investigation
1118  if (count($row) == 3)
1119  {
1120  $ilDB->manipulateF('
1121  INSERT INTO cmi_correct_response
1122  (cmi_correct_resp_id, cmi_interaction_id, pattern)
1123  VALUES (%s, %s, %s)',
1124  array('integer', 'integer', 'text'),
1125  $row
1126  );
1127  }
1128  else
1129  {
1130  $ilLog->write("ERROR SCORM Player, setCMIData, incorrect number of values for cmi_correct_response.");
1131  $ilLog->dump($row);
1132  }
1133  break;
1134 
1135  case 'comment':
1136  $row[$cmi_no] = $ilDB->nextId('cmi_comment');
1137 
1138  $ilDB->insert('cmi_comment', array(
1139  'cmi_comment_id' => array('integer', $row[$cmi_no]),
1140  'cmi_node_id' => array('integer', $row[1]),
1141  'c_comment' => array('clob', $row[2]),
1142  'c_timestamp' => array('timestamp', $row[3]),
1143  'location' => array('text', $row[4]),
1144  'sourceislms' => array('integer', $row[5])
1145  ));
1146  break;
1147 
1148  case 'interaction':
1149  $row[$cmi_no] = $ilDB->nextId('cmi_interaction');
1150 
1151  $ilDB->insert('cmi_interaction', array(
1152  'cmi_interaction_id' => array('integer', $row[$cmi_no]),
1153  'cmi_node_id' => array('integer', $row[1]),
1154  'description' => array('clob', $row[2]),
1155  'id' => array('text', $row[3]),
1156  'latency' => array('text', $row[4]),
1157  'learner_response' => array('clob', $row[5]),
1158  'result' => array('text', $row[6]),
1159  'c_timestamp' => array('text', $row[7]),
1160  'c_type' => array('text', $row[8]),
1161  'weighting' => array('float', $row[9])
1162  ));
1163  break;
1164 
1165  case 'objective':
1166  $row[$cmi_no] = $ilDB->nextId('cmi_objective');
1167 
1168  $ilDB->insert('cmi_objective', array(
1169  'cmi_interaction_id' => array('integer', $row[0]),
1170  'cmi_node_id' => array('integer', $row[1]),
1171  'cmi_objective_id' => array('integer', $row[$cmi_no]),
1172  'completion_status' => array('float', $row[3]),
1173  'description' => array('clob', $row[4]),
1174  'id' => array('text', $row[5]),
1175  'c_max' => array('float', $row[6]),
1176  'c_min' => array('float', $row[7]),
1177  'c_raw' => array('float', $row[8]),
1178  'scaled' => array('float', $row[9]),
1179  'progress_measure' => array('float', $row[10]),
1180  'success_status' => array('text', $row[11]),
1181  'scope' => array('text', $row[12])
1182  ));
1183  break;
1184 
1185  case 'node':
1186  $row[$cmi_no] = $ilDB->nextId('cmi_node');
1187 
1188  $node_fields = array(
1189  'accesscount', 'accessduration', 'accessed', 'activityabsduration', 'activityattemptcount',
1190  'activityexpduration', 'activityprogstatus', 'attemptabsduration', 'attemptcomplamount', 'attemptcomplstatus',
1191  'attemptexpduration', 'attemptprogstatus', 'audio_captioning', 'audio_level', 'availablechildren',
1192  'cmi_node_id', 'completion', 'completion_status', 'completion_threshold', 'cp_node_id',
1193  'created', 'credit', 'delivery_speed', 'c_entry', 'c_exit',
1194  'c_language', 'launch_data', 'learner_name', 'location', 'c_max',
1195  'c_min', 'c_mode', 'modified', 'progress_measure', 'c_raw',
1196  'scaled', 'scaled_passing_score', 'session_time', 'success_status', 'suspend_data',
1197  'total_time', 'user_id', 'c_timestamp'
1198  );
1199 
1200  $node_types = array(
1201  'integer', 'text', 'text', 'text', 'integer', 'text', 'integer', 'text', 'float', 'integer',
1202  'text', 'integer', 'integer', 'float', 'text', 'integer', 'float', 'text', 'text', 'integer',
1203  'text', 'text', 'float', 'text', 'text', 'text', 'clob', 'text', 'text', 'float',
1204  'float', 'text', 'text', 'float', 'float', 'float', 'float', 'text', 'text', 'clob',
1205  'text', 'integer', 'timestamp'
1206  );
1207 
1208  $node_data = array();
1209  foreach($node_fields as $key => $node_field)
1210  {
1211  if($key == 15)
1212  $value = $row[$cmi_no];
1213  else if($key == 42)
1214  $value = date('Y-m-d H:i:s');
1215  else
1216  $value = $row[$key];
1217  $node_data[$node_field] = array($node_types[$key], $value);
1218  }
1219 
1220  $ilLog->write("Want to insert row: ".count($row) );
1221  $ilDB->insert('cmi_node', $node_data);
1222  break;
1223  }
1224 
1225  $ret = true;
1226 
1227  if(!$ret)
1228  {
1229  $return = false;
1230  break;
1231  }
1232 
1233  // if we process a node save new id into result object that will be feedback for client
1234  if($table === 'node')
1235  {
1236  $result[(string)$row[$cp_no]] = $row[$cmi_no];
1237  }
1238 
1239  // add new id to mapping table for later use on dependend elements
1240  $map[$table][$cmi_id] = $row[$cmi_no];
1241  }
1242  }
1243 
1244 //$ilLog->write("-synching-");
1245 
1246  // sync access number and time in read event table
1247  include_once("./Modules/Scorm2004/classes/class.ilSCORM2004Tracking.php");
1248  ilSCORM2004Tracking::_syncReadEvent($packageId, $userId, "sahs", $a_ref_id);
1249 
1250  // update learning progress status
1251  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
1252  ilLPStatusWrapper::_updateStatus($packageId, $userId);
1253 
1254  return $result;
1255  }
1256 
1257  function quoteJSONArray($a_array)
1258  {
1259  global $ilDB;
1260 
1261  if(!is_array($a_array) or !count($a_array))
1262  {
1263  return array("''");
1264  }
1265 
1266  foreach($a_array as $k => $item)
1267  {
1268  if ($item != null) {
1269  $a_array[$k] = $ilDB->quote($item);
1270  } else {
1271  $a_array[$k] = "NULL";
1272  }
1273  }
1274 
1275  return $a_array;
1276  }
1277 
1285  public function getMimetype($filename)
1286  {
1287  $mimetypes = array();
1288  require_once('classes/mimemap.php');
1289  $info = pathinfo($filename);
1290  $ext = $mimetypes[$info['extension']];
1291  return $ext ? $ext : mime_content_type($filename);
1292  }
1293 
1299  public function getCookie()
1300  {
1301  return unserialize(base64_decode($_COOKIE[IL_OP_COOKIE_NAME]));
1302  }
1303 
1304  public function setCookie($cook)
1305  {
1306  setCookie(IL_OP_COOKIE_NAME, base64_encode(serialize($cook)));
1307  }
1308 
1315  public function readFile($path)
1316  {
1317  if (headers_sent())
1318  {
1319  die('Error: Cookie could not be established');
1320  }
1321 
1322  $SAHS_LM_POSITION = 1; // index position of sahs_lm id in splitted path_info
1323 
1324  $comp = explode('/', (string) $path);
1325  $sahs = $comp[$SAHS_LM_POSITION];
1326  $cook = $this->getCookie();
1327  $perm = $cook[$sahs];
1328 
1329  if (!$perm)
1330  {
1331  // check login an package access
1332  // TODO add rbac check function here
1333  $perm = 1;
1334  if (!$perm)
1335  {
1336  header('HTTP/1.0 401 Unauthorized');
1337  die('/* Unauthorized */');
1338  }
1339  // write cookie
1340  $cook[$sahs] = $perm;
1341  $this->setCookie($cook);
1342  }
1343 
1344  $path = '.' . $path;
1345  if (!is_file($path))
1346  {
1347  header('HTTP/1.0 404 Not Found');
1348  die('/* Not Found ' . $path . '*/');
1349  }
1350 
1351  // send mimetype to client
1352  header('Content-Type: ' . $this->getMimetype($path));
1353 
1354  // let page be cached in browser for session duration
1355  header('Expires: ' . gmdate('D, d M Y H:i:s', time() + session_cache_expire()*60) . ' GMT');
1356  header('Cache-Control: private');
1357 
1358  // now show it to the user and be fine
1359  readfile($path);
1360  die();
1361  }
1362 
1366  function get_max_attempts()
1367  {
1368  global $ilDB;
1369 
1370  $res = $ilDB->queryF(
1371  'SELECT max_attempt FROM sahs_lm WHERE id = %s',
1372  array('integer'),
1373  array($this->packageId)
1374  );
1375  $row = $ilDB->fetchAssoc($res);
1376 
1377  return $row['max_attempt'];
1378  }
1379 
1380  function get_module_version()
1381  {
1382  global $ilDB;
1383 
1384  $res = $ilDB->queryF(
1385  'SELECT module_version FROM sahs_lm WHERE id = %s',
1386  array('integer'),
1387  array($this->packageId));
1388  $row = $ilDB->fetchAssoc($res);
1389 
1390  return $row['module_version'];
1391  }
1392 
1396  function get_actual_attempts()
1397  {
1398  global $ilDB, $ilUser;
1399 
1400  $res = $ilDB->queryF('
1401  SELECT rvalue FROM cmi_custom
1402  WHERE user_id = %s AND sco_id = %s
1403  AND lvalue = %s AND obj_id = %s',
1404  array('integer', 'integer', 'text', 'integer'),
1405  array($this->userId, 0, 'package_attempts', $this->packageId)
1406  );
1407  $row = $ilDB->fetchAssoc($res);
1408 
1409  $row['rvalue'] = str_replace("\r\n", "\n", $row['rvalue']);
1410  if($row['rvalue'] == null)
1411  {
1412  $row['rvalue'] = 0;
1413  }
1414  return $row['rvalue'];
1415  }
1416 
1420  function increase_attempt()
1421  {
1422  global $ilDB, $ilUser;
1423 
1424  //get existing account - sco id is always 0
1425  $res = $ilDB->queryF('
1426  SELECT rvalue FROM cmi_custom
1427  WHERE user_id = %s
1428  AND sco_id = %s
1429  AND lvalue = %s
1430  AND obj_id = %s',
1431  array('integer', 'integer','text', 'integer'),
1432  array($this->userId, 0, 'package_attempts', $this->packageId)
1433  );
1434  $row = $ilDB->fetchAssoc($res);
1435 
1436  $tmp_row = $row;
1437 
1438  $row['rvalue'] = str_replace("\r\n", "\n", $row['rvalue']);
1439  if($row['rvalue'] == null)
1440  {
1441  $row['rvalue'] = 0;
1442  }
1443  $new_rec = $row['rvalue'] + 1;
1444 
1445  //increase attempt by 1
1446  if(!is_array($tmp_row) || !count($tmp_row))
1447  {
1448  $ilDB->manipulateF('
1449  INSERT INTO cmi_custom (rvalue, user_id, sco_id, obj_id, lvalue, c_timestamp)
1450  VALUES(%s, %s, %s, %s, %s, %s)',
1451  array('text', 'integer', 'integer', 'integer', 'text', 'timestamp'),
1452  array($new_rec, $this->userId, 0, $this->packageId, 'package_attempts', date('Y-m-d H:i:s'))
1453  );
1454  }
1455  else
1456  {
1457  $ilDB->manipulateF('
1458  UPDATE cmi_custom
1459  SET rvalue = %s,
1460  c_timestamp = %s
1461  WHERE user_id = %s
1462  AND sco_id = %s
1463  AND obj_id = %s
1464  AND lvalue = %s',
1465  array('text', 'timestamp', 'integer', 'integer', 'integer','text'),
1466  array($new_rec, date('Y-m-d H:i:s'), $this->userId, 0, $this->packageId, 'package_attempts')
1467  );
1468  }
1469  }
1470 
1474  function save_module_version()
1475  {
1476  global $ilDB, $ilUser;
1477 
1478  $res = $ilDB->queryF('
1479  SELECT * FROM cmi_custom
1480  WHERE user_id = %s
1481  AND sco_id = %s
1482  AND lvalue = %s
1483  AND obj_id = %s',
1484  array('integer', 'integer', 'text', 'integer'),
1485  array($this->userId, 0, 'module_version', $this->packageId)
1486  );
1487  if(!$ilDB->numRows($res))
1488  {
1489  $ilDB->manipulateF('
1490  INSERT INTO cmi_custom (rvalue, user_id, sco_id, obj_id, lvalue, c_timestamp)
1491  VALUES(%s, %s, %s, %s, %s, %s)',
1492  array('text', 'integer', 'integer', 'integer', 'text', 'timestamp'),
1493  array($this->get_Module_Version(), $this->userId, 0, $this->packageId, 'module_version', date('Y-m-d H:i:s'))
1494  );
1495  }
1496  else
1497  {
1498  $ilDB->manipulateF('
1499  UPDATE cmi_custom
1500  SET rvalue = %s,
1501  c_timestamp = %s
1502  WHERE user_id = %s
1503  AND sco_id = %s
1504  AND obj_id = %s
1505  AND lvalue = %s',
1506  array('text', 'timestamp', 'integer', 'integer', 'integer', 'text'),
1507  array($this->get_Module_Version(), date('Y-m-d H:i:s'), $this->userId, 0, $this->packageId, 'module_version')
1508  );
1509  }
1510  }
1511 }
1512 ?>