1<?php 2/* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4/** 5 * Class ilBadgeHandler 6 * 7 * @author Jörg Lützenkirchen <luetzenkirchen@leifos.com> 8 * @version $Id:$ 9 * 10 * @package ServicesBadge 11 */ 12class ilBadgeHandler 13{ 14 /** 15 * @var ilDB 16 */ 17 protected $db; 18 19 /** 20 * @var ilTree 21 */ 22 protected $tree; 23 24 /** 25 * @var ilLanguage 26 */ 27 protected $lng; 28 29 protected $settings; // [ilSetting] 30 31 protected static $instance; // [ilBadgeHandler] 32 33 /** 34 * Constructor 35 * 36 * @return self 37 */ 38 protected function __construct() 39 { 40 global $DIC; 41 42 $this->db = $DIC->database(); 43 if (isset($DIC["tree"])) { 44 $this->tree = $DIC->repositoryTree(); 45 } 46 $this->settings = new ilSetting("bdga"); 47 } 48 49 /** 50 * Constructor 51 * 52 * @return self 53 */ 54 public static function getInstance() 55 { 56 if (!self::$instance) { 57 self::$instance = new self(); 58 } 59 return self::$instance; 60 } 61 62 63 // 64 // setter/getter 65 // 66 67 public function isActive() 68 { 69 return $this->settings->get("active", false); 70 } 71 72 public function setActive($a_value) 73 { 74 $this->settings->set("active", (bool) $a_value); 75 } 76 77 public function isObiActive() 78 { 79 // see bug #20124 80 return false; 81 82 return $this->settings->get("obi_active", false); 83 } 84 85 public function setObiActive($a_value) 86 { 87 $this->settings->set("obi_active", (bool) $a_value); 88 } 89 90 public function getObiOrganistation() 91 { 92 return $this->settings->get("obi_organisation", null); 93 } 94 95 public function setObiOrganisation($a_value) 96 { 97 $this->settings->set("obi_organisation", trim($a_value)); 98 } 99 100 public function getObiContact() 101 { 102 return $this->settings->get("obi_contact", null); 103 } 104 105 public function setObiContact($a_value) 106 { 107 $this->settings->set("obi_contact", trim($a_value)); 108 } 109 110 public function getObiSalt() 111 { 112 return $this->settings->get("obi_salt", null); 113 } 114 115 public function setObiSalt($a_value) 116 { 117 $this->settings->set("obi_salt", trim($a_value)); 118 } 119 120 public function getComponents() 121 { 122 $components = $this->settings->get("components", null); 123 if ($components) { 124 return unserialize($components); 125 } 126 return array(); 127 } 128 129 public function setComponents(array $a_components = null) 130 { 131 if (is_array($a_components) && 132 !sizeof($a_components)) { 133 $a_components = null; 134 } 135 $this->settings->set("components", $a_components !== null 136 ? serialize(array_unique($a_components)) 137 : null); 138 } 139 140 141 // 142 // component handling 143 // 144 145 protected function getComponent($a_id) 146 { 147 $ilDB = $this->db; 148 149 // see ilCtrl 150 $set = $ilDB->query("SELECT * FROM il_component" . 151 " WHERE id = " . $ilDB->quote($a_id, "text")); 152 $rec = $ilDB->fetchAssoc($set); 153 if ($rec["type"]) { 154 return $rec; 155 } 156 } 157 158 /** 159 * Get provider instance 160 * 161 * @param string $a_component_id 162 * @return ilBadgeProvider 163 */ 164 public function getProviderInstance($a_component_id) 165 { 166 $comp = $this->getComponent($a_component_id); 167 if ($comp) { 168 $class = "il" . $comp["name"] . "BadgeProvider"; 169 $file = $comp["type"] . "/" . $comp["name"] . "/classes/class." . $class . ".php"; 170 if (file_exists($file)) { 171 include_once $file; 172 $obj = new $class; 173 if ($obj instanceof ilBadgeProvider) { 174 return $obj; 175 } 176 } 177 } 178 } 179 180 public function getComponentCaption($a_component_id) 181 { 182 $comp = $this->getComponent($a_component_id); 183 if ($comp) { 184 return $comp["type"] . "/" . $comp["name"]; 185 } 186 } 187 188 // 189 // types 190 // 191 192 public function getUniqueTypeId($a_component_id, ilBadgeType $a_badge) 193 { 194 return $a_component_id . "/" . $a_badge->getId(); 195 } 196 197 /** 198 * Get type instance by unique id (component, type) 199 * @param string $a_id 200 * @return ilBadgeType 201 */ 202 public function getTypeInstanceByUniqueId($a_id) 203 { 204 $parts = explode("/", $a_id); 205 $comp_id = $parts[0]; 206 $type_id = $parts[1]; 207 $provider = $this->getProviderInstance($comp_id); 208 if ($provider) { 209 foreach ($provider->getBadgeTypes() as $type) { 210 if ($type->getId() == $type_id) { 211 return $type; 212 } 213 } 214 } 215 } 216 217 public function getInactiveTypes() 218 { 219 $types = $this->settings->get("inactive_types", null); 220 if ($types) { 221 return unserialize($types); 222 } 223 return array(); 224 } 225 226 public function setInactiveTypes(array $a_types = null) 227 { 228 if (is_array($a_types) && 229 !sizeof($a_types)) { 230 $a_types = null; 231 } 232 $this->settings->set("inactive_types", $a_types !== null 233 ? serialize(array_unique($a_types)) 234 : null); 235 } 236 237 /** 238 * Get badges types 239 * 240 * @return ilBadgeType[] 241 */ 242 public function getAvailableTypes() 243 { 244 $res = array(); 245 246 $inactive = $this->getInactiveTypes(); 247 foreach ($this->getComponents() as $component_id) { 248 $provider = $this->getProviderInstance($component_id); 249 if ($provider) { 250 foreach ($provider->getBadgeTypes() as $type) { 251 $id = $this->getUniqueTypeId($component_id, $type); 252 if (!in_array($id, $inactive)) { 253 $res[$id] = $type; 254 } 255 } 256 } 257 } 258 259 return $res; 260 } 261 262 /** 263 * Get valid badges types for object type 264 * 265 * @param string $a_object_type 266 * @return ilBadgeType[] 267 */ 268 public function getAvailableTypesForObjType($a_object_type) 269 { 270 $res = array(); 271 272 foreach ($this->getAvailableTypes() as $id => $type) { 273 if (in_array($a_object_type, $type->getValidObjectTypes())) { 274 $res[$id] = $type; 275 } 276 } 277 278 return $res; 279 } 280 281 /** 282 * Get available manual badges for object id 283 * 284 * @param int $a_parent_obj_id 285 * @param string $a_parent_obj_type 286 * @return array id,title 287 */ 288 public function getAvailableManualBadges($a_parent_obj_id, $a_parent_obj_type = null) 289 { 290 $res = array(); 291 292 if (!$a_parent_obj_type) { 293 $a_parent_obj_type = ilObject::_lookupType($a_parent_obj_id); 294 } 295 296 include_once "./Services/Badge/classes/class.ilBadge.php"; 297 $badges = ilBadge::getInstancesByParentId($a_parent_obj_id); 298 foreach (ilBadgeHandler::getInstance()->getAvailableTypesForObjType($a_parent_obj_type) as $type_id => $type) { 299 if (!$type instanceof ilBadgeAuto) { 300 foreach ($badges as $badge) { 301 if ($badge->getTypeId() == $type_id && 302 $badge->isActive()) { 303 $res[$badge->getId()] = $badge->getTitle(); 304 } 305 } 306 } 307 } 308 309 asort($res); 310 return $res; 311 } 312 313 314 315 // 316 // service/module definition 317 // 318 319 /** 320 * Import component definition 321 * 322 * @param string $a_component_id 323 */ 324 public static function updateFromXML($a_component_id) 325 { 326 $handler = self::getInstance(); 327 $components = $handler->getComponents(); 328 $components[] = $a_component_id; 329 $handler->setComponents($components); 330 } 331 332 /** 333 * Remove component definition 334 * 335 * @param string $a_component_id 336 */ 337 public static function clearFromXML($a_component_id) 338 { 339 $handler = self::getInstance(); 340 $components = $handler->getComponents(); 341 foreach ($components as $idx => $component) { 342 if ($component == $a_component_id) { 343 unset($components[$idx]); 344 } 345 } 346 $handler->setComponents($components); 347 } 348 349 350 // 351 // helper 352 // 353 354 public function isObjectActive($a_obj_id, $a_obj_type = null) 355 { 356 if (!$this->isActive()) { 357 return false; 358 } 359 360 if (!$a_obj_type) { 361 $a_obj_type = ilObject::_lookupType($a_obj_id); 362 } 363 364 if ($a_obj_type != "bdga") { 365 include_once 'Services/Container/classes/class.ilContainer.php'; 366 include_once 'Services/Object/classes/class.ilObjectServiceSettingsGUI.php'; 367 if (!ilContainer::_lookupContainerSetting( 368 $a_obj_id, 369 ilObjectServiceSettingsGUI::BADGES, 370 false 371 )) { 372 return false; 373 } 374 } 375 376 return true; 377 } 378 379 public function triggerEvaluation($a_type_id, $a_user_id, array $a_params = null) 380 { 381 if (!$this->isActive() || 382 in_array($a_type_id, $this->getInactiveTypes())) { 383 return; 384 } 385 386 $type = $this->getTypeInstanceByUniqueId($a_type_id); 387 if (!$type || 388 !$type instanceof ilBadgeAuto) { 389 return; 390 } 391 392 include_once "Services/Badge/classes/class.ilBadge.php"; 393 include_once "Services/Badge/classes/class.ilBadgeAssignment.php"; 394 $new_badges = array(); 395 foreach (ilBadge::getInstancesByType($a_type_id) as $badge) { 396 if ($badge->isActive()) { 397 // already assigned? 398 if (!ilBadgeAssignment::exists($badge->getId(), $a_user_id)) { 399 if ((bool) $type->evaluate($a_user_id, (array) $a_params, (array) $badge->getConfiguration())) { 400 $ass = new ilBadgeAssignment($badge->getId(), $a_user_id); 401 $ass->store(); 402 403 $new_badges[$a_user_id][] = $badge->getId(); 404 } 405 } 406 } 407 } 408 409 $this->sendNotification($new_badges); 410 } 411 412 public function getUserIds($a_parent_ref_id, $a_parent_obj_id = null, $a_parent_type = null) 413 { 414 $tree = $this->tree; 415 416 if (!$a_parent_obj_id) { 417 $a_parent_obj_id = ilObject::_lookupObjectId($a_parent_ref_id); 418 } 419 if (!$a_parent_type) { 420 $a_parent_type = ilObject::_lookupType($a_parent_obj_id); 421 } 422 423 // try to get participants from (parent) course/group 424 switch ($a_parent_type) { 425 case "crs": 426 include_once "Modules/Course/classes/class.ilCourseParticipants.php"; 427 $member_obj = ilCourseParticipants::_getInstanceByObjId($a_parent_obj_id); 428 return $member_obj->getMembers(); 429 430 case "grp": 431 include_once "Modules/Group/classes/class.ilGroupParticipants.php"; 432 $member_obj = ilGroupParticipants::_getInstanceByObjId($a_parent_obj_id); 433 return $member_obj->getMembers(); 434 435 default: 436 // walk path to find course or group object and use members of that object 437 $path = $tree->getPathId($a_parent_ref_id); 438 array_pop($path); 439 foreach (array_reverse($path) as $path_ref_id) { 440 $type = ilObject::_lookupType($path_ref_id, true); 441 if ($type == "crs" || $type == "grp") { 442 return $this->getParticipantsForObject($path_ref_id, null, $type); 443 } 444 } 445 break; 446 } 447 } 448 449 450 // 451 // PATH HANDLING (PUBLISHING) 452 // 453 454 protected function getBasePath() 455 { 456 return ilUtil::getWebspaceDir() . "/pub_badges/"; 457 } 458 459 public function getInstancePath(ilBadgeAssignment $a_ass) 460 { 461 $hash = md5($a_ass->getBadgeId() . "_" . $a_ass->getUserId()); 462 463 $path = $this->getBasePath() . "instances/" . 464 $a_ass->getBadgeId() . "/" . 465 floor($a_ass->getUserId() / 1000) . "/"; 466 467 ilUtil::makeDirParents($path); 468 469 $path .= $hash . ".json"; 470 471 return $path; 472 } 473 474 public function countStaticBadgeInstances(ilBadge $a_badge) 475 { 476 $path = $this->getBasePath() . "instances/" . $a_badge->getId(); 477 $cnt = 0; 478 if (is_dir($path)) { 479 $this->countStaticBadgeInstancesHelper($cnt, $path); 480 } 481 return $cnt; 482 } 483 484 protected function countStaticBadgeInstancesHelper(&$a_cnt, $a_path) 485 { 486 foreach (glob($a_path . "/*") as $item) { 487 if (is_dir($item)) { 488 $this->countStaticBadgeInstancesHelper($a_cnt, $item); 489 } elseif (substr($item, -5) == ".json") { 490 $a_cnt++; 491 } 492 } 493 } 494 495 public function getBadgePath(ilBadge $a_badge) 496 { 497 $hash = md5($a_badge->getId()); 498 499 $path = $this->getBasePath() . "badges/" . 500 floor($a_badge->getId() / 100) . "/" . 501 $hash . "/"; 502 503 ilUtil::makeDirParents($path); 504 505 return $path; 506 } 507 508 protected function prepareIssuerJson($a_url) 509 { 510 $json = new stdClass(); 511 $json->{"@context"} = "https://w3id.org/openbadges/v1"; 512 $json->type = "Issuer"; 513 $json->id = $a_url; 514 $json->name = $this->getObiOrganistation(); 515 $json->url = ILIAS_HTTP_PATH . "/"; 516 $json->email = $this->getObiContact(); 517 518 return $json; 519 } 520 521 public function getIssuerStaticUrl() 522 { 523 $path = $this->getBasePath() . "issuer/"; 524 ilUtil::makeDirParents($path); 525 $path .= "issuer.json"; 526 527 $url = ILIAS_HTTP_PATH . substr($path, 1); 528 529 if (!file_exists($path)) { 530 $json = json_encode($this->prepareIssuerJson($url)); 531 file_put_contents($path, $json); 532 } 533 534 return $url; 535 } 536 537 public function rebuildIssuerStaticUrl() 538 { 539 $path = $this->getBasePath() . "issuer/issuer.json"; 540 if (file_exists($path)) { 541 unlink($path); 542 } 543 $this->getIssuerStaticUrl(); 544 } 545 546 547 // 548 // notification 549 // 550 551 public function sendNotification(array $a_user_map, $a_parent_ref_id = null) 552 { 553 $badges = array(); 554 555 include_once "Services/Badge/classes/class.ilBadge.php"; 556 include_once "Services/Badge/classes/class.ilBadgeAssignment.php"; 557 include_once "Services/Notification/classes/class.ilSystemNotification.php"; 558 include_once "Services/Link/classes/class.ilLink.php"; 559 560 foreach ($a_user_map as $user_id => $badge_ids) { 561 $user_badges = array(); 562 563 foreach ($badge_ids as $badge_id) { 564 // making extra sure 565 if (!ilBadgeAssignment::exists($badge_id, $user_id)) { 566 continue; 567 } 568 569 if (!array_key_exists($badge_id, $badges)) { 570 $badges[$badge_id] = new ilBadge($badge_id); 571 } 572 573 $badge = $badges[$badge_id]; 574 575 $user_badges[] = $badge->getTitle(); 576 } 577 578 if (sizeof($user_badges)) { 579 // compose and send mail 580 581 $ntf = new ilSystemNotification(false); 582 $ntf->setLangModules(array("badge")); 583 584 $ntf->setRefId($a_parent_ref_id); 585 $ntf->setGotoLangId("badge_notification_parent_goto"); 586 587 // user specific language 588 $lng = $ntf->getUserLanguage($user_id); 589 590 $ntf->setIntroductionLangId("badge_notification_body"); 591 592 $ntf->addAdditionalInfo("badge_notification_badges", implode("\n", $user_badges), true); 593 594 $url = ilLink::_getLink($user_id, "usr", array(), "_bdg"); 595 $ntf->addAdditionalInfo("badge_notification_badges_goto", $url); 596 597 $ntf->setReasonLangId("badge_notification_reason"); 598 599 // force email 600 $mail = new ilMail(ANONYMOUS_USER_ID); 601 $mail->enableSOAP(false); 602 $mail->sendMail( 603 ilObjUser::_lookupEmail($user_id), 604 null, 605 null, 606 $lng->txt("badge_notification_subject"), 607 $ntf->composeAndGetMessage($user_id, null, "read", true), 608 null, 609 array("system") 610 ); 611 612 613 // osd 614 // bug #24562 615 if (ilContext::hasHTML()) { 616 $osd_params = array("badge_list" => "<br />" . implode("<br />", $user_badges)); 617 618 require_once "Services/Notifications/classes/class.ilNotificationConfig.php"; 619 $notification = new ilNotificationConfig("osd_main"); 620 $notification->setTitleVar("badge_notification_subject", array(), "badge"); 621 $notification->setShortDescriptionVar("badge_notification_osd", $osd_params, "badge"); 622 $notification->setLongDescriptionVar("", $osd_params, ""); 623 $notification->setAutoDisable(false); 624 $notification->setLink($url); 625 $notification->setIconPath(ilUtil::getImagePath('icon_bdga.svg')); 626 $notification->setValidForSeconds(ilNotificationConfig::TTL_SHORT); 627 $notification->setVisibleForSeconds(ilNotificationConfig::DEFAULT_TTS); 628 $notification->notifyByUsers(array($user_id)); 629 } 630 } 631 } 632 } 633} 634