Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Forum subscription manager. 19 * 20 * @package mod_forum 21 * @copyright 2014 Andrew Nicols <andrew@nicols.co.uk> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_forum; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Forum subscription manager. 31 * 32 * @copyright 2014 Andrew Nicols <andrew@nicols.co.uk> 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class subscriptions { 36 37 /** 38 * The status value for an unsubscribed discussion. 39 * 40 * @var int 41 */ 42 const FORUM_DISCUSSION_UNSUBSCRIBED = -1; 43 44 /** 45 * The subscription cache for forums. 46 * 47 * The first level key is the user ID 48 * The second level is the forum ID 49 * The Value then is bool for subscribed of not. 50 * 51 * @var array[] An array of arrays. 52 */ 53 protected static $forumcache = array(); 54 55 /** 56 * The list of forums which have been wholly retrieved for the forum subscription cache. 57 * 58 * This allows for prior caching of an entire forum to reduce the 59 * number of DB queries in a subscription check loop. 60 * 61 * @var bool[] 62 */ 63 protected static $fetchedforums = array(); 64 65 /** 66 * The subscription cache for forum discussions. 67 * 68 * The first level key is the user ID 69 * The second level is the forum ID 70 * The third level key is the discussion ID 71 * The value is then the users preference (int) 72 * 73 * @var array[] 74 */ 75 protected static $forumdiscussioncache = array(); 76 77 /** 78 * The list of forums which have been wholly retrieved for the forum discussion subscription cache. 79 * 80 * This allows for prior caching of an entire forum to reduce the 81 * number of DB queries in a subscription check loop. 82 * 83 * @var bool[] 84 */ 85 protected static $discussionfetchedforums = array(); 86 87 /** 88 * Whether a user is subscribed to this forum, or a discussion within 89 * the forum. 90 * 91 * If a discussion is specified, then report whether the user is 92 * subscribed to posts to this particular discussion, taking into 93 * account the forum preference. 94 * 95 * If it is not specified then only the forum preference is considered. 96 * 97 * @param int $userid The user ID 98 * @param \stdClass $forum The record of the forum to test 99 * @param int $discussionid The ID of the discussion to check 100 * @param $cm The coursemodule record. If not supplied, this will be calculated using get_fast_modinfo instead. 101 * @return boolean 102 */ 103 public static function is_subscribed($userid, $forum, $discussionid = null, $cm = null) { 104 // If forum is force subscribed and has allowforcesubscribe, then user is subscribed. 105 if (self::is_forcesubscribed($forum)) { 106 if (!$cm) { 107 $cm = get_fast_modinfo($forum->course)->instances['forum'][$forum->id]; 108 } 109 if (has_capability('mod/forum:allowforcesubscribe', \context_module::instance($cm->id), $userid)) { 110 return true; 111 } 112 } 113 114 if ($discussionid === null) { 115 return self::is_subscribed_to_forum($userid, $forum); 116 } 117 118 $subscriptions = self::fetch_discussion_subscription($forum->id, $userid); 119 120 // Check whether there is a record for this discussion subscription. 121 if (isset($subscriptions[$discussionid])) { 122 return ($subscriptions[$discussionid] != self::FORUM_DISCUSSION_UNSUBSCRIBED); 123 } 124 125 return self::is_subscribed_to_forum($userid, $forum); 126 } 127 128 /** 129 * Whether a user is subscribed to this forum. 130 * 131 * @param int $userid The user ID 132 * @param \stdClass $forum The record of the forum to test 133 * @return boolean 134 */ 135 protected static function is_subscribed_to_forum($userid, $forum) { 136 return self::fetch_subscription_cache($forum->id, $userid); 137 } 138 139 /** 140 * Helper to determine whether a forum has it's subscription mode set 141 * to forced subscription. 142 * 143 * @param \stdClass $forum The record of the forum to test 144 * @return bool 145 */ 146 public static function is_forcesubscribed($forum) { 147 return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE); 148 } 149 150 /** 151 * Helper to determine whether a forum has it's subscription mode set to disabled. 152 * 153 * @param \stdClass $forum The record of the forum to test 154 * @return bool 155 */ 156 public static function subscription_disabled($forum) { 157 return ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE); 158 } 159 160 /** 161 * Helper to determine whether the specified forum can be subscribed to. 162 * 163 * @param \stdClass $forum The record of the forum to test 164 * @return bool 165 */ 166 public static function is_subscribable($forum) { 167 return (isloggedin() && !isguestuser() && 168 !\mod_forum\subscriptions::is_forcesubscribed($forum) && 169 !\mod_forum\subscriptions::subscription_disabled($forum)); 170 } 171 172 /** 173 * Set the forum subscription mode. 174 * 175 * By default when called without options, this is set to FORUM_FORCESUBSCRIBE. 176 * 177 * @param \stdClass $forum The record of the forum to set 178 * @param int $status The new subscription state 179 * @return bool true 180 * @throws dml_exception A DML specific exception is thrown for any errors. 181 */ 182 public static function set_subscription_mode($forum, $status = FORUM_FORCESUBSCRIBE): bool { 183 global $DB; 184 185 if (is_numeric($forum)) { 186 debugging(__METHOD__.': Argument #1 ($forum) must be a stdClass record of a forum', DEBUG_DEVELOPER); 187 188 $forum = $DB->get_record("forum", ["id" => $forum], '*', MUST_EXIST); 189 } 190 191 $DB->set_field("forum", "forcesubscribe", $status, ["id" => $forum->id]); 192 193 if ($forum->forcesubscribe != $status) { 194 // Trigger event if subscription mode has been changed. 195 $event = \mod_forum\event\subscription_mode_updated::create([ 196 "context" => forum_get_context($forum->id), 197 "objectid" => $forum->id, 198 "other" => ["oldvalue" => $forum->forcesubscribe, "newvalue" => $status], 199 ]); 200 $event->add_record_snapshot("forum", $forum); 201 $event->trigger(); 202 } 203 204 return true; 205 } 206 207 /** 208 * Returns the current subscription mode for the forum. 209 * 210 * @param \stdClass $forum The record of the forum to set 211 * @return int The forum subscription mode 212 */ 213 public static function get_subscription_mode($forum) { 214 return $forum->forcesubscribe; 215 } 216 217 /** 218 * Returns an array of forums that the current user is subscribed to and is allowed to unsubscribe from 219 * 220 * @return array An array of unsubscribable forums 221 */ 222 public static function get_unsubscribable_forums() { 223 global $USER, $DB; 224 225 // Get courses that $USER is enrolled in and can see. 226 $courses = enrol_get_my_courses(); 227 if (empty($courses)) { 228 return array(); 229 } 230 231 $courseids = array(); 232 foreach($courses as $course) { 233 $courseids[] = $course->id; 234 } 235 list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'c'); 236 237 // Get all forums from the user's courses that they are subscribed to and which are not set to forced. 238 // It is possible for users to be subscribed to a forum in subscription disallowed mode so they must be listed 239 // here so that that can be unsubscribed from. 240 $sql = "SELECT f.id, cm.id as cm, cm.visible, f.course 241 FROM {forum} f 242 JOIN {course_modules} cm ON cm.instance = f.id 243 JOIN {modules} m ON m.name = :modulename AND m.id = cm.module 244 LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid) 245 WHERE f.forcesubscribe <> :forcesubscribe 246 AND fs.id IS NOT NULL 247 AND cm.course 248 $coursesql"; 249 $params = array_merge($courseparams, array( 250 'modulename'=>'forum', 251 'userid' => $USER->id, 252 'forcesubscribe' => FORUM_FORCESUBSCRIBE, 253 )); 254 $forums = $DB->get_recordset_sql($sql, $params); 255 256 $unsubscribableforums = array(); 257 foreach($forums as $forum) { 258 if (empty($forum->visible)) { 259 // The forum is hidden - check if the user can view the forum. 260 $context = \context_module::instance($forum->cm); 261 if (!has_capability('moodle/course:viewhiddenactivities', $context)) { 262 // The user can't see the hidden forum to cannot unsubscribe. 263 continue; 264 } 265 } 266 267 $unsubscribableforums[] = $forum; 268 } 269 $forums->close(); 270 271 return $unsubscribableforums; 272 } 273 274 /** 275 * Get the list of potential subscribers to a forum. 276 * 277 * @param context_module $context the forum context. 278 * @param integer $groupid the id of a group, or 0 for all groups. 279 * @param string $fields the list of fields to return for each user. As for get_users_by_capability. 280 * @param string $sort sort order. As for get_users_by_capability. 281 * @return array list of users. 282 */ 283 public static function get_potential_subscribers($context, $groupid, $fields, $sort = '') { 284 global $DB; 285 286 // Only active enrolled users or everybody on the frontpage. 287 list($esql, $params) = get_enrolled_sql($context, 'mod/forum:allowforcesubscribe', $groupid, true); 288 if (!$sort) { 289 list($sort, $sortparams) = users_order_by_sql('u'); 290 $params = array_merge($params, $sortparams); 291 } 292 293 $sql = "SELECT $fields 294 FROM {user} u 295 JOIN ($esql) je ON je.id = u.id 296 WHERE u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1 297 ORDER BY $sort"; 298 299 return $DB->get_records_sql($sql, $params); 300 } 301 302 /** 303 * Fetch the forum subscription data for the specified userid and forum. 304 * 305 * @param int $forumid The forum to retrieve a cache for 306 * @param int $userid The user ID 307 * @return boolean 308 */ 309 public static function fetch_subscription_cache($forumid, $userid) { 310 if (isset(self::$forumcache[$userid]) && isset(self::$forumcache[$userid][$forumid])) { 311 return self::$forumcache[$userid][$forumid]; 312 } 313 self::fill_subscription_cache($forumid, $userid); 314 315 if (!isset(self::$forumcache[$userid]) || !isset(self::$forumcache[$userid][$forumid])) { 316 return false; 317 } 318 319 return self::$forumcache[$userid][$forumid]; 320 } 321 322 /** 323 * Fill the forum subscription data for the specified userid and forum. 324 * 325 * If the userid is not specified, then all subscription data for that forum is fetched in a single query and used 326 * for subsequent lookups without requiring further database queries. 327 * 328 * @param int $forumid The forum to retrieve a cache for 329 * @param int $userid The user ID 330 * @return void 331 */ 332 public static function fill_subscription_cache($forumid, $userid = null) { 333 global $DB; 334 335 if (!isset(self::$fetchedforums[$forumid])) { 336 // This forum has not been fetched as a whole. 337 if (isset($userid)) { 338 if (!isset(self::$forumcache[$userid])) { 339 self::$forumcache[$userid] = array(); 340 } 341 342 if (!isset(self::$forumcache[$userid][$forumid])) { 343 if ($DB->record_exists('forum_subscriptions', array( 344 'userid' => $userid, 345 'forum' => $forumid, 346 ))) { 347 self::$forumcache[$userid][$forumid] = true; 348 } else { 349 self::$forumcache[$userid][$forumid] = false; 350 } 351 } 352 } else { 353 $subscriptions = $DB->get_recordset('forum_subscriptions', array( 354 'forum' => $forumid, 355 ), '', 'id, userid'); 356 foreach ($subscriptions as $id => $data) { 357 if (!isset(self::$forumcache[$data->userid])) { 358 self::$forumcache[$data->userid] = array(); 359 } 360 self::$forumcache[$data->userid][$forumid] = true; 361 } 362 self::$fetchedforums[$forumid] = true; 363 $subscriptions->close(); 364 } 365 } 366 } 367 368 /** 369 * Fill the forum subscription data for all forums that the specified userid can subscribe to in the specified course. 370 * 371 * @param int $courseid The course to retrieve a cache for 372 * @param int $userid The user ID 373 * @return void 374 */ 375 public static function fill_subscription_cache_for_course($courseid, $userid) { 376 global $DB; 377 378 if (!isset(self::$forumcache[$userid])) { 379 self::$forumcache[$userid] = array(); 380 } 381 382 $sql = "SELECT 383 f.id AS forumid, 384 s.id AS subscriptionid 385 FROM {forum} f 386 LEFT JOIN {forum_subscriptions} s ON (s.forum = f.id AND s.userid = :userid) 387 WHERE f.course = :course 388 AND f.forcesubscribe <> :subscriptionforced"; 389 390 $subscriptions = $DB->get_recordset_sql($sql, array( 391 'course' => $courseid, 392 'userid' => $userid, 393 'subscriptionforced' => FORUM_FORCESUBSCRIBE, 394 )); 395 396 foreach ($subscriptions as $id => $data) { 397 self::$forumcache[$userid][$id] = !empty($data->subscriptionid); 398 } 399 $subscriptions->close(); 400 } 401 402 /** 403 * Returns a list of user objects who are subscribed to this forum. 404 * 405 * @param stdClass $forum The forum record. 406 * @param int $groupid The group id if restricting subscriptions to a group of users, or 0 for all. 407 * @param context_module $context the forum context, to save re-fetching it where possible. 408 * @param string $fields requested user fields (with "u." table prefix). 409 * @param boolean $includediscussionsubscriptions Whether to take discussion subscriptions and unsubscriptions into consideration. 410 * @return array list of users. 411 */ 412 public static function fetch_subscribed_users($forum, $groupid = 0, $context = null, $fields = null, 413 $includediscussionsubscriptions = false) { 414 global $CFG, $DB; 415 416 if (empty($fields)) { 417 $userfieldsapi = \core_user\fields::for_name(); 418 $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects; 419 $fields ="u.id, 420 u.username, 421 $allnames, 422 u.maildisplay, 423 u.mailformat, 424 u.maildigest, 425 u.imagealt, 426 u.email, 427 u.emailstop, 428 u.city, 429 u.country, 430 u.lastaccess, 431 u.lastlogin, 432 u.picture, 433 u.timezone, 434 u.theme, 435 u.lang, 436 u.trackforums, 437 u.mnethostid"; 438 } 439 440 // Retrieve the forum context if it wasn't specified. 441 $context = forum_get_context($forum->id, $context); 442 if (self::is_forcesubscribed($forum)) { 443 $results = self::get_potential_subscribers($context, $groupid, $fields); 444 445 } else { 446 // Only active enrolled users or everybody on the frontpage. 447 list($esql, $params) = get_enrolled_sql($context, '', $groupid, true); 448 $params['forumid'] = $forum->id; 449 450 list($sort, $sortparams) = users_order_by_sql('u'); 451 $params = array_merge($params, $sortparams); 452 453 if ($includediscussionsubscriptions) { 454 $params['sforumid'] = $forum->id; 455 $params['dsforumid'] = $forum->id; 456 $params['unsubscribed'] = self::FORUM_DISCUSSION_UNSUBSCRIBED; 457 458 $sql = "SELECT $fields 459 FROM ( 460 SELECT userid FROM {forum_subscriptions} s 461 WHERE 462 s.forum = :sforumid 463 UNION 464 SELECT userid FROM {forum_discussion_subs} ds 465 WHERE 466 ds.forum = :dsforumid AND ds.preference <> :unsubscribed 467 ) subscriptions 468 JOIN {user} u ON u.id = subscriptions.userid 469 JOIN ($esql) je ON je.id = u.id 470 WHERE u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1 471 ORDER BY $sort"; 472 473 } else { 474 $sql = "SELECT $fields 475 FROM {user} u 476 JOIN ($esql) je ON je.id = u.id 477 JOIN {forum_subscriptions} s ON s.userid = u.id 478 WHERE 479 s.forum = :forumid AND u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1 480 ORDER BY $sort"; 481 } 482 $results = $DB->get_records_sql($sql, $params); 483 } 484 485 // Guest user should never be subscribed to a forum. 486 unset($results[$CFG->siteguest]); 487 488 // Apply the activity module availability resetrictions. 489 $cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course); 490 $modinfo = get_fast_modinfo($forum->course); 491 $info = new \core_availability\info_module($modinfo->get_cm($cm->id)); 492 $results = $info->filter_user_list($results); 493 494 return $results; 495 } 496 497 /** 498 * Retrieve the discussion subscription data for the specified userid and forum. 499 * 500 * This is returned as an array of discussions for that forum which contain the preference in a stdClass. 501 * 502 * @param int $forumid The forum to retrieve a cache for 503 * @param int $userid The user ID 504 * @return array of stdClass objects with one per discussion in the forum. 505 */ 506 public static function fetch_discussion_subscription($forumid, $userid = null) { 507 self::fill_discussion_subscription_cache($forumid, $userid); 508 509 if (!isset(self::$forumdiscussioncache[$userid]) || !isset(self::$forumdiscussioncache[$userid][$forumid])) { 510 return array(); 511 } 512 513 return self::$forumdiscussioncache[$userid][$forumid]; 514 } 515 516 /** 517 * Fill the discussion subscription data for the specified userid and forum. 518 * 519 * If the userid is not specified, then all discussion subscription data for that forum is fetched in a single query 520 * and used for subsequent lookups without requiring further database queries. 521 * 522 * @param int $forumid The forum to retrieve a cache for 523 * @param int $userid The user ID 524 * @return void 525 */ 526 public static function fill_discussion_subscription_cache($forumid, $userid = null) { 527 global $DB; 528 529 if (!isset(self::$discussionfetchedforums[$forumid])) { 530 // This forum hasn't been fetched as a whole yet. 531 if (isset($userid)) { 532 if (!isset(self::$forumdiscussioncache[$userid])) { 533 self::$forumdiscussioncache[$userid] = array(); 534 } 535 536 if (!isset(self::$forumdiscussioncache[$userid][$forumid])) { 537 $subscriptions = $DB->get_recordset('forum_discussion_subs', array( 538 'userid' => $userid, 539 'forum' => $forumid, 540 ), null, 'id, discussion, preference'); 541 542 self::$forumdiscussioncache[$userid][$forumid] = array(); 543 foreach ($subscriptions as $id => $data) { 544 self::add_to_discussion_cache($forumid, $userid, $data->discussion, $data->preference); 545 } 546 547 $subscriptions->close(); 548 } 549 } else { 550 $subscriptions = $DB->get_recordset('forum_discussion_subs', array( 551 'forum' => $forumid, 552 ), null, 'id, userid, discussion, preference'); 553 foreach ($subscriptions as $id => $data) { 554 self::add_to_discussion_cache($forumid, $data->userid, $data->discussion, $data->preference); 555 } 556 self::$discussionfetchedforums[$forumid] = true; 557 $subscriptions->close(); 558 } 559 } 560 } 561 562 /** 563 * Add the specified discussion and user preference to the discussion 564 * subscription cache. 565 * 566 * @param int $forumid The ID of the forum that this preference belongs to 567 * @param int $userid The ID of the user that this preference belongs to 568 * @param int $discussion The ID of the discussion that this preference relates to 569 * @param int $preference The preference to store 570 */ 571 protected static function add_to_discussion_cache($forumid, $userid, $discussion, $preference) { 572 if (!isset(self::$forumdiscussioncache[$userid])) { 573 self::$forumdiscussioncache[$userid] = array(); 574 } 575 576 if (!isset(self::$forumdiscussioncache[$userid][$forumid])) { 577 self::$forumdiscussioncache[$userid][$forumid] = array(); 578 } 579 580 self::$forumdiscussioncache[$userid][$forumid][$discussion] = $preference; 581 } 582 583 /** 584 * Reset the discussion cache. 585 * 586 * This cache is used to reduce the number of database queries when 587 * checking forum discussion subscription states. 588 */ 589 public static function reset_discussion_cache() { 590 self::$forumdiscussioncache = array(); 591 self::$discussionfetchedforums = array(); 592 } 593 594 /** 595 * Reset the forum cache. 596 * 597 * This cache is used to reduce the number of database queries when 598 * checking forum subscription states. 599 */ 600 public static function reset_forum_cache() { 601 self::$forumcache = array(); 602 self::$fetchedforums = array(); 603 } 604 605 /** 606 * Adds user to the subscriber list. 607 * 608 * @param int $userid The ID of the user to subscribe 609 * @param \stdClass $forum The forum record for this forum. 610 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current 611 * module set in page. 612 * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether 613 * discussion subscriptions are removed too. 614 * @return bool|int Returns true if the user is already subscribed, or the forum_subscriptions ID if the user was 615 * successfully subscribed. 616 */ 617 public static function subscribe_user($userid, $forum, $context = null, $userrequest = false) { 618 global $DB; 619 620 if (self::is_subscribed($userid, $forum)) { 621 return true; 622 } 623 624 $sub = new \stdClass(); 625 $sub->userid = $userid; 626 $sub->forum = $forum->id; 627 628 $result = $DB->insert_record("forum_subscriptions", $sub); 629 630 if ($userrequest) { 631 $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', array('userid' => $userid, 'forum' => $forum->id)); 632 $DB->delete_records_select('forum_discussion_subs', 633 'userid = :userid AND forum = :forumid AND preference <> :preference', array( 634 'userid' => $userid, 635 'forumid' => $forum->id, 636 'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED, 637 )); 638 639 // Reset the subscription caches for this forum. 640 // We know that the there were previously entries and there aren't any more. 641 if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) { 642 foreach (self::$forumdiscussioncache[$userid][$forum->id] as $discussionid => $preference) { 643 if ($preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) { 644 unset(self::$forumdiscussioncache[$userid][$forum->id][$discussionid]); 645 } 646 } 647 } 648 } 649 650 // Reset the cache for this forum. 651 self::$forumcache[$userid][$forum->id] = true; 652 653 $context = forum_get_context($forum->id, $context); 654 $params = array( 655 'context' => $context, 656 'objectid' => $result, 657 'relateduserid' => $userid, 658 'other' => array('forumid' => $forum->id), 659 660 ); 661 $event = event\subscription_created::create($params); 662 if ($userrequest && $discussionsubscriptions) { 663 foreach ($discussionsubscriptions as $subscription) { 664 $event->add_record_snapshot('forum_discussion_subs', $subscription); 665 } 666 $discussionsubscriptions->close(); 667 } 668 $event->trigger(); 669 670 return $result; 671 } 672 673 /** 674 * Removes user from the subscriber list 675 * 676 * @param int $userid The ID of the user to unsubscribe 677 * @param \stdClass $forum The forum record for this forum. 678 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current 679 * module set in page. 680 * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether 681 * discussion subscriptions are removed too. 682 * @return boolean Always returns true. 683 */ 684 public static function unsubscribe_user($userid, $forum, $context = null, $userrequest = false) { 685 global $DB; 686 687 $sqlparams = array( 688 'userid' => $userid, 689 'forum' => $forum->id, 690 ); 691 $DB->delete_records('forum_digests', $sqlparams); 692 693 if ($forumsubscription = $DB->get_record('forum_subscriptions', $sqlparams)) { 694 $DB->delete_records('forum_subscriptions', array('id' => $forumsubscription->id)); 695 696 if ($userrequest) { 697 $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', $sqlparams); 698 $DB->delete_records('forum_discussion_subs', 699 array('userid' => $userid, 'forum' => $forum->id, 'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED)); 700 701 // We know that the there were previously entries and there aren't any more. 702 if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) { 703 self::$forumdiscussioncache[$userid][$forum->id] = array(); 704 } 705 } 706 707 // Reset the cache for this forum. 708 self::$forumcache[$userid][$forum->id] = false; 709 710 $context = forum_get_context($forum->id, $context); 711 $params = array( 712 'context' => $context, 713 'objectid' => $forumsubscription->id, 714 'relateduserid' => $userid, 715 'other' => array('forumid' => $forum->id), 716 717 ); 718 $event = event\subscription_deleted::create($params); 719 $event->add_record_snapshot('forum_subscriptions', $forumsubscription); 720 if ($userrequest && $discussionsubscriptions) { 721 foreach ($discussionsubscriptions as $subscription) { 722 $event->add_record_snapshot('forum_discussion_subs', $subscription); 723 } 724 $discussionsubscriptions->close(); 725 } 726 $event->trigger(); 727 } 728 729 return true; 730 } 731 732 /** 733 * Subscribes the user to the specified discussion. 734 * 735 * @param int $userid The userid of the user being subscribed 736 * @param \stdClass $discussion The discussion to subscribe to 737 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current 738 * module set in page. 739 * @return boolean Whether a change was made 740 */ 741 public static function subscribe_user_to_discussion($userid, $discussion, $context = null) { 742 global $DB; 743 744 // First check whether the user is subscribed to the discussion already. 745 $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id)); 746 if ($subscription) { 747 if ($subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) { 748 // The user is already subscribed to the discussion. Ignore. 749 return false; 750 } 751 } 752 // No discussion-level subscription. Check for a forum level subscription. 753 if ($DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) { 754 if ($subscription && $subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) { 755 // The user is subscribed to the forum, but unsubscribed from the discussion, delete the discussion preference. 756 $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id)); 757 unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]); 758 } else { 759 // The user is already subscribed to the forum. Ignore. 760 return false; 761 } 762 } else { 763 if ($subscription) { 764 $subscription->preference = time(); 765 $DB->update_record('forum_discussion_subs', $subscription); 766 } else { 767 $subscription = new \stdClass(); 768 $subscription->userid = $userid; 769 $subscription->forum = $discussion->forum; 770 $subscription->discussion = $discussion->id; 771 $subscription->preference = time(); 772 773 $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription); 774 self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference; 775 } 776 } 777 778 $context = forum_get_context($discussion->forum, $context); 779 $params = array( 780 'context' => $context, 781 'objectid' => $subscription->id, 782 'relateduserid' => $userid, 783 'other' => array( 784 'forumid' => $discussion->forum, 785 'discussion' => $discussion->id, 786 ), 787 788 ); 789 $event = event\discussion_subscription_created::create($params); 790 $event->trigger(); 791 792 return true; 793 } 794 /** 795 * Unsubscribes the user from the specified discussion. 796 * 797 * @param int $userid The userid of the user being unsubscribed 798 * @param \stdClass $discussion The discussion to unsubscribe from 799 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current 800 * module set in page. 801 * @return boolean Whether a change was made 802 */ 803 public static function unsubscribe_user_from_discussion($userid, $discussion, $context = null) { 804 global $DB; 805 806 // First check whether the user's subscription preference for this discussion. 807 $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id)); 808 if ($subscription) { 809 if ($subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) { 810 // The user is already unsubscribed from the discussion. Ignore. 811 return false; 812 } 813 } 814 // No discussion-level preference. Check for a forum level subscription. 815 if (!$DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) { 816 if ($subscription && $subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) { 817 // The user is not subscribed to the forum, but subscribed from the discussion, delete the discussion subscription. 818 $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id)); 819 unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]); 820 } else { 821 // The user is not subscribed from the forum. Ignore. 822 return false; 823 } 824 } else { 825 if ($subscription) { 826 $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED; 827 $DB->update_record('forum_discussion_subs', $subscription); 828 } else { 829 $subscription = new \stdClass(); 830 $subscription->userid = $userid; 831 $subscription->forum = $discussion->forum; 832 $subscription->discussion = $discussion->id; 833 $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED; 834 835 $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription); 836 } 837 self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference; 838 } 839 840 $context = forum_get_context($discussion->forum, $context); 841 $params = array( 842 'context' => $context, 843 'objectid' => $subscription->id, 844 'relateduserid' => $userid, 845 'other' => array( 846 'forumid' => $discussion->forum, 847 'discussion' => $discussion->id, 848 ), 849 850 ); 851 $event = event\discussion_subscription_deleted::create($params); 852 $event->trigger(); 853 854 return true; 855 } 856 857 /** 858 * Gets the default subscription value for the logged in user. 859 * 860 * @param \stdClass $forum The forum record 861 * @param \context $context The course context 862 * @param \cm_info $cm cm_info 863 * @param int|null $discussionid The discussion we are checking against 864 * @return bool Default subscription 865 * @throws coding_exception 866 */ 867 public static function get_user_default_subscription($forum, $context, $cm, ?int $discussionid) { 868 global $USER; 869 $manageactivities = has_capability('moodle/course:manageactivities', $context); 870 if (\mod_forum\subscriptions::subscription_disabled($forum) && !$manageactivities) { 871 // User does not have permission to subscribe to this discussion at all. 872 $discussionsubscribe = false; 873 } else if (\mod_forum\subscriptions::is_forcesubscribed($forum)) { 874 // User does not have permission to unsubscribe from this discussion at all. 875 $discussionsubscribe = true; 876 } else { 877 if (isset($discussionid) && self::is_subscribed($USER->id, $forum, $discussionid, $cm)) { 878 // User is subscribed to the discussion - continue the subscription. 879 $discussionsubscribe = true; 880 } else if (!isset($discussionid) && \mod_forum\subscriptions::is_subscribed($USER->id, $forum, null, $cm)) { 881 // Starting a new discussion, and the user is subscribed to the forum - subscribe to the discussion. 882 $discussionsubscribe = true; 883 } else { 884 // User is not subscribed to either forum or discussion. Follow user preference. 885 $discussionsubscribe = $USER->autosubscribe ?? false; 886 } 887 } 888 889 return $discussionsubscribe; 890 } 891 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body