1<?php 2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4/** 5 * Membership notification settings 6 * 7 * @author Jörg Lützenkirchen <luetzenkirchen@leifos.com> 8 * @ingroup ServicesMembership 9 */ 10class ilMembershipNotifications 11{ 12 protected $ref_id; // [int] 13 protected $mode; // [int] 14 protected $custom; // [array] 15 protected $participants; // [ilParticipants] 16 17 const VALUE_OFF = 0; 18 const VALUE_ON = 1; 19 const VALUE_BLOCKED = 2; 20 21 const MODE_SELF = 1; 22 const MODE_ALL = 2; 23 const MODE_ALL_BLOCKED = 3; 24 const MODE_CUSTOM = 4; 25 26 /** 27 * Constructor 28 * 29 * @param int $a_ref_id 30 * @return self 31 */ 32 public function __construct($a_ref_id) 33 { 34 $this->ref_id = (int) $a_ref_id; 35 $this->custom = array(); 36 $this->setMode(self::MODE_SELF); 37 38 if ($this->ref_id) { 39 $this->read(); 40 } 41 } 42 43 /** 44 * Is feature active? 45 * 46 * @return bool 47 */ 48 public static function isActive() 49 { 50 global $DIC; 51 52 $ilSetting = $DIC['ilSetting']; 53 54 if (!$ilSetting->get("block_activated_news") || !$ilSetting->get("crsgrp_ntf")) { 55 return false; 56 } 57 return true; 58 } 59 60 public static function isActiveForRefId(int $ref_id) 61 { 62 if (!self::isActive()) { 63 return false; 64 } 65 // see #31471, #30687, and ilNewsItem::getNewsForRefId 66 $obj_id = ilObject::_lookupObjId($ref_id); 67 if (!ilContainer::_lookupContainerSetting( 68 $obj_id, 69 'cont_use_news', 70 true 71 ) || ( 72 !ilContainer::_lookupContainerSetting( 73 $obj_id, 74 'cont_show_news', 75 true 76 ) && !ilContainer::_lookupContainerSetting( 77 $obj_id, 78 'news_timeline' 79 ) 80 )) { 81 return false; 82 } 83 return true; 84 } 85 86 /** 87 * Read from DB 88 */ 89 protected function read() 90 { 91 global $DIC; 92 93 $ilDB = $DIC['ilDB']; 94 95 $set = $ilDB->query("SELECT nmode mode" . 96 " FROM member_noti" . 97 " WHERE ref_id = " . $ilDB->quote($this->ref_id, "integer")); 98 if ($ilDB->numRows($set)) { 99 $row = $ilDB->fetchAssoc($set); 100 $this->setMode($row["mode"]); 101 102 if ($row["mode"] == self::MODE_CUSTOM) { 103 $set = $ilDB->query("SELECT *" . 104 " FROM member_noti_user" . 105 " WHERE ref_id = " . $ilDB->quote($this->ref_id, "integer")); 106 while ($row = $ilDB->fetchAssoc($set)) { 107 $this->custom[$row["user_id"]] = $row["status"]; 108 } 109 } 110 } 111 } 112 113 114 // 115 // MODE 116 // 117 118 /** 119 * Get mode 120 * 121 * @return int 122 */ 123 public function getMode() 124 { 125 return $this->mode; 126 } 127 128 /** 129 * Set mode 130 * 131 * @param int $a_value 132 */ 133 protected function setMode($a_value) 134 { 135 if ($this->isValidMode($a_value)) { 136 $this->mode = $a_value; 137 } 138 } 139 140 /** 141 * Is given mode valid? 142 * 143 * @param int $a_value 144 * @return bool 145 */ 146 protected function isValidMode($a_value) 147 { 148 $valid = array( 149 self::MODE_SELF 150 ,self::MODE_ALL 151 ,self::MODE_ALL_BLOCKED 152 // ,self::MODE_CUSTOM currently used in forum 153 ); 154 return in_array($a_value, $valid); 155 } 156 157 /** 158 * Switch mode for object 159 * 160 * @param int $a_new_mode 161 * @return bool 162 */ 163 public function switchMode($a_new_mode) 164 { 165 global $DIC; 166 167 $ilDB = $DIC['ilDB']; 168 169 if (!$this->ref_id) { 170 return; 171 } 172 173 if ($this->mode && 174 $this->mode != $a_new_mode && 175 $this->isValidMode($a_new_mode)) { 176 $ilDB->manipulate("DELETE FROM member_noti" . 177 " WHERE ref_id = " . $ilDB->quote($this->ref_id, "integer")); 178 179 // no custom data 180 if ($a_new_mode != self::MODE_CUSTOM) { 181 $ilDB->manipulate("DELETE FROM member_noti_user" . 182 " WHERE ref_id = " . $ilDB->quote($this->ref_id, "integer")); 183 } 184 185 // mode self is default 186 if ($a_new_mode != self::MODE_SELF) { 187 $ilDB->insert("member_noti", array( 188 "ref_id" => array("integer", $this->ref_id), 189 "nmode" => array("integer", $a_new_mode) 190 )); 191 } 192 193 // remove all user settings (all active is preset, optional opt out) 194 if ($a_new_mode == self::MODE_ALL) { 195 $ilDB->manipulate("DELETE FROM usr_pref" . 196 " WHERE " . $ilDB->like("keyword", "text", "grpcrs_ntf_" . $this->ref_id)); 197 } 198 } 199 200 $this->setMode($a_new_mode); 201 } 202 203 204 // 205 // ACTIVE USERS 206 // 207 208 /** 209 * Init participants for current object 210 * 211 * @return ilParticipants 212 */ 213 protected function getParticipants() 214 { 215 global $DIC; 216 217 $tree = $DIC['tree']; 218 219 if ($this->participants === null) { 220 $this->participants = false; 221 222 $grp_ref_id = $tree->checkForParentType($this->ref_id, "grp"); 223 if ($grp_ref_id) { 224 include_once "Modules/Group/classes/class.ilGroupParticipants.php"; 225 $this->participants = ilGroupParticipants::_getInstanceByObjId(ilObject::_lookupObjId($grp_ref_id)); 226 } 227 228 if (!$this->participants) { 229 $crs_ref_id = $tree->checkForParentType($this->ref_id, "crs"); 230 if ($crs_ref_id) { 231 include_once "Modules/Course/classes/class.ilCourseParticipants.php"; 232 $this->participants = ilCourseParticipants::_getInstanceByObjId(ilObject::_lookupObjId($crs_ref_id)); 233 } 234 } 235 } 236 237 return $this->participants; 238 } 239 240 /** 241 * Get active notifications for current object 242 * 243 * @return array 244 */ 245 public function getActiveUsers() 246 { 247 global $DIC; 248 249 $ilDB = $DIC->database(); 250 251 $users = $all = array(); 252 253 $part_obj = $this->getParticipants(); 254 if ($part_obj) { 255 $all = $part_obj->getParticipants(); 256 } 257 if (!sizeof($all)) { 258 return array(); 259 } 260 261 switch ($this->getMode()) { 262 // users decide themselves 263 case self::MODE_SELF: 264 $set = $ilDB->query("SELECT usr_id" . 265 " FROM usr_pref" . 266 " WHERE keyword = " . $ilDB->quote("grpcrs_ntf_".$this->ref_id, "text") . 267 " AND value = " . $ilDB->quote(self::VALUE_ON, "text")); 268 while ($row = $ilDB->fetchAssoc($set)) { 269 $users[] = $row["usr_id"]; 270 } 271 break; 272 273 // all members, mind opt-out 274 case self::MODE_ALL: 275 // users who did opt-out 276 $inactive = array(); 277 $set = $ilDB->query("SELECT usr_id" . 278 " FROM usr_pref" . 279 " WHERE keyword = " . $ilDB->quote("grpcrs_ntf_".$this->ref_id, "text") . 280 " AND value = " . $ilDB->quote(self::VALUE_OFF, "text")); 281 while ($row = $ilDB->fetchAssoc($set)) { 282 $inactive[] = $row["usr_id"]; 283 } 284 $users = array_diff($all, $inactive); 285 break; 286 287 // all members, no opt-out 288 case self::MODE_ALL_BLOCKED: 289 $users = $all; 290 break; 291 292 // custom settings 293 case self::MODE_CUSTOM: 294 foreach ($this->custom as $user_id => $status) { 295 if ($status != self::VALUE_OFF) { 296 $users[] = $user_id; 297 } 298 } 299 break; 300 } 301 302 // only valid participants 303 return array_intersect($all, $users); 304 } 305 306 307 // 308 // USER STATUS 309 // 310 311 /** 312 * Activate notification for user 313 * 314 * @param int $a_user_id 315 * @return bool 316 */ 317 public function activateUser($a_user_id = null) 318 { 319 return $this->toggleUser(true, $a_user_id); 320 } 321 322 /** 323 * Deactivate notification for user 324 * 325 * @param int $a_user_id 326 * @return bool 327 */ 328 public function deactivateUser($a_user_id = null) 329 { 330 return $this->toggleUser(false, $a_user_id); 331 } 332 333 /** 334 * Init user instance 335 * 336 * @param int $a_user_id 337 * @return ilUser 338 */ 339 protected function getUser($a_user_id = null) 340 { 341 global $DIC; 342 343 $ilUser = $DIC['ilUser']; 344 345 if ($a_user_id === null || 346 $a_user_id == $ilUser->getId()) { 347 $user = $ilUser; 348 } else { 349 $user = new ilUser($a_user_id); 350 } 351 352 if ($user->getId() && 353 $user->getId() != ANONYMOUS_USER_ID) { 354 return $user; 355 } 356 } 357 358 /** 359 * Toggle user notification status 360 * 361 * @param bool $a_status 362 * @param int $a_user_id 363 * @return boolean 364 */ 365 protected function toggleUser($a_status, $a_user_id = null) 366 { 367 global $DIC; 368 369 $ilDB = $DIC['ilDB']; 370 371 if (!self::isActive()) { 372 return; 373 } 374 375 switch ($this->getMode()) { 376 case self::MODE_ALL: 377 case self::MODE_SELF: 378 // current user! 379 $user = $this->getUser(); 380 if ($user) { 381 // blocked value not supported in user pref! 382 $user->setPref("grpcrs_ntf_" . $this->ref_id, (int) (bool) $a_status); 383 $user->writePrefs(); 384 return true; 385 } 386 break; 387 388 case self::MODE_CUSTOM: 389 $user = $this->getUser($a_user_id); 390 if ($user) { 391 $user_id = $user->getId(); 392 393 // did status change at all? 394 if (!array_key_exists($user_id, $this->custom) || 395 $this->custom[$user_id != $a_status]) { 396 $this->custom[$user_id] = $a_status; 397 398 $ilDB->replace( 399 "member_noti_user", 400 array( 401 "ref_id" => array("integer", $this->ref_id), 402 "user_id" => array("integer", $user_id), 403 ), 404 array( 405 "status" => array("integer", $a_status) 406 ) 407 ); 408 } 409 return true; 410 } 411 break; 412 413 case self::MODE_ALL_BLOCKED: 414 // no individual settings 415 break; 416 } 417 418 return false; 419 } 420 421 422 // 423 // CURRENT USER 424 // 425 426 /** 427 * Get user notification status 428 * 429 * @return boolean 430 */ 431 public function isCurrentUserActive() 432 { 433 global $DIC; 434 435 $ilUser = $DIC['ilUser']; 436 437 return in_array($ilUser->getId(), $this->getActiveUsers()); 438 } 439 440 /** 441 * Can user change notification status? 442 * 443 * @return boolean 444 */ 445 public function canCurrentUserEdit() 446 { 447 global $DIC; 448 449 $ilUser = $DIC['ilUser']; 450 451 $user_id = $ilUser->getId(); 452 if ($user_id == ANONYMOUS_USER_ID) { 453 return false; 454 } 455 456 switch ($this->getMode()) { 457 case self::MODE_SELF: 458 case self::MODE_ALL: 459 return true; 460 461 case self::MODE_ALL_BLOCKED: 462 return false; 463 464 case self::MODE_CUSTOM: 465 return !(array_key_exists($user_id, $this->custom) && 466 $this->custom[$user_id] == self::VALUE_BLOCKED); 467 } 468 } 469 470 471 // 472 // CRON 473 // 474 475 /** 476 * Get active notifications for all objects 477 * 478 * @return array 479 */ 480 public static function getActiveUsersforAllObjects() 481 { 482 global $DIC; 483 484 $ilDB = $DIC['ilDB']; 485 $tree = $DIC['tree']; 486 487 $log = ilLoggerFactory::getLogger("mmbr"); 488 489 490 $res = array(); 491 492 if (self::isActive()) { 493 $objects = array(); 494 495 // user-preference data (MODE_SELF) 496 $log->debug("read usr_pref"); 497 $set = $ilDB->query("SELECT DISTINCT(keyword) keyword" . 498 " FROM usr_pref" . 499 " WHERE " . $ilDB->like("keyword", "text", "grpcrs_ntf_%") . 500 " AND value = " . $ilDB->quote("1", "text")); 501 while ($row = $ilDB->fetchAssoc($set)) { 502 $ref_id = substr($row["keyword"], 11); 503 $objects[(int) $ref_id] = (int) $ref_id; 504 } 505 506 // all other modes 507 $log->debug("read member_noti"); 508 $set = $ilDB->query("SELECT ref_id" . 509 " FROM member_noti"); 510 while ($row = $ilDB->fetchAssoc($set)) { 511 $objects[(int) $row["ref_id"]] = (int) $row["ref_id"]; 512 } 513 514 // this might be slow but it is to be used in CRON JOB ONLY! 515 foreach (array_unique($objects) as $ref_id) { 516 // :TODO: enough checking? 517 if (!$tree->isDeleted($ref_id)) { 518 $log->debug("get active users"); 519 $noti = new self($ref_id); 520 $active = $noti->getActiveUsers(); 521 if (sizeof($active)) { 522 $res[$ref_id] = $active; 523 } 524 } 525 } 526 } 527 528 return $res; 529 } 530 531 532 // 533 // (OBJECT SETTINGS) FORM 534 // 535 536 /** 537 * Add notification settings to form 538 * 539 * @param int $a_ref_id 540 * @param ilPropertyFormGUI $a_form 541 * @param ilPropertyFormGUI $a_input 542 */ 543 public static function addToSettingsForm($a_ref_id, ilPropertyFormGUI $a_form = null, ilFormPropertyGUI $a_input = null) 544 { 545 global $DIC; 546 547 $lng = $DIC['lng']; 548 549 if (self::isActive() && 550 $a_ref_id) { 551 $lng->loadLanguageModule("membership"); 552 $noti = new self($a_ref_id); 553 554 $force_noti = new ilRadioGroupInputGUI($lng->txt("mem_force_notification"), "force_noti"); 555 $force_noti->setRequired(true); 556 if ($a_form) { 557 $a_form->addItem($force_noti); 558 } else { 559 $a_input->addSubItem($force_noti); 560 } 561 562 if ($noti->isValidMode(self::MODE_SELF)) { 563 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_self"), self::MODE_SELF); 564 $force_noti->addOption($option); 565 } 566 if ($noti->isValidMode(self::MODE_ALL_BLOCKED)) { 567 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_blocked"), self::MODE_ALL_BLOCKED); 568 $force_noti->addOption($option); 569 570 if ($noti->isValidMode(self::MODE_ALL)) { 571 $changeable = new ilCheckboxInputGUI($lng->txt("mem_force_notification_mode_all_sub_blocked"), "force_noti_allblk"); 572 $option->addSubItem($changeable); 573 } 574 } elseif ($noti->isValidMode(self::MODE_ALL)) { 575 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_all"), self::MODE_ALL); 576 $force_noti->addOption($option); 577 } 578 /* not supported in GUI 579 if($noti->isValidMode(self::MODE_CUSTOM)) 580 { 581 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_custom"), self::MODE_CUSTOM); 582 $option->setInfo($lng->txt("mem_force_notification_mode_custom_info")); 583 $force_noti->addOption($option); 584 } 585 */ 586 587 // set current mode 588 $current_mode = $noti->getMode(); 589 $has_changeable_cb = ($noti->isValidMode(self::MODE_ALL_BLOCKED) && 590 $noti->isValidMode(self::MODE_ALL)); 591 if (!$has_changeable_cb) { 592 $force_noti->setValue($current_mode); 593 } else { 594 switch ($current_mode) { 595 case self::MODE_SELF: 596 $force_noti->setValue($current_mode); 597 $changeable->setChecked(true); // checked as "default" on selection of parent 598 break; 599 600 case self::MODE_ALL_BLOCKED: 601 $force_noti->setValue($current_mode); 602 break; 603 604 case self::MODE_ALL: 605 $force_noti->setValue(self::MODE_ALL_BLOCKED); 606 $changeable->setChecked(true); 607 break; 608 } 609 } 610 } 611 } 612 613 /** 614 * Import notification settings from form 615 * 616 * @param int $a_ref_id 617 * @param ilPropertyFormGUI $a_form 618 */ 619 public static function importFromForm($a_ref_id, ilPropertyFormGUI $a_form = null) 620 { 621 if (self::isActive() && 622 $a_ref_id) { 623 $noti = new self($a_ref_id); 624 $has_changeable_cb = ($noti->isValidMode(self::MODE_ALL_BLOCKED) && 625 $noti->isValidMode(self::MODE_ALL)); 626 $changeable = null; 627 if (!$a_form) { 628 $mode = (int) $_POST["force_noti"]; 629 if ($has_changeable_cb) { 630 $changeable = (int) $_POST["force_noti_allblk"]; 631 } 632 } else { 633 $mode = $a_form->getInput("force_noti"); 634 if ($has_changeable_cb) { 635 $changeable = $a_form->getInput("force_noti_allblk"); 636 } 637 } 638 // checkbox (all) is subitem of all_blocked 639 if ($changeable && 640 $mode == self::MODE_ALL_BLOCKED) { 641 $mode = self::MODE_ALL; 642 } 643 $noti->switchMode($mode); 644 } 645 } 646 647 /** 648 * Clone notification object settings 649 * 650 * @param $new_ref_id 651 */ 652 public function cloneSettings($new_ref_id) 653 { 654 global $ilDB; 655 656 $set = $ilDB->queryF( 657 "SELECT * FROM member_noti " . 658 " WHERE ref_id = %s ", 659 array("integer"), 660 array($this->ref_id) 661 ); 662 while ($rec = $ilDB->fetchAssoc($set)) { 663 $ilDB->insert("member_noti", array( 664 "ref_id" => array("integer", $new_ref_id), 665 "nmode" => array("integer", $rec["nmode"]) 666 )); 667 } 668 } 669} 670