ILIAS  trunk Revision v11.0_alpha-1769-g99a433fe2dc
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilSoapLearningProgressAdministration.php
Go to the documentation of this file.
1 <?php
2 
25 {
27  protected static array $DELETE_PROGRESS_FILTER_TYPES = ['sahs', 'tst'];
28 
29  public const PROGRESS_FILTER_ALL = 0;
30  public const PROGRESS_FILTER_IN_PROGRESS = 1;
31  public const PROGRESS_FILTER_COMPLETED = 2;
32  public const PROGRESS_FILTER_FAILED = 3;
34 
35  public const SOAP_LP_ERROR_AUTHENTICATION = 50;
36  public const SOAP_LP_ERROR_INVALID_FILTER = 52;
37  public const SOAP_LP_ERROR_INVALID_REF_ID = 54;
38  public const SOAP_LP_ERROR_LP_NOT_AVAILABLE = 56;
39  public const SOAP_LP_ERROR_NO_PERMISSION = 58;
40  public const SOAP_LP_ERROR_LP_NOT_ENABLED = 60;
41 
43  protected static array $PROGRESS_INFO_TYPES = [
44  self::PROGRESS_FILTER_ALL,
45  self::PROGRESS_FILTER_IN_PROGRESS,
46  self::PROGRESS_FILTER_COMPLETED,
47  self::PROGRESS_FILTER_FAILED,
48  self::PROGRESS_FILTER_NOT_ATTEMPTED
49  ];
50 
51  public const USER_FILTER_ALL = -1;
52 
56  public function deleteProgress(string $sid, array $ref_ids, array $usr_ids, array $type_filter, array $progress_filter)
57  {
58  $this->initAuth($sid);
59  $this->initIlias();
60 
61  if (!is_array($usr_ids)) {
62  $usr_ids = (array) $usr_ids;
63  }
64  if (!is_array($type_filter)) {
65  $type_filter = (array) $type_filter;
66  }
67 
68  if (!$this->checkSession($sid)) {
69  return $this->raiseError($this->getMessage(), $this->getMessageCode());
70  }
71 
72  if (array_diff($type_filter, self::$DELETE_PROGRESS_FILTER_TYPES)) {
73  return $this->raiseError('Invalid filter type given', 'Client');
74  }
75 
76  if (!in_array(self::USER_FILTER_ALL, $usr_ids) && !ilObjUser::userExists($usr_ids)) {
77  return $this->raiseError('Invalid user ids given', 'Client');
78  }
79 
80  $valid_refs = array();
81  $type = '';
82  foreach ($ref_ids as $ref_id) {
83  $obj_id = ilObject::_lookupObjId($ref_id);
84  $type = ilObject::_lookupType($obj_id);
85 
86  // All containers
87  if ($GLOBALS['DIC']['objDefinition']->isContainer($type)) {
88  $all_sub_objs = array();
89  foreach (($type_filter) as $type_filter_item) {
90  $sub_objs = $GLOBALS['DIC']['tree']->getSubTree(
91  $GLOBALS['DIC']['tree']->getNodeData($ref_id),
92  false,
93  $type_filter_item
94  );
95  $all_sub_objs = array_merge($all_sub_objs, $sub_objs);
96  }
97 
98  foreach ($all_sub_objs as $child_ref) {
99  $child_type = ilObject::_lookupType(ilObject::_lookupObjId($child_ref));
100  if (!$GLOBALS['DIC']['ilAccess']->checkAccess('write', '', $child_ref)) {
101  return $this->raiseError(
102  'Permission denied for : ' . $ref_id . ' -> type ' . $type,
103  'Client'
104  );
105  }
106  $valid_refs[] = $child_ref;
107  }
108  } elseif (in_array($type, $type_filter)) {
109  if (!$GLOBALS['DIC']['ilAccess']->checkAccess('write', '', $ref_id)) {
110  return $this->raiseError('Permission denied for : ' . $ref_id . ' -> type ' . $type, 'Client');
111  }
112  $valid_refs[] = $ref_id;
113  } else {
114  return $this->raiseError(
115  'Invalid object type given for : ' . $ref_id . ' -> type ' . $type,
116  'Client'
117  );
118  }
119  }
120 
121  // Delete tracking data
122  foreach ($valid_refs as $ref_id) {
123  $obj = ilObjectFactory::getInstanceByRefId($ref_id, false);
124 
125  if (!$obj instanceof ilObject) {
126  return $this->raiseError('Invalid reference id given : ' . $ref_id . ' -> type ' . $type, 'Client');
127  }
128 
129  // filter users
130  $valid_users = $this->applyProgressFilter($obj->getId(), $usr_ids, $progress_filter);
131 
132  switch ($obj->getType()) {
133  case 'sahs':
134  $subtype = ilObjSAHSLearningModule::_lookupSubType($obj->getId());
135 
136  switch ($subtype) {
137  case 'scorm':
138  $this->deleteScormTracking($obj->getId(), $valid_users);
139  break;
140 
141  case 'scorm2004':
142  $this->deleteScorm2004Tracking($obj->getId(), $valid_users);
143  break;
144  }
145  break;
146 
147  case 'tst':
148 
150  $obj->removeTestResultsFromSoapLpAdministration(array_values($valid_users));
151  break;
152  }
153 
154  // Refresh status
156  ilLPStatusWrapper::_refreshStatus($obj->getId(), $valid_users);
157  }
158  return true;
159  }
160 
165  public function getProgressInfo(string $sid, int $a_ref_id, array $a_progress_filter)
166  {
167  global $DIC;
168 
169  $this->initAuth($sid);
170  $this->initIlias();
171 
172  $ilAccess = $DIC->access();
173 
174  // Check session
175  if (!$this->checkSession($sid)) {
176  return $this->raiseError(
177  'Error ' . self::SOAP_LP_ERROR_AUTHENTICATION . ':' . $this->getMessage(),
178  self::SOAP_LP_ERROR_AUTHENTICATION
179  );
180  }
181 
182  // Check filter
183  if (array_diff($a_progress_filter, self::$PROGRESS_INFO_TYPES)) {
184  return $this->raiseError(
185  'Error ' . self::SOAP_LP_ERROR_INVALID_FILTER . ': Invalid filter type given',
186  self::SOAP_LP_ERROR_INVALID_FILTER
187  );
188  }
189  // Check LP enabled
191  return $this->raiseError(
192  'Error ' . self::SOAP_LP_ERROR_LP_NOT_ENABLED . ': Learning progress not enabled in ILIAS',
193  self::SOAP_LP_ERROR_LP_NOT_ENABLED
194  );
195  }
196 
197  $obj = ilObjectFactory::getInstanceByRefId($a_ref_id, false);
198  if (!$obj instanceof ilObject) {
199  return $this->raiseError(
200  'Error ' . self::SOAP_LP_ERROR_INVALID_REF_ID . ': Invalid reference id ' . $a_ref_id . ' given',
201  self::SOAP_LP_ERROR_INVALID_REF_ID
202  );
203  }
204 
205  // check lp available
206  $mode = ilLPObjSettings::_lookupDBMode($obj->getId());
207  if ($mode === ilLPObjSettings::LP_MODE_UNDEFINED) {
208  return $this->raiseError(
209  'Error ' . self::SOAP_LP_ERROR_LP_NOT_AVAILABLE . ': Learning progress not available for objects of type ' .
210  $obj->getType(),
211  self::SOAP_LP_ERROR_LP_NOT_AVAILABLE
212  );
213  }
214 
215  // check rbac
219  if (!$ilAccess->checkRbacOrPositionPermissionAccess(
220  'read_learning_progress',
221  'read_learning_progress',
222  $a_ref_id
223  )) {
224  return $this->raiseError(
225  'Error ' . self::SOAP_LP_ERROR_NO_PERMISSION . ': No Permission to access learning progress in this object',
226  self::SOAP_LP_ERROR_NO_PERMISSION
227  );
228  }
229 
230  $writer = new ilXmlWriter();
231  $writer->xmlStartTag(
232  'LearningProgressInfo',
233  array(
234  'ref_id' => $obj->getRefId(),
235  'type' => $obj->getType()
236  )
237  );
238 
239  $writer->xmlStartTag('LearningProgressSummary');
240 
241  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
242  self::PROGRESS_FILTER_COMPLETED,
243  $a_progress_filter
244  )) {
245  $completed = ilLPStatusWrapper::_getCompleted($obj->getId());
246  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
247  'read_learning_progress',
249  $a_ref_id,
250  $completed
251  );
252  $completed = count($completed);
253 
254  $writer->xmlElement(
255  'Status',
256  array(
257  'type' => self::PROGRESS_FILTER_COMPLETED,
258  'num' => $completed
259  )
260  );
261  }
262  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
263  self::PROGRESS_FILTER_IN_PROGRESS,
264  $a_progress_filter
265  )) {
266  $completed = ilLPStatusWrapper::_getInProgress($obj->getId());
267  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
268  'read_learning_progress',
270  $a_ref_id,
271  $completed
272  );
273  $completed = count($completed);
274 
275  $writer->xmlElement(
276  'Status',
277  array(
278  'type' => self::PROGRESS_FILTER_IN_PROGRESS,
279  'num' => $completed
280  )
281  );
282  }
283  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
284  self::PROGRESS_FILTER_FAILED,
285  $a_progress_filter
286  )) {
287  $completed = ilLPStatusWrapper::_getFailed($obj->getId());
288  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
289  'read_learning_progress',
291  $a_ref_id,
292  $completed
293  );
294  $completed = count($completed);
295 
296  $writer->xmlElement(
297  'Status',
298  array(
299  'type' => self::PROGRESS_FILTER_FAILED,
300  'num' => $completed
301  )
302  );
303  }
304  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
305  self::PROGRESS_FILTER_NOT_ATTEMPTED,
306  $a_progress_filter
307  )) {
308  $completed = ilLPStatusWrapper::_getNotAttempted($obj->getId());
309  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
310  'read_learning_progress',
312  $a_ref_id,
313  $completed
314  );
315  $completed = count($completed);
316 
317  $writer->xmlElement(
318  'Status',
319  array(
320  'type' => self::PROGRESS_FILTER_NOT_ATTEMPTED,
321  'num' => $completed
322  )
323  );
324  }
325  $writer->xmlEndTag('LearningProgressSummary');
326  $writer->xmlStartTag('UserProgress');
327  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
328  self::PROGRESS_FILTER_COMPLETED,
329  $a_progress_filter
330  )) {
331  $completed = ilLPStatusWrapper::_getCompleted($obj->getId());
332  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
333  'read_learning_progress',
335  $a_ref_id,
336  $completed
337  );
338 
339  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_COMPLETED);
340  }
341  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
342  self::PROGRESS_FILTER_IN_PROGRESS,
343  $a_progress_filter
344  )) {
345  $completed = ilLPStatusWrapper::_getInProgress($obj->getId());
346  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
347  'read_learning_progress',
349  $a_ref_id,
350  $completed
351  );
352  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_IN_PROGRESS);
353  }
354  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
355  self::PROGRESS_FILTER_FAILED,
356  $a_progress_filter
357  )) {
358  $completed = ilLPStatusWrapper::_getFailed($obj->getId());
359  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
360  'read_learning_progress',
362  $a_ref_id,
363  $completed
364  );
365  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_FAILED);
366  }
367  if (in_array(self::PROGRESS_FILTER_ALL, $a_progress_filter) || in_array(
368  self::PROGRESS_FILTER_NOT_ATTEMPTED,
369  $a_progress_filter
370  )) {
371  $completed = ilLPStatusWrapper::_getNotAttempted($obj->getId());
372  $completed = $GLOBALS['DIC']->access()->filterUserIdsByRbacOrPositionOfCurrentUser(
373  'read_learning_progress',
375  $a_ref_id,
376  $completed
377  );
378 
379  $this->addUserProgress($writer, $completed, self::PROGRESS_FILTER_NOT_ATTEMPTED);
380  }
381  $writer->xmlEndTag('UserProgress');
382  $writer->xmlEndTag('LearningProgressInfo');
383 
384  return $writer->xmlDumpMem();
385  }
386 
387  protected function addUserProgress(ilXmlWriter $writer, array $users, int $a_type): void
388  {
389  foreach ($users as $user_id) {
390  $writer->xmlStartTag(
391  'User',
392  array(
393  'id' => $user_id,
394  'status' => $a_type
395  )
396  );
397 
398  $info = ilObjUser::_lookupName($user_id);
399  $writer->xmlElement('Login', array(), (string) $info['login']);
400  $writer->xmlElement('Firstname', array(), (string) $info['firstname']);
401  $writer->xmlElement('Lastname', array(), (string) $info['lastname']);
402  $writer->xmlEndTag('User');
403  }
404  }
405 
413  protected function applyProgressFilter(int $obj_id, array $usr_ids, array $filter): array
414  {
415  $all_users = array();
416  if (in_array(self::USER_FILTER_ALL, $usr_ids)) {
417  $all_users = array_unique(
418  array_merge(
422  )
423  );
424  } else {
425  $all_users = $usr_ids;
426  }
427 
428  if (!$filter || in_array(self::PROGRESS_FILTER_ALL, $filter)) {
429  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Deleting all progress data');
430  return $all_users;
431  }
432 
433  $filter_users = array();
434  if (in_array(self::PROGRESS_FILTER_IN_PROGRESS, $filter)) {
435  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering in progress.');
436  $filter_users = array_merge($filter, ilLPStatusWrapper::_getInProgress($obj_id));
437  }
438  if (in_array(self::PROGRESS_FILTER_COMPLETED, $filter)) {
439  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering completed.');
440  $filter_users = array_merge($filter, ilLPStatusWrapper::_getCompleted($obj_id));
441  }
442  if (in_array(self::PROGRESS_FILTER_FAILED, $filter)) {
443  $GLOBALS['DIC']['log']->write(__METHOD__ . ': Filtering failed.');
444  $filter_users = array_merge($filter, ilLPStatusWrapper::_getFailed($obj_id));
445  }
446 
447  // Build intersection
448  return array_intersect($all_users, $filter_users);
449  }
450 
454  protected function deleteScormTracking(int $a_obj_id, array $a_usr_ids): bool
455  {
456  global $DIC;
457 
458  $ilDB = $DIC['ilDB'];
459 
460  $query = 'DELETE FROM scorm_tracking ' .
461  'WHERE ' . $ilDB->in('user_id', $a_usr_ids, false, 'integer') . ' ' .
462  'AND obj_id = ' . $ilDB->quote($a_obj_id, 'integer') . ' ';
463  $res = $ilDB->manipulate($query);
464  return true;
465  }
466 
470  protected function deleteScorm2004Tracking(int $a_obj_id, array $a_usr_ids): void
471  {
472  global $DIC;
473 
474  $ilDB = $DIC['ilDB'];
475 
476  $query = 'SELECT cp_node_id FROM cp_node ' .
477  'WHERE nodename = ' . $ilDB->quote('item', 'text') . ' ' .
478  'AND cp_node.slm_id = ' . $ilDB->quote($a_obj_id, 'integer');
479  $res = $ilDB->query($query);
480 
481  $scos = array();
482  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
483  $scos[] = $row->cp_node_id;
484  }
485 
486  $query = 'DELETE FROM cmi_node ' .
487  'WHERE ' . $ilDB->in('user_id', $a_usr_ids, false, 'integer') . ' ' .
488  'AND ' . $ilDB->in('cp_node_id', $scos, false, 'integer');
489  $ilDB->manipulate($query);
490  }
491 
496  public function getLearningProgressChanges(string $sid, string $timestamp, bool $include_ref_ids, array $type_filter)
497  {
498  $this->initAuth($sid);
499  $this->initIlias();
500 
501  if (!$this->checkSession($sid)) {
502  return $this->raiseError($this->getMessage(), $this->getMessageCode());
503  }
504  global $DIC;
505 
506  $rbacsystem = $DIC['rbacsystem'];
507  $tree = $DIC['tree'];
508  $ilLog = $DIC['ilLog'];
509 
510  // check administrator
511  $types = "";
512  if (is_array($type_filter)) {
513  $types = implode(",", $type_filter);
514  }
515 
516  // output lp changes as xml
517  try {
518  $writer = new ilLPXmlWriter(true);
519  $writer->setTimestamp($timestamp);
520  $writer->setIncludeRefIds($include_ref_ids);
521  $writer->setTypeFilter($type_filter);
522  $writer->write();
523 
524  return $writer->xmlDumpMem(true);
525  } catch (UnexpectedValueException $e) {
526  return $this->raiseError($e->getMessage(), 'Client');
527  }
528  }
529 }
$res
Definition: ltiservices.php:66
getLearningProgressChanges(string $sid, string $timestamp, bool $include_ref_ids, array $type_filter)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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:65
XML writer learning progress.
static userExists(array $a_usr_ids=[])
$GLOBALS["DIC"]
Definition: wac.php:53
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:22
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.