ILIAS  release_4-4 Revision
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilSCORM2004StoreData.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 
13 {
14 
15  public function scormPlayerUnload($userId=null, $packageId)
16  {
17  global $ilDB;
18 
19  $data = json_decode(is_string($data) ? $data : file_get_contents('php://input'));
20  if (!$data) return;
21  if ($userId == null) {
22  $userId=(int) $data->p;
23  self::checkIfAllowed($packageId,$userId,$data->hash);
24  }
25  $last_visited = "";
26  if ($data->last !="") $last_visited = $data->last;
27  $endDate = date('Y-m-d H:i:s', mktime(date('H'), date('i')+5, date('s'), date('m'), date('d'), date('Y')));
28  $ilDB->manipulateF('UPDATE sahs_user
29  SET last_visited = %s, hash_end =%s, last_access = %s
30  WHERE obj_id = %s AND user_id = %s',
31  array('text', 'timestamp', 'timestamp', 'integer', 'integer'),
32  array($last_visited, $endDate, date('Y-m-d H:i:s'), $packageId, $userId)
33  );
34 
35  header('Content-Type: text/plain; charset=UTF-8');
36  print("");
37  }
38 
39 
40  public function persistCMIData($userId=null, $packageId, $defaultLessonMode, $comments, $interactions, $objectives, $data = null)
41  {
42  global $ilLog;
43 
44  if ($defaultLessonMode == "browse") {return;}
45 
46  $jsMode = strpos($_SERVER['HTTP_ACCEPT'], 'text/javascript')!==false;
47 
48  $data = json_decode(is_string($data) ? $data : file_get_contents('php://input'));
49  $ilLog->write("dataTo_setCMIData: ".file_get_contents('php://input'));
50  if (!$data) return;
51  if ($userId == null) {
52  $userId=(int) $data->p;
53  self::checkIfAllowed($packageId,$userId,$data->hash);
54 // header('Access-Control-Allow-Origin: http://localhost:50012');//just for tests - not for release UK
55  }
56  $return = array();
58  $userId,
59  $packageId,
60  $data,
61  $comments,
62  $interactions,
63  $objectives
64  );
65 
66  //$new_global_status=ilSCORM2004StoreData::setGlobalObjectivesAndGetGlobalStatus($userId, $packageId, $data);
68  $new_global_status = $data->now_global_status;
69  $return["new_global_status"] = $new_global_status;
70 
71  ilSCORM2004StoreData::syncGlobalStatus($userId, $packageId, $data, $new_global_status);
72 
73  $ilLog->write("SCORM: return of persistCMIData: ".json_encode($return));
74  if ($jsMode)
75  {
76  header('Content-Type: text/javascript; charset=UTF-8');
77  print(json_encode($return));
78  }
79  else
80  {
81  header('Content-Type: text/html; charset=UTF-8');
82  print(var_export($return, true));
83  }
84  }
85 
86  function checkIfAllowed($packageId,$userId,$hash){
87  global $ilDB;
88  $res = $ilDB->queryF('select hash from sahs_user where obj_id=%s AND user_id=%s AND hash_end>%s',
89  array('integer','integer','timestamp'),
90  array($packageId,$userId,date('Y-m-d H:i:s'))
91  );
92  $rowtmp=$ilDB->fetchAssoc($res);
93  if ($rowtmp['hash']==$hash) return;
94  else die("not allowed");
95  }
96 
97  public function setCMIData($userId, $packageId, $data,$getComments,$getInteractions,$getObjectives) {
98  global $ilDB, $ilLog;
99 
100  $result = array();
101 
102  if (!$data) return $result;
103 
104  $i_check=$data->i_check;
105  $i_set=$data->i_set;
106  $b_node_update=false;
107  $cmi_node_id=null;
108  $a_map_cmi_interaction_id=array();
109 
110  $tables = array('node', 'comment', 'interaction', 'objective', 'correct_response');
111 
112  foreach($tables as $table)
113  {
114  if (!is_array($data->$table)) continue;
115 
116  $ilLog->write("SCORM: setCMIData, table -".$table."-");
117 
118  // now iterate through data rows from input
119  foreach($data->$table as &$row)
120  {
121  $ilLog->write("Checking table: ".$table);
122 
123  switch($table)
124  {
125  case 'node': //is always first and has only 1 row
126 
127  $res = $ilDB->queryF(
128  'SELECT cmi_node_id FROM cmi_node WHERE cp_node_id = %s and user_id = %s',
129  array('integer','integer'),
130  array($row[19],$userId)
131  );
132  $rowtmp=$ilDB->fetchAssoc($res);
133  $cmi_node_id=$rowtmp['cmi_node_id'];
134  if ($cmi_node_id!=null) $b_node_update=true;
135  else {
136  $cmi_node_id = $ilDB->nextId('cmi_node');
137  $b_node_update=false;
138  }
139  $ilLog->write("setCMIdata with cmi_node_id = ".$cmi_node_id);
140  $a_data=array(
141  'accesscount' => array('integer', $row[0]),
142  'accessduration' => array('text', $row[1]),
143  'accessed' => array('text', $row[2]),
144  'activityabsduration' => array('text', $row[3]),
145  'activityattemptcount' => array('integer', $row[4]),
146  'activityexpduration' => array('text', $row[5]),
147  'activityprogstatus' => array('integer', $row[6]),
148  'attemptabsduration' => array('text', $row[7]),
149  'attemptcomplamount' => array('float', $row[8]),
150  'attemptcomplstatus' => array('integer', $row[9]),
151  'attemptexpduration' => array('text', $row[10]),
152  'attemptprogstatus' => array('integer', $row[11]),
153  'audio_captioning' => array('integer', $row[12]),
154  'audio_level' => array('float', $row[13]),
155  'availablechildren' => array('text', $row[14]),
156  'cmi_node_id' => array('integer', $cmi_node_id),
157  'completion' => array('float', $row[16]),
158  'completion_status' => array('text', $row[17]),
159  'completion_threshold' => array('text', $row[18]),
160  'cp_node_id' => array('integer', $row[19]),
161  'created' => array('text', $row[20]),
162  'credit' => array('text', $row[21]),
163  'delivery_speed' => array('float', $row[22]),
164  'c_entry' => array('text', $row[23]),
165  'c_exit' => array('text', $row[24]),
166  'c_language' => array('text', $row[25]),
167  'launch_data' => array('clob', $row[26]),
168  'learner_name' => array('text', $row[27]),
169  'location' => array('text', $row[28]),
170  'c_max' => array('float', $row[29]),
171  'c_min' => array('float', $row[30]),
172  'c_mode' => array('text', $row[31]),
173  'modified' => array('text', $row[32]),
174  'progress_measure' => array('float', $row[33]),
175  'c_raw' => array('float', $row[34]),
176  'scaled' => array('float', $row[35]),
177  'scaled_passing_score' => array('float', $row[36]),
178  'session_time' => array('text', $row[37]),
179  'success_status' => array('text', $row[38]),
180  'suspend_data' => array('clob', $row[39]),
181  'total_time' => array('text', $row[40]),
182  'user_id' => array('integer', $userId),
183  'c_timestamp' => array('timestamp', date('Y-m-d H:i:s')),
184  'additional_tables' => array('integer', $i_check)
185  );
186 
187  if($b_node_update==false) {
188  $ilLog->write("Want to insert row: ".count($row) );
189  $ilDB->insert('cmi_node', $a_data);
190  } else {
191  $ilDB->update('cmi_node', $a_data, array('cmi_node_id' => array('integer', $cmi_node_id)));
192  $ilLog->write("updated");
193  }
194 
195  if($b_node_update==true) {
196  //remove
197  if ($i_set>7) {
198  $i_set-=8;
199  if ($getComments) {
200  $q = 'DELETE FROM cmi_comment WHERE cmi_node_id = %s';
201  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
202  }
203  }
204  if ($i_set>3) {
205  $i_set-=4;
206  if ($getInteractions) {
207  $q = 'DELETE FROM cmi_correct_response
208  WHERE cmi_interaction_id IN (
209  SELECT cmi_interaction.cmi_interaction_id FROM cmi_interaction WHERE cmi_interaction.cmi_node_id = %s)';
210  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
211  }
212  }
213  if ($i_set>1) {
214  $i_set-=2;
215  if ($getInteractions) {
216  $q = 'DELETE FROM cmi_interaction WHERE cmi_node_id = %s';
217  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
218  }
219  }
220  if ($i_set>0) {
221  $i_set=0;
222  if ($getObjectives) {
223  $q = 'DELETE FROM cmi_objective WHERE cmi_node_id = %s';
224  $ilDB->manipulateF($q, array('integer'), array($cmi_node_id));
225  }
226  }
227  //end remove
228  }
229  //to send to client
230  $result[(string)$row[19]] = $cmi_node_id;
231  break;
232 
233  case 'comment':
234  $row[0] = $ilDB->nextId('cmi_comment');
235 
236  $ilDB->insert('cmi_comment', array(
237  'cmi_comment_id' => array('integer', $row[0]),
238  'cmi_node_id' => array('integer', $cmi_node_id),
239  'c_comment' => array('clob', $row[2]),
240  'c_timestamp' => array('text', $row[3]),
241  'location' => array('text', $row[4]),
242  'sourceislms' => array('integer', $row[5])
243  ));
244  break;
245 
246  case 'interaction':
247  $cmi_interaction_id = $ilDB->nextId('cmi_interaction');
248  $a_map_cmi_interaction_id[]=array($row[0],$cmi_interaction_id);
249  $ilDB->insert('cmi_interaction', array(
250  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
251  'cmi_node_id' => array('integer', $cmi_node_id),
252  'description' => array('clob', $row[2]),
253  'id' => array('text', $row[3]),
254  'latency' => array('text', $row[4]),
255  'learner_response' => array('clob', $row[5]),
256  'result' => array('text', $row[6]),
257  'c_timestamp' => array('text', $row[7]),
258  'c_type' => array('text', $row[8]),
259  'weighting' => array('float', $row[9])
260  ));
261  break;
262 
263  case 'objective':
264  $row[2] = $ilDB->nextId('cmi_objective');
265  $cmi_interaction_id = null;
266  if ($row[0] != null) {
267  for($i=0;$i<count($a_map_cmi_interaction_id);$i++)
268  if ($row[0] == $a_map_cmi_interaction_id[$i][0]) $cmi_interaction_id=$a_map_cmi_interaction_id[$i][1];
269  }
270  $ilDB->insert('cmi_objective', array(
271  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
272  'cmi_node_id' => array('integer', $cmi_node_id),
273  'cmi_objective_id' => array('integer', $row[2]),
274  'completion_status' => array('text', $row[3]),
275  'description' => array('clob', $row[4]),
276  'id' => array('text', $row[5]),
277  'c_max' => array('float', $row[6]),
278  'c_min' => array('float', $row[7]),
279  'c_raw' => array('float', $row[8]),
280  'scaled' => array('float', $row[9]),
281  'progress_measure' => array('float', $row[10]),
282  'success_status' => array('text', $row[11]),
283  'scope' => array('text', $row[12])
284  ));
285  break;
286 
287  case 'correct_response':
288  $cmi_interaction_id = null;
289  if ($row[1] !== null) {
290  for($i=0;$i<count($a_map_cmi_interaction_id);$i++)
291  if ($row[1] == $a_map_cmi_interaction_id[$i][0]) $cmi_interaction_id=$a_map_cmi_interaction_id[$i][1];
292  $row[0] = $ilDB->nextId('cmi_correct_response');
293  $ilDB->insert('cmi_correct_response', array(
294  'cmi_correct_resp_id' => array('integer', $row[0]),
295  'cmi_interaction_id' => array('integer', $cmi_interaction_id),
296  'pattern' => array('text', $row[2])
297  ));
298  }
299  break;
300  }
301  }
302  }
303  return $result;
304  }
305 
306 
307  // private function setGlobalObjectivesAndGetGlobalStatus($userId, $packageId, $data) {
308 
309  // global $ilLog;
310  // $changed_seq_utilities=$data->changed_seq_utilities;
311  // $ilLog->write("SCORM2004 adl_seq_utilities changed: ".$changed_seq_utilities);
312  // if ($changed_seq_utilities == 1) {
313  // $returnAr=ilSCORM2004StoreData::writeGObjective($data->adl_seq_utilities, $userId, $packageId);
314  // }
315  // // $completed=$returnAr[0];
316  // // $satisfied=$returnAr[1];
317  // // $measure=$returnAr[2];
318  // self::ensureObjectDataCacheExistence();
319 
320  // $lp_mode=$data->lp_mode;
321  // if ($lp_mode=="12") //12=scorm_package
322  // {
323  // include_once './Services/Tracking/classes/status/class.ilLPStatusSCORMPackage.php';
324  // $new_global_status=ilLPStatusSCORMPackage::determineStatus($packageId, $userId);
325  // }
326  // else $new_global_status = $data->now_global_status; //6=selected scos, 0=no tracking
327  // $ilLog->write("new_global_status=".$new_global_status);
328  // return $new_global_status;
329  // }
330 
331 
332  private function setGlobalObjectives($userId, $packageId, $data) {
333  global $ilLog;
334  $changed_seq_utilities=$data->changed_seq_utilities;
335  $ilLog->write("SCORM2004 adl_seq_utilities changed: ".$changed_seq_utilities);
336  if ($changed_seq_utilities == 1) {
337  $returnAr=ilSCORM2004StoreData::writeGObjective($data->adl_seq_utilities, $userId, $packageId);
338  }
339  }
340 
341  public function syncGlobalStatus($userId, $packageId, $data, $new_global_status) {
342 
343  global $ilDB, $ilLog;
344  $saved_global_status=$data->saved_global_status;
345  $ilLog->write("saved_global_status=".$saved_global_status);
346 
347  // sync access number and time in read event table
348  //include_once("./Modules/Scorm2004/classes/class.ilSCORM2004Tracking.php");
349  //ilSCORM2004Tracking::_syncReadEvent($packageId, $userId, "sahs", $a_ref_id);
350 
351  // get attempts
352  if (!$data->packageAttempts) {
353  $val_set = $ilDB->queryF('SELECT package_attempts FROM sahs_user WHERE obj_id = %s AND user_id = %s',
354  array('integer','integer'), array($packageId,$userId));
355  $val_rec = $ilDB->fetchAssoc($val_set);
356  $attempts = $val_rec["package_attempts"];
357  } else {
358  $attempts=$data->packageAttempts;
359  }
360  if ($attempts == null) $attempts = "";
361 
362  //update percentage_completed, sco_total_time_sec,status in sahs_user
363  $totalTime=(int)$data->totalTimeCentisec;
364  $totalTime=round($totalTime/100);
365  $ilDB->queryF('UPDATE sahs_user SET sco_total_time_sec=%s, status=%s, percentage_completed=%s, package_attempts=%s WHERE obj_id = %s AND user_id = %s',
366  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
367  array($totalTime, $new_global_status, $data->percentageCompleted, $attempts, $packageId, $userId));
368 
369  self::ensureObjectDataCacheExistence();
370  global $ilObjDataCache;
371  include_once("./Services/Tracking/classes/class.ilChangeEvent.php");
372  ilChangeEvent::_recordReadEvent("sahs", (int)$_GET['ref_id'],$packageId, $userId, false, $attempts, $totalTime);
373 
374  //end sync access number and time in read event table
375 
376  // update learning progress
377  if ($new_global_status != null) {//could only happen when synchronising from SCORM Offline Player
378 // include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
379 // ilLPStatusWrapper::_updateStatus($packageId, $userId);
380  include_once("./Services/Tracking/classes/class.ilObjUserTracking.php");
381  include_once("./Services/Tracking/classes/class.ilLPStatus.php");
382  ilLPStatus::writeStatus($packageId, $userId,$new_global_status,$data->percentageCompleted);
383 
384 // here put code for soap to MaxCMS e.g. when if($saved_global_status != $new_global_status)
385  }
386  return true;
387  }
388 
389 
390  //saves global_objectives to database
391  //$dowrite only if changed adl_seq_utilities
392  public function writeGObjective($g_data, $user, $package)
393  {
394  global $ilDB, $ilLog;
395  $ilLog->write("SCORM2004 writeGObjective");
396 
397  $returnAr=array(null,null,null);
398 
399  //iterate over assoziative array
400  if($g_data == null)
401  return $returnAr;
402 
403  $rows_to_insert = Array();
404 
405  foreach($g_data as $key => $value)
406  {
407  $ilLog->write("SCORM2004 writeGObjective -key: ".$key);
408  //objective
409  //learner = ilias learner id
410  //scope = null / course
411  foreach($value as $skey => $svalue)
412  {
413  $ilLog->write("SCORM2004 writeGObjective -skey: ".$skey);
414  //we always have objective and learner id
415  if($g_data->$key->$skey->$user->$package)
416  {
417  $o_value = $g_data->$key->$skey->$user->$package;
418  $scope = $package;
419  }
420  else //UK: is this okay? can $scope=0 and $user->{"null"}; when is $scope used?
421 
422  {
423  //scope 0
424  $o_value = $g_data->$key->$skey->$user->{"null"};
425  //has to be converted to NULL in JS Later
426  $scope = 0;
427  }
428 
429  //insert into database
430  $objective_id = $skey;
431  $toset = $o_value;
432  $dbuser = $user;
433 
434  if($key == "status")
435  {
436 
437  //special handling for status
438  $completed = $g_data->$key->$skey->$user->{completed};
439  $measure = $g_data->$key->$skey->$user->{measure};
440  $satisfied = $g_data->$key->$skey->$user->{satisfied};
441 
442  $returnAr=array($completed, $satisfied, $measure);
443 
444  $obj = '-course_overall_status-';
445  $pkg_id = $package;
446 
447  $res = $ilDB->queryF('
448  SELECT user_id FROM cmi_gobjective
449  WHERE objective_id =%s
450  AND user_id = %s
451  AND scope_id = %s',
452  array('text', 'integer', 'integer'),
453  array($obj, $dbuser, $pkg_id)
454  );
455  $ilLog->write("SCORM2004 Count is: ".$ilDB->numRows($res));
456  if(!$ilDB->numRows($res))
457  {
458  $ilDB->manipulateF('
459  INSERT INTO cmi_gobjective
460  (user_id, status, scope_id, measure, satisfied, objective_id)
461  VALUES (%s, %s, %s, %s, %s, %s)',
462  array('integer', 'text', 'integer', 'text', 'text', 'text'),
463  array($dbuser, $completed, $pkg_id, $measure, $satisfied, $obj)
464  );
465  $ilLog->write("SCORM2004 cmi_gobjective Insert status=".$completed." scope_id=".$pkg_id." measure=".$measure." satisfied=".$satisfied." objective_id=".$obj);
466  }
467  else
468  {
469  $ilDB->manipulateF('
470  UPDATE cmi_gobjective
471  SET status = %s,
472  measure = %s,
473  satisfied = %s
474  WHERE objective_id = %s
475  AND user_id = %s
476  AND scope_id = %s',
477  array('text', 'text', 'text', 'text', 'integer', 'integer'),
478  array($completed, $measure, $satisfied, $obj, $dbuser, $pkg_id)
479  );
480  $ilLog->write("SCORM2004 cmi_gobjective Update status=".$completed." scope_id=".$pkg_id." measure=".$measure." satisfied=".$satisfied." objective_id=".$obj);
481  }
482  } else //add it to the rows_to_insert
483  {
484  //create the row if this is the first time it has been found
485  if($rows_to_insert[$objective_id] == NULL)
486  {
487  $rows_to_insert[$objective_id] = Array();
488  }
489  $rows_to_insert[$objective_id][$key] = $toset;
490  }
491 
492  }
493  }
494 
495  //Get the scope for all the global objectives!!!
496  $res = $ilDB->queryF("SELECT global_to_system
497  FROM cp_package
498  WHERE obj_id = %s",
499  array('text'),
500  array($package)
501  );
502 
503  $scope_id = ($ilDB->fetchObject($res)->global_to_system) ? 0 : $package;
504 
505  //build up the set to look for in the query
506  $existing_key_template = "";
507  foreach(array_keys($rows_to_insert) as $obj_id)
508  {
509  $existing_key_template .= "'{$obj_id}',";
510 
511  }
512  //remove trailing ','
513  $existing_key_template = substr($existing_key_template, 0, strlen($existing_key_template) - 1);
514  $existing_keys = Array();
515 
516  if($existing_key_template != "")
517  {
518  //Get the ones that need to be updated in a single query
519  $res = $ilDB->queryF("SELECT objective_id
520  FROM cmi_gobjective
521  WHERE user_id = %s
522  AND scope_id = %s
523  AND objective_id IN ($existing_key_template)",
524  array('integer', 'integer'),
525  array($dbuser, $scope_id)
526  );
527 
528  while($row = $ilDB->fetchAssoc($res))
529  {
530  $existing_keys[] = $row['objective_id'];
531  }
532  }
533 
534  foreach($rows_to_insert as $obj_id => $vals)
535  {
536  if(in_array($obj_id, $existing_keys))
537  {
538  $ilDB->manipulateF("UPDATE cmi_gobjective
539  SET satisfied=%s,
540  measure=%s,
541  score_raw=%s,
542  score_min=%s,
543  score_max=%s,
544  completion_status=%s,
545  progress_measure=%s
546  WHERE objective_id = %s
547  AND user_id = %s
548  AND scope_id = %s",
549 
550  array('text','text', 'text', 'text', 'text', 'text',
551  'text', 'text', 'integer', 'integer'),
552 
553  array($vals['satisfied'], $vals["measure"], $vals["score_raw"],
554  $vals["score_min"], $vals["score_max"],
555  $vals["completion_status"], $vals["progress_measure"],
556  $obj_id, $dbuser, $scope_id)
557  );
558  } else
559  {
560  $ilDB->manipulateF("INSERT INTO cmi_gobjective
561  (user_id, satisfied, measure, scope_id, status, objective_id,
562  score_raw, score_min, score_max, progress_measure, completion_status)
563  VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
564 
565 
566  array('integer', 'text', 'text', 'integer', 'text', 'text',
567  'text', 'text', 'text', 'text', 'text'),
568 
569  array($dbuser, $vals['satisfied'], $vals['measure'],
570  $scope_id, NULL, $obj_id, $vals['score_raw'],
571  $vals['score_min'], $vals['score_max'],
572  $vals['progress_measure'], $vals['completion_status'])
573  );
574  }
575  }
576 
577  // update learning progress here not necessary because integrated in setCMIdata
578  // check _updateStatus for cmi_gobjective
579 // include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
580 // ilLPStatusWrapper::_updateStatus($package, $user);
581 
582  return $returnAr;
583  }
584 
585  protected static function ensureObjectDataCacheExistence()
586  {
590  global $ilObjDataCache;
591 
592  if($ilObjDataCache instanceof ilObjectDataCache)
593  {
594  return;
595  }
596 
597  require_once './Services/Object/classes/class.ilObjectDataCache.php';
598  $ilObjDataCache = new ilObjectDataCache();
599  $GLOBALS['ilObjDataCache'] = $ilObjDataCache;
600  }
601 
602 
603 
604 }
persistCMIData($userId=null, $packageId, $defaultLessonMode, $comments, $interactions, $objectives, $data=null)
$result
$_GET["client_id"]
scormPlayerUnload($userId=null, $packageId)
Class ilSCORM2004StoreData.
class ilObjectDataCache
writeGObjective($g_data, $user, $package)
$GLOBALS['ct_recipient']
setGlobalObjectives($userId, $packageId, $data)
setCMIData($userId, $packageId, $data, $getComments, $getInteractions, $getObjectives)
queryF($a_query, $a_types, $a_values)
Formatted query (for SELECTS).
_recordReadEvent($a_type, $a_ref_id, $obj_id, $usr_id, $isCatchupWriteEvents=true, $a_ext_rc=false, $a_ext_time=false)
Records a read event and catches up with write events.
static writeStatus($a_obj_id, $a_user_id, $a_status, $a_percentage=false, $a_force_per=false)
Write status for user and object.
while($lm_rec=$ilDB->fetchAssoc($lm_set)) $data
checkIfAllowed($packageId, $userId, $hash)
$packageId
syncGlobalStatus($userId, $packageId, $data, $new_global_status)