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