ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilSoapLearningProgressAdministration.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 
11 {
13  protected static array $DELETE_PROGRESS_FILTER_TYPES = ['sahs', 'tst'];
14 
15  public const PROGRESS_FILTER_ALL = 0;
16  public const PROGRESS_FILTER_IN_PROGRESS = 1;
17  public const PROGRESS_FILTER_COMPLETED = 2;
18  public const PROGRESS_FILTER_FAILED = 3;
20 
21  public const SOAP_LP_ERROR_AUTHENTICATION = 50;
22  public const SOAP_LP_ERROR_INVALID_FILTER = 52;
23  public const SOAP_LP_ERROR_INVALID_REF_ID = 54;
24  public const SOAP_LP_ERROR_LP_NOT_AVAILABLE = 56;
25  public const SOAP_LP_ERROR_NO_PERMISSION = 58;
26  public const SOAP_LP_ERROR_LP_NOT_ENABLED = 60;
27 
29  protected static array $PROGRESS_INFO_TYPES = [
30  self::PROGRESS_FILTER_ALL,
31  self::PROGRESS_FILTER_IN_PROGRESS,
32  self::PROGRESS_FILTER_COMPLETED,
33  self::PROGRESS_FILTER_FAILED,
34  self::PROGRESS_FILTER_NOT_ATTEMPTED
35  ];
36 
37  public const USER_FILTER_ALL = -1;
38 
42  public function deleteProgress(string $sid, array $ref_ids, array $usr_ids, array $type_filter, array $progress_filter)
43  {
44  $this->initAuth($sid);
45  $this->initIlias();
46 
47  if (!is_array($usr_ids)) {
48  $usr_ids = (array) $usr_ids;
49  }
50  if (!is_array($type_filter)) {
51  $type_filter = (array) $type_filter;
52  }
53 
54  if (!$this->checkSession($sid)) {
55  return $this->raiseError($this->getMessage(), $this->getMessageCode());
56  }
57 
58  if (array_diff($type_filter, self::$DELETE_PROGRESS_FILTER_TYPES)) {
59  return $this->raiseError('Invalid filter type given', 'Client');
60  }
61 
62  if (!in_array(self::USER_FILTER_ALL, $usr_ids) && !ilObjUser::userExists($usr_ids)) {
63  return $this->raiseError('Invalid user ids given', 'Client');
64  }
65 
66  $valid_refs = array();
67  $type = '';
68  foreach ($ref_ids as $ref_id) {
69  $obj_id = ilObject::_lookupObjId($ref_id);
70  $type = ilObject::_lookupType($obj_id);
71 
72  // All containers
73  if ($GLOBALS['DIC']['objDefinition']->isContainer($type)) {
74  $all_sub_objs = array();
75  foreach (($type_filter) as $type_filter_item) {
76  $sub_objs = $GLOBALS['DIC']['tree']->getSubTree(
77  $GLOBALS['DIC']['tree']->getNodeData($ref_id),
78  false,
79  $type_filter_item
80  );
81  $all_sub_objs = array_merge($all_sub_objs, $sub_objs);
82  }
83 
84  foreach ($all_sub_objs as $child_ref) {
85  $child_type = ilObject::_lookupType(ilObject::_lookupObjId($child_ref));
86  if (!$GLOBALS['DIC']['ilAccess']->checkAccess('write', '', $child_ref)) {
87  return $this->raiseError(
88  'Permission denied for : ' . $ref_id . ' -> type ' . $type,
89  'Client'
90  );
91  }
92  $valid_refs[] = $child_ref;
93  }
94  } elseif (in_array($type, $type_filter)) {
95  if (!$GLOBALS['DIC']['ilAccess']->checkAccess('write', '', $ref_id)) {
96  return $this->raiseError('Permission denied for : ' . $ref_id . ' -> type ' . $type, 'Client');
97  }
98  $valid_refs[] = $ref_id;
99  } else {
100  return $this->raiseError(
101  'Invalid object type given for : ' . $ref_id . ' -> type ' . $type,
102  'Client'
103  );
104  }
105  }
106 
107  // Delete tracking data
108  foreach ($valid_refs as $ref_id) {
109  $obj = ilObjectFactory::getInstanceByRefId($ref_id, false);
110 
111  if (!$obj instanceof ilObject) {
112  return $this->raiseError('Invalid reference id given : ' . $ref_id . ' -> type ' . $type, 'Client');
113  }
114 
115  // filter users
116  $valid_users = $this->applyProgressFilter($obj->getId(), $usr_ids, $progress_filter);
117 
118  switch ($obj->getType()) {
119  case 'sahs':
120  $subtype = ilObjSAHSLearningModule::_lookupSubType($obj->getId());
121 
122  switch ($subtype) {
123  case 'scorm':
124  $this->deleteScormTracking($obj->getId(), $valid_users);
125  break;
126 
127  case 'scorm2004':
128  $this->deleteScorm2004Tracking($obj->getId(), $valid_users);
129  break;
130  }
131  break;
132 
133  case 'tst':
134 
136  $obj->removeTestResultsFromSoapLpAdministration(array_values($valid_users));
137  break;
138  }
139 
140  // Refresh status
142  ilLPStatusWrapper::_refreshStatus($obj->getId(), $valid_users);
143  }
144  return true;
145  }
146 
151  public function getProgressInfo(string $sid, int $a_ref_id, array $a_progress_filter)
152  {
153  global $DIC;
154 
155  $this->initAuth($sid);
156  $this->initIlias();
157 
158  $ilAccess = $DIC->access();
159 
160  // Check session
161  if (!$this->checkSession($sid)) {
162  return $this->raiseError(
163  'Error ' . self::SOAP_LP_ERROR_AUTHENTICATION . ':' . $this->getMessage(),
164  self::SOAP_LP_ERROR_AUTHENTICATION
165  );
166  }
167 
168  // Check filter
169  if (array_diff($a_progress_filter, self::$PROGRESS_INFO_TYPES)) {
170  return $this->raiseError(
171  'Error ' . self::SOAP_LP_ERROR_INVALID_FILTER . ': Invalid filter type given',
172  self::SOAP_LP_ERROR_INVALID_FILTER
173  );
174  }
175  // Check LP enabled
177  return $this->raiseError(
178  'Error ' . self::SOAP_LP_ERROR_LP_NOT_ENABLED . ': Learning progress not enabled in ILIAS',
179  self::SOAP_LP_ERROR_LP_NOT_ENABLED
180  );
181  }
182 
183  $obj = ilObjectFactory::getInstanceByRefId($a_ref_id, false);
184  if (!$obj instanceof ilObject) {
185  return $this->raiseError(
186  'Error ' . self::SOAP_LP_ERROR_INVALID_REF_ID . ': Invalid reference id ' . $a_ref_id . ' given',
187  self::SOAP_LP_ERROR_INVALID_REF_ID
188  );
189  }
190 
191  // check lp available
192  $mode = ilLPObjSettings::_lookupDBMode($obj->getId());
193  if ($mode === ilLPObjSettings::LP_MODE_UNDEFINED) {
194  return $this->raiseError(
195  'Error ' . self::SOAP_LP_ERROR_LP_NOT_AVAILABLE . ': Learning progress not available for objects of type ' .
196  $obj->getType(),
197  self::SOAP_LP_ERROR_LP_NOT_AVAILABLE
198  );
199  }
200 
201  // check rbac
205  if (!$ilAccess->checkRbacOrPositionPermissionAccess(
206  'read_learning_progress',
207  'read_learning_progress',
208  $a_ref_id
209  )) {
210  return $this->raiseError(
211  'Error ' . self::SOAP_LP_ERROR_NO_PERMISSION . ': No Permission to access learning progress in this object',
212  self::SOAP_LP_ERROR_NO_PERMISSION
213  );
214  }
215 
216  $writer = new ilXmlWriter();
217  $writer->xmlStartTag(
218  'LearningProgressInfo',
219  array(
220  'ref_id' => $obj->getRefId(),
221  'type' => $obj->getType()
222  )
223  );
224 
225  $writer->xmlStartTag('LearningProgressSummary');
226 
227  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
228  self::PROGRESS_FILTER_COMPLETED,
229  $a_progress_filter
230  )) {
231  $completed = ilLPStatusWrapper::_getCompleted($obj->getId());
232  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
233  'read_learning_progress',
235  $a_ref_id,
236  $completed
237  );
238  $completed = count($completed);
239 
240  $writer->xmlElement(
241  'Status',
242  array(
243  'type' => self::PROGRESS_FILTER_COMPLETED,
244  'num' => $completed
245  )
246  );
247  }
248  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
249  self::PROGRESS_FILTER_IN_PROGRESS,
250  $a_progress_filter
251  )) {
252  $completed = ilLPStatusWrapper::_getInProgress($obj->getId());
253  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
254  'read_learning_progress',
256  $a_ref_id,
257  $completed
258  );
259  $completed = count($completed);
260 
261  $writer->xmlElement(
262  'Status',
263  array(
264  'type' => self::PROGRESS_FILTER_IN_PROGRESS,
265  'num' => $completed
266  )
267  );
268  }
269  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
270  self::PROGRESS_FILTER_FAILED,
271  $a_progress_filter
272  )) {
273  $completed = ilLPStatusWrapper::_getFailed($obj->getId());
274  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
275  'read_learning_progress',
277  $a_ref_id,
278  $completed
279  );
280  $completed = count($completed);
281 
282  $writer->xmlElement(
283  'Status',
284  array(
285  'type' => self::PROGRESS_FILTER_FAILED,
286  'num' => $completed
287  )
288  );
289  }
290  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
291  self::PROGRESS_FILTER_NOT_ATTEMPTED,
292  $a_progress_filter
293  )) {
294  $completed = ilLPStatusWrapper::_getNotAttempted($obj->getId());
295  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
296  'read_learning_progress',
298  $a_ref_id,
299  $completed
300  );
301  $completed = count($completed);
302 
303  $writer->xmlElement(
304  'Status',
305  array(
306  'type' => self::PROGRESS_FILTER_NOT_ATTEMPTED,
307  'num' => $completed
308  )
309  );
310  }
311  $writer->xmlEndTag('LearningProgressSummary');
312  $writer->xmlStartTag('UserProgress');
313  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
314  self::PROGRESS_FILTER_COMPLETED,
315  $a_progress_filter
316  )) {
317  $completed = ilLPStatusWrapper::_getCompleted($obj->getId());
318  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
319  'read_learning_progress',
321  $a_ref_id,
322  $completed
323  );
324 
325  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_COMPLETED);
326  }
327  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
328  self::PROGRESS_FILTER_IN_PROGRESS,
329  $a_progress_filter
330  )) {
331  $completed = ilLPStatusWrapper::_getInProgress($obj->getId());
332  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
333  'read_learning_progress',
335  $a_ref_id,
336  $completed
337  );
338  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_IN_PROGRESS);
339  }
340  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
341  self::PROGRESS_FILTER_FAILED,
342  $a_progress_filter
343  )) {
344  $completed = ilLPStatusWrapper::_getFailed($obj->getId());
345  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
346  'read_learning_progress',
348  $a_ref_id,
349  $completed
350  );
351  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_FAILED);
352  }
353  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
354  self::PROGRESS_FILTER_NOT_ATTEMPTED,
355  $a_progress_filter
356  )) {
357  $completed = ilLPStatusWrapper::_getNotAttempted($obj->getId());
358  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
359  'read_learning_progress',
361  $a_ref_id,
362  $completed
363  );
364 
365  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_NOT_ATTEMPTED);
366  }
367  $writer->xmlEndTag('UserProgress');
368  $writer->xmlEndTag('LearningProgressInfo');
369 
370  return $writer->xmlDumpMem();
371  }
372 
373  protected function addUserProgress(ilXmlWriter $writer, array $users, int $a_type): void
374  {
375  foreach ($users as $user_id) {
376  $writer->xmlStartTag(
377  'User',
378  array(
379  'id' => $user_id,
380  'status' => $a_type
381  )
382  );
383 
384  $info = ilObjUser::_lookupName($user_id);
385  $writer->xmlElement('Login', array(), (string) $info['login']);
386  $writer->xmlElement('Firstname', array(), (string) $info['firstname']);
387  $writer->xmlElement('Lastname', array(), (string) $info['lastname']);
388  $writer->xmlEndTag('User');
389  }
390  }
391 
399  protected function applyProgressFilter(int $obj_id, array $usr_ids, array $filter): array
400  {
401  $all_users = array();
402  if (in_array(self::USER_FILTER_ALL, $usr_ids)) {
403  $all_users = array_unique(
404  array_merge(
408  )
409  );
410  } else {
411  $all_users = $usr_ids;
412  }
413 
414  if (!$filter || in_array(self::PROGRESS_FILTER_ALL, $filter)) {
415  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Deleting all progress data');
416  return $all_users;
417  }
418 
419  $filter_users = array();
420  if (in_array(self::PROGRESS_FILTER_IN_PROGRESS, $filter)) {
421  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering in progress.');
422  $filter_users = array_merge($filter, ilLPStatusWrapper::_getInProgress($obj_id));
423  }
424  if (in_array(self::PROGRESS_FILTER_COMPLETED, $filter)) {
425  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering completed.');
426  $filter_users = array_merge($filter, ilLPStatusWrapper::_getCompleted($obj_id));
427  }
428  if (in_array(self::PROGRESS_FILTER_FAILED, $filter)) {
429  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering failed.');
430  $filter_users = array_merge($filter, ilLPStatusWrapper::_getFailed($obj_id));
431  }
432 
433  // Build intersection
434  return array_intersect($all_users, $filter_users);
435  }
436 
440  protected function deleteScormTracking(int $a_obj_id, array $a_usr_ids): bool
441  {
442  global $DIC;
443 
444  $ilDB = $DIC['ilDB'];
445 
446  $query = 'DELETE FROM scorm_tracking ' .
447  'WHERE ' . $ilDB->in('user_id', $a_usr_ids, false, 'integer') . ' ' .
448  'AND obj_id = ' . $ilDB->quote($a_obj_id, 'integer') . ' ';
449  $res = $ilDB->manipulate($query);
450  return true;
451  }
452 
456  protected function deleteScorm2004Tracking(int $a_obj_id, array $a_usr_ids): void
457  {
458  global $DIC;
459 
460  $ilDB = $DIC['ilDB'];
461 
462  $query = 'SELECT cp_node_id FROM cp_node ' .
463  'WHERE nodename = ' . $ilDB->quote('item', 'text') . ' ' .
464  'AND cp_node.slm_id = ' . $ilDB->quote($a_obj_id, 'integer');
465  $res = $ilDB->query($query);
466 
467  $scos = array();
468  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
469  $scos[] = $row->cp_node_id;
470  }
471 
472  $query = 'DELETE FROM cmi_node ' .
473  'WHERE ' . $ilDB->in('user_id', $a_usr_ids, false, 'integer') . ' ' .
474  'AND ' . $ilDB->in('cp_node_id', $scos, false, 'integer');
475  $ilDB->manipulate($query);
476  }
477 
482  public function getLearningProgressChanges(string $sid, string $timestamp, bool $include_ref_ids, array $type_filter)
483  {
484  $this->initAuth($sid);
485  $this->initIlias();
486 
487  if (!$this->checkSession($sid)) {
488  return $this->raiseError($this->getMessage(), $this->getMessageCode());
489  }
490  global $DIC;
491 
492  $rbacsystem = $DIC['rbacsystem'];
493  $tree = $DIC['tree'];
494  $ilLog = $DIC['ilLog'];
495 
496  // check administrator
497  $types = "";
498  if (is_array($type_filter)) {
499  $types = implode(",", $type_filter);
500  }
501 
502  // output lp changes as xml
503  try {
504  $writer = new ilLPXmlWriter(true);
505  $writer->setTimestamp($timestamp);
506  $writer->setIncludeRefIds($include_ref_ids);
507  $writer->setTypeFilter($type_filter);
508  $writer->write();
509 
510  return $writer->xmlDumpMem(true);
511  } catch (UnexpectedValueException $e) {
512  return $this->raiseError($e->getMessage(), 'Client');
513  }
514  }
515 }
$res
Definition: ltiservices.php:69
getLearningProgressChanges(string $sid, string $timestamp, bool $include_ref_ids, array $type_filter)
This class handles all DB changes necessary for fraunhofer.
static _getCompleted(int $a_obj_id)
Static function to read the users who have the status &#39;completed&#39;.
deleteScorm2004Tracking(int $a_obj_id, array $a_usr_ids)
Delete scorm 2004 tracking.
raiseError(string $a_message, $a_code)
static _lookupName(int $a_user_id)
lookup user name
static _resetInfoCaches($a_obj_id)
static _getInProgress(int $a_obj_id)
Static function to read users who have the status &#39;in_progress&#39;.
static _getNotAttempted(int $a_obj_id)
Static function to read the number of user who have the status &#39;not_attempted&#39;.
static _lookupSubType(int $a_obj_id)
lookup subtype id (scorm, )
static _lookupObjId(int $ref_id)
xmlEndTag(string $tag)
Writes an endtag.
$ref_id
Definition: ltiauth.php:66
XML writer learning progress.
static userExists(array $a_usr_ids=[])
$GLOBALS["DIC"]
Definition: wac.php:30
static _getFailed(int $a_obj_id)
Static function to read the users who have the status &#39;completed&#39;.
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
global $DIC
Definition: shib_login.php:25
static _refreshStatus(int $a_obj_id, ?array $a_users=null)
addUserProgress(ilXmlWriter $writer, array $users, int $a_type)
static _lookupDBMode(int $a_obj_id)
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:70
applyProgressFilter(int $obj_id, array $usr_ids, array $filter)
Apply progress filter.
xmlStartTag(string $tag, ?array $attrs=null, bool $empty=false, bool $encode=true, bool $escape=true)
Writes a starttag.
xmlElement(string $tag, $attrs=null, $data=null, $encode=true, $escape=true)
Writes a basic element (no children, just textual content)
static _lookupType(int $id, bool $reference=false)
deleteScormTracking(int $a_obj_id, array $a_usr_ids)
Delete SCORM Tracking.