Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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   * The module forums tests
  19   *
  20   * @package    mod_forum
  21   * @copyright  2013 Frédéric Massart
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  global $CFG;
  28  require_once (__DIR__ . '/generator_trait.php');
  29  require_once("{$CFG->dirroot}/mod/forum/lib.php");
  30  
  31  class mod_forum_subscriptions_testcase extends advanced_testcase {
  32      // Include the mod_forum test helpers.
  33      // This includes functions to create forums, users, discussions, and posts.
  34      use mod_forum_tests_generator_trait;
  35  
  36      /**
  37       * Test setUp.
  38       */
  39      public function setUp(): void {
  40          global $DB;
  41  
  42          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  43          // tests using these functions.
  44          \mod_forum\subscriptions::reset_forum_cache();
  45          \mod_forum\subscriptions::reset_discussion_cache();
  46      }
  47  
  48      /**
  49       * Test tearDown.
  50       */
  51      public function tearDown(): void {
  52          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  53          // tests using these functions.
  54          \mod_forum\subscriptions::reset_forum_cache();
  55          \mod_forum\subscriptions::reset_discussion_cache();
  56      }
  57  
  58      public function test_subscription_modes() {
  59          global $DB;
  60  
  61          $this->resetAfterTest(true);
  62  
  63          // Create a course, with a forum.
  64          $course = $this->getDataGenerator()->create_course();
  65  
  66          $options = array('course' => $course->id);
  67          $forum = $this->getDataGenerator()->create_module('forum', $options);
  68  
  69          // Create a user enrolled in the course as a student.
  70          list($user) = $this->helper_create_users($course, 1);
  71  
  72          // Must be logged in as the current user.
  73          $this->setUser($user);
  74  
  75          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_FORCESUBSCRIBE);
  76          $forum = $DB->get_record('forum', array('id' => $forum->id));
  77          $this->assertEquals(FORUM_FORCESUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
  78          $this->assertTrue(\mod_forum\subscriptions::is_forcesubscribed($forum));
  79          $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
  80          $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));
  81  
  82          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_DISALLOWSUBSCRIBE);
  83          $forum = $DB->get_record('forum', array('id' => $forum->id));
  84          $this->assertEquals(FORUM_DISALLOWSUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
  85          $this->assertTrue(\mod_forum\subscriptions::subscription_disabled($forum));
  86          $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
  87          $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));
  88  
  89          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_INITIALSUBSCRIBE);
  90          $forum = $DB->get_record('forum', array('id' => $forum->id));
  91          $this->assertEquals(FORUM_INITIALSUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
  92          $this->assertTrue(\mod_forum\subscriptions::is_subscribable($forum));
  93          $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));
  94          $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));
  95  
  96          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_CHOOSESUBSCRIBE);
  97          $forum = $DB->get_record('forum', array('id' => $forum->id));
  98          $this->assertEquals(FORUM_CHOOSESUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
  99          $this->assertTrue(\mod_forum\subscriptions::is_subscribable($forum));
 100          $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));
 101          $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));
 102      }
 103  
 104      /**
 105       * Test fetching unsubscribable forums.
 106       */
 107      public function test_unsubscribable_forums() {
 108          global $DB;
 109  
 110          $this->resetAfterTest(true);
 111  
 112          // Create a course, with a forum.
 113          $course = $this->getDataGenerator()->create_course();
 114  
 115          // Create a user enrolled in the course as a student.
 116          list($user) = $this->helper_create_users($course, 1);
 117  
 118          // Must be logged in as the current user.
 119          $this->setUser($user);
 120  
 121          // Without any subscriptions, there should be nothing returned.
 122          $result = \mod_forum\subscriptions::get_unsubscribable_forums();
 123          $this->assertEquals(0, count($result));
 124  
 125          // Create the forums.
 126          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 127          $forceforum = $this->getDataGenerator()->create_module('forum', $options);
 128          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
 129          $disallowforum = $this->getDataGenerator()->create_module('forum', $options);
 130          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 131          $chooseforum = $this->getDataGenerator()->create_module('forum', $options);
 132          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 133          $initialforum = $this->getDataGenerator()->create_module('forum', $options);
 134  
 135          // At present the user is only subscribed to the initial forum.
 136          $result = \mod_forum\subscriptions::get_unsubscribable_forums();
 137          $this->assertEquals(1, count($result));
 138  
 139          // Ensure that the user is enrolled in all of the forums except force subscribed.
 140          \mod_forum\subscriptions::subscribe_user($user->id, $disallowforum);
 141          \mod_forum\subscriptions::subscribe_user($user->id, $chooseforum);
 142  
 143          $result = \mod_forum\subscriptions::get_unsubscribable_forums();
 144          $this->assertEquals(3, count($result));
 145  
 146          // Hide the forums.
 147          set_coursemodule_visible($forceforum->cmid, 0);
 148          set_coursemodule_visible($disallowforum->cmid, 0);
 149          set_coursemodule_visible($chooseforum->cmid, 0);
 150          set_coursemodule_visible($initialforum->cmid, 0);
 151          $result = \mod_forum\subscriptions::get_unsubscribable_forums();
 152          $this->assertEquals(0, count($result));
 153  
 154          // Add the moodle/course:viewhiddenactivities capability to the student user.
 155          $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
 156          $context = \context_course::instance($course->id);
 157          assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleids['student'], $context);
 158  
 159          // All of the unsubscribable forums should now be listed.
 160          $result = \mod_forum\subscriptions::get_unsubscribable_forums();
 161          $this->assertEquals(3, count($result));
 162      }
 163  
 164      /**
 165       * Test that toggling the forum-level subscription for a different user does not affect their discussion-level
 166       * subscriptions.
 167       */
 168      public function test_forum_subscribe_toggle_as_other() {
 169          global $DB;
 170  
 171          $this->resetAfterTest(true);
 172  
 173          // Create a course, with a forum.
 174          $course = $this->getDataGenerator()->create_course();
 175  
 176          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 177          $forum = $this->getDataGenerator()->create_module('forum', $options);
 178  
 179          // Create a user enrolled in the course as a student.
 180          list($author) = $this->helper_create_users($course, 1);
 181  
 182          // Post a discussion to the forum.
 183          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 184  
 185          // Check that the user is currently not subscribed to the forum.
 186          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 187  
 188          // Check that the user is unsubscribed from the discussion too.
 189          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 190  
 191          // Check that we have no records in either of the subscription tables.
 192          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 193              'userid'        => $author->id,
 194              'forum'         => $forum->id,
 195          )));
 196          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 197              'userid'        => $author->id,
 198              'discussion'    => $discussion->id,
 199          )));
 200  
 201          // Subscribing to the forum should create a record in the subscriptions table, but not the forum discussion
 202          // subscriptions table.
 203          \mod_forum\subscriptions::subscribe_user($author->id, $forum);
 204          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 205              'userid'        => $author->id,
 206              'forum'         => $forum->id,
 207          )));
 208          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 209              'userid'        => $author->id,
 210              'discussion'    => $discussion->id,
 211          )));
 212  
 213          // Unsubscribing should remove the record from the forum subscriptions table, and not modify the forum
 214          // discussion subscriptions table.
 215          \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
 216          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 217              'userid'        => $author->id,
 218              'forum'         => $forum->id,
 219          )));
 220          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 221              'userid'        => $author->id,
 222              'discussion'    => $discussion->id,
 223          )));
 224  
 225          // Enroling the user in the discussion should add one record to the forum discussion table without modifying the
 226          // form subscriptions.
 227          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 228          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 229              'userid'        => $author->id,
 230              'forum'         => $forum->id,
 231          )));
 232          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 233              'userid'        => $author->id,
 234              'discussion'    => $discussion->id,
 235          )));
 236  
 237          // Unsubscribing should remove the record from the forum subscriptions table, and not modify the forum
 238          // discussion subscriptions table.
 239          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 240          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 241              'userid'        => $author->id,
 242              'forum'         => $forum->id,
 243          )));
 244          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 245              'userid'        => $author->id,
 246              'discussion'    => $discussion->id,
 247          )));
 248  
 249          // Re-subscribe to the discussion so that we can check the effect of forum-level subscriptions.
 250          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 251          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 252              'userid'        => $author->id,
 253              'forum'         => $forum->id,
 254          )));
 255          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 256              'userid'        => $author->id,
 257              'discussion'    => $discussion->id,
 258          )));
 259  
 260          // Subscribing to the forum should have no effect on the forum discussion subscriptions table if the user did
 261          // not request the change themself.
 262          \mod_forum\subscriptions::subscribe_user($author->id, $forum);
 263          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 264              'userid'        => $author->id,
 265              'forum'         => $forum->id,
 266          )));
 267          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 268              'userid'        => $author->id,
 269              'discussion'    => $discussion->id,
 270          )));
 271  
 272          // Unsubscribing from the forum should have no effect on the forum discussion subscriptions table if the user
 273          // did not request the change themself.
 274          \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
 275          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 276              'userid'        => $author->id,
 277              'forum'         => $forum->id,
 278          )));
 279          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 280              'userid'        => $author->id,
 281              'discussion'    => $discussion->id,
 282          )));
 283  
 284          // Subscribing to the forum should remove the per-discussion subscription preference if the user requested the
 285          // change themself.
 286          \mod_forum\subscriptions::subscribe_user($author->id, $forum, null, true);
 287          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 288              'userid'        => $author->id,
 289              'forum'         => $forum->id,
 290          )));
 291          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 292              'userid'        => $author->id,
 293              'discussion'    => $discussion->id,
 294          )));
 295  
 296          // Now unsubscribe from the current discussion whilst being subscribed to the forum as a whole.
 297          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 298          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 299              'userid'        => $author->id,
 300              'forum'         => $forum->id,
 301          )));
 302          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 303              'userid'        => $author->id,
 304              'discussion'    => $discussion->id,
 305          )));
 306  
 307          // Unsubscribing from the forum should remove the per-discussion subscription preference if the user requested the
 308          // change themself.
 309          \mod_forum\subscriptions::unsubscribe_user($author->id, $forum, null, true);
 310          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 311              'userid'        => $author->id,
 312              'forum'         => $forum->id,
 313          )));
 314          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 315              'userid'        => $author->id,
 316              'discussion'    => $discussion->id,
 317          )));
 318  
 319          // Subscribe to the discussion.
 320          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 321          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 322              'userid'        => $author->id,
 323              'forum'         => $forum->id,
 324          )));
 325          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 326              'userid'        => $author->id,
 327              'discussion'    => $discussion->id,
 328          )));
 329  
 330          // Subscribe to the forum without removing the discussion preferences.
 331          \mod_forum\subscriptions::subscribe_user($author->id, $forum);
 332          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 333              'userid'        => $author->id,
 334              'forum'         => $forum->id,
 335          )));
 336          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 337              'userid'        => $author->id,
 338              'discussion'    => $discussion->id,
 339          )));
 340  
 341          // Unsubscribing from the discussion should result in a change.
 342          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 343          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 344              'userid'        => $author->id,
 345              'forum'         => $forum->id,
 346          )));
 347          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 348              'userid'        => $author->id,
 349              'discussion'    => $discussion->id,
 350          )));
 351  
 352      }
 353  
 354      /**
 355       * Test that a user unsubscribed from a forum is not subscribed to it's discussions by default.
 356       */
 357      public function test_forum_discussion_subscription_forum_unsubscribed() {
 358          $this->resetAfterTest(true);
 359  
 360          // Create a course, with a forum.
 361          $course = $this->getDataGenerator()->create_course();
 362  
 363          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 364          $forum = $this->getDataGenerator()->create_module('forum', $options);
 365  
 366          // Create users enrolled in the course as students.
 367          list($author) = $this->helper_create_users($course, 1);
 368  
 369          // Check that the user is currently not subscribed to the forum.
 370          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 371  
 372          // Post a discussion to the forum.
 373          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 374  
 375          // Check that the user is unsubscribed from the discussion too.
 376          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 377      }
 378  
 379      /**
 380       * Test that the act of subscribing to a forum subscribes the user to it's discussions by default.
 381       */
 382      public function test_forum_discussion_subscription_forum_subscribed() {
 383          $this->resetAfterTest(true);
 384  
 385          // Create a course, with a forum.
 386          $course = $this->getDataGenerator()->create_course();
 387  
 388          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 389          $forum = $this->getDataGenerator()->create_module('forum', $options);
 390  
 391          // Create users enrolled in the course as students.
 392          list($author) = $this->helper_create_users($course, 1);
 393  
 394          // Enrol the user in the forum.
 395          // If a subscription was added, we get the record ID.
 396          $this->assertIsInt(\mod_forum\subscriptions::subscribe_user($author->id, $forum));
 397  
 398          // If we already have a subscription when subscribing the user, we get a boolean (true).
 399          $this->assertTrue(\mod_forum\subscriptions::subscribe_user($author->id, $forum));
 400  
 401          // Check that the user is currently subscribed to the forum.
 402          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 403  
 404          // Post a discussion to the forum.
 405          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 406  
 407          // Check that the user is subscribed to the discussion too.
 408          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 409      }
 410  
 411      /**
 412       * Test that a user unsubscribed from a forum can be subscribed to a discussion.
 413       */
 414      public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed() {
 415          $this->resetAfterTest(true);
 416  
 417          // Create a course, with a forum.
 418          $course = $this->getDataGenerator()->create_course();
 419  
 420          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 421          $forum = $this->getDataGenerator()->create_module('forum', $options);
 422  
 423          // Create a user enrolled in the course as a student.
 424          list($author) = $this->helper_create_users($course, 1);
 425  
 426          // Check that the user is currently not subscribed to the forum.
 427          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 428  
 429          // Post a discussion to the forum.
 430          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 431  
 432          // Attempting to unsubscribe from the discussion should not make a change.
 433          $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion));
 434  
 435          // Then subscribe them to the discussion.
 436          $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
 437  
 438          // Check that the user is still unsubscribed from the forum.
 439          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 440  
 441          // But subscribed to the discussion.
 442          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 443      }
 444  
 445      /**
 446       * Test that a user subscribed to a forum can be unsubscribed from a discussion.
 447       */
 448      public function test_forum_discussion_subscription_forum_subscribed_discussion_unsubscribed() {
 449          $this->resetAfterTest(true);
 450  
 451          // Create a course, with a forum.
 452          $course = $this->getDataGenerator()->create_course();
 453  
 454          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 455          $forum = $this->getDataGenerator()->create_module('forum', $options);
 456  
 457          // Create two users enrolled in the course as students.
 458          list($author) = $this->helper_create_users($course, 2);
 459  
 460          // Enrol the student in the forum.
 461          \mod_forum\subscriptions::subscribe_user($author->id, $forum);
 462  
 463          // Check that the user is currently subscribed to the forum.
 464          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 465  
 466          // Post a discussion to the forum.
 467          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 468  
 469          // Then unsubscribe them from the discussion.
 470          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 471  
 472          // Check that the user is still subscribed to the forum.
 473          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 474  
 475          // But unsubscribed from the discussion.
 476          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 477      }
 478  
 479      /**
 480       * Test the effect of toggling the discussion subscription status when subscribed to the forum.
 481       */
 482      public function test_forum_discussion_toggle_forum_subscribed() {
 483          global $DB;
 484  
 485          $this->resetAfterTest(true);
 486  
 487          // Create a course, with a forum.
 488          $course = $this->getDataGenerator()->create_course();
 489  
 490          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 491          $forum = $this->getDataGenerator()->create_module('forum', $options);
 492  
 493          // Create two users enrolled in the course as students.
 494          list($author) = $this->helper_create_users($course, 2);
 495  
 496          // Enrol the student in the forum.
 497          \mod_forum\subscriptions::subscribe_user($author->id, $forum);
 498  
 499          // Check that the user is currently subscribed to the forum.
 500          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 501  
 502          // Post a discussion to the forum.
 503          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 504  
 505          // Check that the user is initially subscribed to that discussion.
 506          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 507  
 508          // An attempt to subscribe again should result in a falsey return to indicate that no change was made.
 509          $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
 510  
 511          // And there should be no discussion subscriptions (and one forum subscription).
 512          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 513              'userid'        => $author->id,
 514              'discussion'    => $discussion->id,
 515          )));
 516          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 517              'userid'        => $author->id,
 518              'forum'         => $forum->id,
 519          )));
 520  
 521          // Then unsubscribe them from the discussion.
 522          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 523  
 524          // Check that the user is still subscribed to the forum.
 525          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 526  
 527          // An attempt to unsubscribe again should result in a falsey return to indicate that no change was made.
 528          $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion));
 529  
 530          // And there should be a discussion subscriptions (and one forum subscription).
 531          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 532              'userid'        => $author->id,
 533              'discussion'    => $discussion->id,
 534          )));
 535          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 536              'userid'        => $author->id,
 537              'forum'         => $forum->id,
 538          )));
 539  
 540          // But unsubscribed from the discussion.
 541          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 542  
 543          // There should be a record in the discussion subscription tracking table.
 544          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 545              'userid'        => $author->id,
 546              'discussion'    => $discussion->id,
 547          )));
 548  
 549          // And one in the forum subscription tracking table.
 550          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 551              'userid'        => $author->id,
 552              'forum'         => $forum->id,
 553          )));
 554  
 555          // Now subscribe the user again to the discussion.
 556          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 557  
 558          // Check that the user is still subscribed to the forum.
 559          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 560  
 561          // And is subscribed to the discussion again.
 562          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 563  
 564          // There should be no record in the discussion subscription tracking table.
 565          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 566              'userid'        => $author->id,
 567              'discussion'    => $discussion->id,
 568          )));
 569  
 570          // And one in the forum subscription tracking table.
 571          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 572              'userid'        => $author->id,
 573              'forum'         => $forum->id,
 574          )));
 575  
 576          // And unsubscribe again.
 577          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 578  
 579          // Check that the user is still subscribed to the forum.
 580          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 581  
 582          // But unsubscribed from the discussion.
 583          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 584  
 585          // There should be a record in the discussion subscription tracking table.
 586          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 587              'userid'        => $author->id,
 588              'discussion'    => $discussion->id,
 589          )));
 590  
 591          // And one in the forum subscription tracking table.
 592          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 593              'userid'        => $author->id,
 594              'forum'         => $forum->id,
 595          )));
 596  
 597          // And subscribe the user again to the discussion.
 598          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 599  
 600          // Check that the user is still subscribed to the forum.
 601          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 602          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 603  
 604          // And is subscribed to the discussion again.
 605          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 606  
 607          // There should be no record in the discussion subscription tracking table.
 608          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 609              'userid'        => $author->id,
 610              'discussion'    => $discussion->id,
 611          )));
 612  
 613          // And one in the forum subscription tracking table.
 614          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 615              'userid'        => $author->id,
 616              'forum'         => $forum->id,
 617          )));
 618  
 619          // And unsubscribe again.
 620          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 621  
 622          // Check that the user is still subscribed to the forum.
 623          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 624  
 625          // But unsubscribed from the discussion.
 626          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 627  
 628          // There should be a record in the discussion subscription tracking table.
 629          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 630              'userid'        => $author->id,
 631              'discussion'    => $discussion->id,
 632          )));
 633  
 634          // And one in the forum subscription tracking table.
 635          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
 636              'userid'        => $author->id,
 637              'forum'         => $forum->id,
 638          )));
 639  
 640          // Now unsubscribe the user from the forum.
 641          $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user($author->id, $forum, null, true));
 642  
 643          // This removes both the forum_subscriptions, and the forum_discussion_subs records.
 644          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 645              'userid'        => $author->id,
 646              'discussion'    => $discussion->id,
 647          )));
 648          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
 649              'userid'        => $author->id,
 650              'forum'         => $forum->id,
 651          )));
 652  
 653          // And should have reset the discussion cache value.
 654          $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $author->id);
 655          $this->assertIsArray($result);
 656          $this->assertFalse(isset($result[$discussion->id]));
 657      }
 658  
 659      /**
 660       * Test the effect of toggling the discussion subscription status when unsubscribed from the forum.
 661       */
 662      public function test_forum_discussion_toggle_forum_unsubscribed() {
 663          global $DB;
 664  
 665          $this->resetAfterTest(true);
 666  
 667          // Create a course, with a forum.
 668          $course = $this->getDataGenerator()->create_course();
 669  
 670          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 671          $forum = $this->getDataGenerator()->create_module('forum', $options);
 672  
 673          // Create two users enrolled in the course as students.
 674          list($author) = $this->helper_create_users($course, 2);
 675  
 676          // Check that the user is currently unsubscribed to the forum.
 677          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 678  
 679          // Post a discussion to the forum.
 680          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 681  
 682          // Check that the user is initially unsubscribed to that discussion.
 683          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 684  
 685          // Then subscribe them to the discussion.
 686          $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
 687  
 688          // An attempt to subscribe again should result in a falsey return to indicate that no change was made.
 689          $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
 690  
 691          // Check that the user is still unsubscribed from the forum.
 692          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 693  
 694          // But subscribed to the discussion.
 695          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 696  
 697          // There should be a record in the discussion subscription tracking table.
 698          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 699              'userid'        => $author->id,
 700              'discussion'    => $discussion->id,
 701          )));
 702  
 703          // Now unsubscribe the user again from the discussion.
 704          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 705  
 706          // Check that the user is still unsubscribed from the forum.
 707          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 708  
 709          // And is unsubscribed from the discussion again.
 710          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 711  
 712          // There should be no record in the discussion subscription tracking table.
 713          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 714              'userid'        => $author->id,
 715              'discussion'    => $discussion->id,
 716          )));
 717  
 718          // And subscribe the user again to the discussion.
 719          \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
 720  
 721          // Check that the user is still unsubscribed from the forum.
 722          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 723  
 724          // And is subscribed to the discussion again.
 725          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 726  
 727          // There should be a record in the discussion subscription tracking table.
 728          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
 729              'userid'        => $author->id,
 730              'discussion'    => $discussion->id,
 731          )));
 732  
 733          // And unsubscribe again.
 734          \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
 735  
 736          // Check that the user is still unsubscribed from the forum.
 737          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 738  
 739          // But unsubscribed from the discussion.
 740          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 741  
 742          // There should be no record in the discussion subscription tracking table.
 743          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
 744              'userid'        => $author->id,
 745              'discussion'    => $discussion->id,
 746          )));
 747      }
 748  
 749      /**
 750       * Test that the correct users are returned when fetching subscribed users from a forum where users can choose to
 751       * subscribe and unsubscribe.
 752       */
 753      public function test_fetch_subscribed_users_subscriptions() {
 754          global $DB, $CFG;
 755  
 756          $this->resetAfterTest(true);
 757  
 758          // Create a course, with a forum. where users are initially subscribed.
 759          $course = $this->getDataGenerator()->create_course();
 760          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 761          $forum = $this->getDataGenerator()->create_module('forum', $options);
 762  
 763          // Create some user enrolled in the course as a student.
 764          $usercount = 5;
 765          $users = $this->helper_create_users($course, $usercount);
 766  
 767          // All users should be subscribed.
 768          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 769          $this->assertEquals($usercount, count($subscribers));
 770  
 771          // Subscribe the guest user too to the forum - they should never be returned by this function.
 772          $this->getDataGenerator()->enrol_user($CFG->siteguest, $course->id);
 773          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 774          $this->assertEquals($usercount, count($subscribers));
 775  
 776          // Unsubscribe 2 users.
 777          $unsubscribedcount = 2;
 778          for ($i = 0; $i < $unsubscribedcount; $i++) {
 779              \mod_forum\subscriptions::unsubscribe_user($users[$i]->id, $forum);
 780          }
 781  
 782          // The subscription count should now take into account those users who have been unsubscribed.
 783          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 784          $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
 785      }
 786  
 787      /**
 788       * Test that the correct users are returned hwen fetching subscribed users from a forum where users are forcibly
 789       * subscribed.
 790       */
 791      public function test_fetch_subscribed_users_forced() {
 792          global $DB;
 793  
 794          $this->resetAfterTest(true);
 795  
 796          // Create a course, with a forum. where users are initially subscribed.
 797          $course = $this->getDataGenerator()->create_course();
 798          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 799          $forum = $this->getDataGenerator()->create_module('forum', $options);
 800  
 801          // Create some user enrolled in the course as a student.
 802          $usercount = 5;
 803          $users = $this->helper_create_users($course, $usercount);
 804  
 805          // All users should be subscribed.
 806          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 807          $this->assertEquals($usercount, count($subscribers));
 808      }
 809  
 810      /**
 811       * Test that unusual combinations of discussion subscriptions do not affect the subscribed user list.
 812       */
 813      public function test_fetch_subscribed_users_discussion_subscriptions() {
 814          global $DB;
 815  
 816          $this->resetAfterTest(true);
 817  
 818          // Create a course, with a forum. where users are initially subscribed.
 819          $course = $this->getDataGenerator()->create_course();
 820          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 821          $forum = $this->getDataGenerator()->create_module('forum', $options);
 822  
 823          // Create some user enrolled in the course as a student.
 824          $usercount = 5;
 825          $users = $this->helper_create_users($course, $usercount);
 826  
 827          list($discussion, $post) = $this->helper_post_to_forum($forum, $users[0]);
 828  
 829          // All users should be subscribed.
 830          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 831          $this->assertEquals($usercount, count($subscribers));
 832          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
 833          $this->assertEquals($usercount, count($subscribers));
 834  
 835          \mod_forum\subscriptions::unsubscribe_user_from_discussion($users[0]->id, $discussion);
 836  
 837          // All users should be subscribed.
 838          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 839          $this->assertEquals($usercount, count($subscribers));
 840  
 841          // All users should be subscribed.
 842          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
 843          $this->assertEquals($usercount, count($subscribers));
 844  
 845          // Manually insert an extra subscription for one of the users.
 846          $record = new stdClass();
 847          $record->userid = $users[2]->id;
 848          $record->forum = $forum->id;
 849          $record->discussion = $discussion->id;
 850          $record->preference = time();
 851          $DB->insert_record('forum_discussion_subs', $record);
 852  
 853          // The discussion count should not have changed.
 854          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 855          $this->assertEquals($usercount, count($subscribers));
 856          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
 857          $this->assertEquals($usercount, count($subscribers));
 858  
 859          // Unsubscribe 2 users.
 860          $unsubscribedcount = 2;
 861          for ($i = 0; $i < $unsubscribedcount; $i++) {
 862              \mod_forum\subscriptions::unsubscribe_user($users[$i]->id, $forum);
 863          }
 864  
 865          // The subscription count should now take into account those users who have been unsubscribed.
 866          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 867          $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
 868          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
 869          $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
 870  
 871          // Now subscribe one of those users back to the discussion.
 872          $subscribeddiscussionusers = 1;
 873          for ($i = 0; $i < $subscribeddiscussionusers; $i++) {
 874              \mod_forum\subscriptions::subscribe_user_to_discussion($users[$i]->id, $discussion);
 875          }
 876          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 877          $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
 878          $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
 879          $this->assertEquals($usercount - $unsubscribedcount + $subscribeddiscussionusers, count($subscribers));
 880      }
 881  
 882      /**
 883       * Test whether a user is force-subscribed to a forum.
 884       */
 885      public function test_force_subscribed_to_forum() {
 886          global $DB;
 887  
 888          $this->resetAfterTest(true);
 889  
 890          // Create a course, with a forum.
 891          $course = $this->getDataGenerator()->create_course();
 892  
 893          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
 894          $forum = $this->getDataGenerator()->create_module('forum', $options);
 895  
 896          // Create a user enrolled in the course as a student.
 897          $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
 898          $user = $this->getDataGenerator()->create_user();
 899          $this->getDataGenerator()->enrol_user($user->id, $course->id, $roleids['student']);
 900  
 901          // Check that the user is currently subscribed to the forum.
 902          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
 903  
 904          // Remove the allowforcesubscribe capability from the user.
 905          $cm = get_coursemodule_from_instance('forum', $forum->id);
 906          $context = \context_module::instance($cm->id);
 907          assign_capability('mod/forum:allowforcesubscribe', CAP_PROHIBIT, $roleids['student'], $context);
 908          $this->assertFalse(has_capability('mod/forum:allowforcesubscribe', $context, $user->id));
 909  
 910          // Check that the user is no longer subscribed to the forum.
 911          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
 912      }
 913  
 914      /**
 915       * Test that the subscription cache can be pre-filled.
 916       */
 917      public function test_subscription_cache_prefill() {
 918          global $DB;
 919  
 920          $this->resetAfterTest(true);
 921  
 922          // Create a course, with a forum.
 923          $course = $this->getDataGenerator()->create_course();
 924  
 925          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 926          $forum = $this->getDataGenerator()->create_module('forum', $options);
 927  
 928          // Create some users.
 929          $users = $this->helper_create_users($course, 20);
 930  
 931          // Reset the subscription cache.
 932          \mod_forum\subscriptions::reset_forum_cache();
 933  
 934          // Filling the subscription cache should use a query.
 935          $startcount = $DB->perf_get_reads();
 936          $this->assertNull(\mod_forum\subscriptions::fill_subscription_cache($forum->id));
 937          $postfillcount = $DB->perf_get_reads();
 938          $this->assertNotEquals($postfillcount, $startcount);
 939  
 940          // Now fetch some subscriptions from that forum - these should use
 941          // the cache and not perform additional queries.
 942          foreach ($users as $user) {
 943              $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($forum->id, $user->id));
 944          }
 945          $finalcount = $DB->perf_get_reads();
 946          $this->assertEquals(0, $finalcount - $postfillcount);
 947      }
 948  
 949      /**
 950       * Test that the subscription cache can filled user-at-a-time.
 951       */
 952      public function test_subscription_cache_fill() {
 953          global $DB;
 954  
 955          $this->resetAfterTest(true);
 956  
 957          // Create a course, with a forum.
 958          $course = $this->getDataGenerator()->create_course();
 959  
 960          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 961          $forum = $this->getDataGenerator()->create_module('forum', $options);
 962  
 963          // Create some users.
 964          $users = $this->helper_create_users($course, 20);
 965  
 966          // Reset the subscription cache.
 967          \mod_forum\subscriptions::reset_forum_cache();
 968  
 969          // Filling the subscription cache should only use a single query.
 970          $startcount = $DB->perf_get_reads();
 971  
 972          // Fetch some subscriptions from that forum - these should not use the cache and will perform additional queries.
 973          foreach ($users as $user) {
 974              $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($forum->id, $user->id));
 975          }
 976          $finalcount = $DB->perf_get_reads();
 977          $this->assertEquals(20, $finalcount - $startcount);
 978      }
 979  
 980      /**
 981       * Test that the discussion subscription cache can filled course-at-a-time.
 982       */
 983      public function test_discussion_subscription_cache_fill_for_course() {
 984          global $DB;
 985  
 986          $this->resetAfterTest(true);
 987  
 988          // Create a course, with a forum.
 989          $course = $this->getDataGenerator()->create_course();
 990  
 991          // Create the forums.
 992          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
 993          $disallowforum = $this->getDataGenerator()->create_module('forum', $options);
 994          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
 995          $chooseforum = $this->getDataGenerator()->create_module('forum', $options);
 996          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
 997          $initialforum = $this->getDataGenerator()->create_module('forum', $options);
 998  
 999          // Create some users and keep a reference to the first user.
1000          $users = $this->helper_create_users($course, 20);
1001          $user = reset($users);
1002  
1003          // Reset the subscription caches.
1004          \mod_forum\subscriptions::reset_forum_cache();
1005  
1006          $startcount = $DB->perf_get_reads();
1007          $result = \mod_forum\subscriptions::fill_subscription_cache_for_course($course->id, $user->id);
1008          $this->assertNull($result);
1009          $postfillcount = $DB->perf_get_reads();
1010          $this->assertNotEquals($postfillcount, $startcount);
1011          $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($disallowforum->id, $user->id));
1012          $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($chooseforum->id, $user->id));
1013          $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($initialforum->id, $user->id));
1014          $finalcount = $DB->perf_get_reads();
1015          $this->assertEquals(0, $finalcount - $postfillcount);
1016  
1017          // Test for all users.
1018          foreach ($users as $user) {
1019              $result = \mod_forum\subscriptions::fill_subscription_cache_for_course($course->id, $user->id);
1020              $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($disallowforum->id, $user->id));
1021              $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($chooseforum->id, $user->id));
1022              $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($initialforum->id, $user->id));
1023          }
1024          $finalcount = $DB->perf_get_reads();
1025          $this->assertNotEquals($finalcount, $postfillcount);
1026      }
1027  
1028      /**
1029       * Test that the discussion subscription cache can be forcibly updated for a user.
1030       */
1031      public function test_discussion_subscription_cache_prefill() {
1032          global $DB;
1033  
1034          $this->resetAfterTest(true);
1035  
1036          // Create a course, with a forum.
1037          $course = $this->getDataGenerator()->create_course();
1038  
1039          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1040          $forum = $this->getDataGenerator()->create_module('forum', $options);
1041  
1042          // Create some users.
1043          $users = $this->helper_create_users($course, 20);
1044  
1045          // Post some discussions to the forum.
1046          $discussions = array();
1047          $author = $users[0];
1048          $userwithnosubs = $users[1];
1049  
1050          for ($i = 0; $i < 20; $i++) {
1051              list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1052              $discussions[] = $discussion;
1053          }
1054  
1055          // Unsubscribe half the users from the half the discussions.
1056          $forumcount = 0;
1057          $usercount = 0;
1058          $userwithsubs = null;
1059          foreach ($discussions as $data) {
1060              // Unsubscribe user from all discussions.
1061              \mod_forum\subscriptions::unsubscribe_user_from_discussion($userwithnosubs->id, $data);
1062  
1063              if ($forumcount % 2) {
1064                  continue;
1065              }
1066              foreach ($users as $user) {
1067                  if ($usercount % 2) {
1068                      $userwithsubs = $user;
1069                      continue;
1070                  }
1071                  \mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $data);
1072                  $usercount++;
1073              }
1074              $forumcount++;
1075          }
1076  
1077          // Reset the subscription caches.
1078          \mod_forum\subscriptions::reset_forum_cache();
1079          \mod_forum\subscriptions::reset_discussion_cache();
1080  
1081          // A user with no subscriptions should only be fetched once.
1082          $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithnosubs->id));
1083          $startcount = $DB->perf_get_reads();
1084          $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithnosubs->id));
1085          $this->assertEquals($startcount, $DB->perf_get_reads());
1086  
1087          // Confirm subsequent calls properly tries to fetch subs.
1088          $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithsubs->id));
1089          $this->assertNotEquals($startcount, $DB->perf_get_reads());
1090  
1091          // Another read should be performed to get all subscriptions for the forum.
1092          $startcount = $DB->perf_get_reads();
1093          $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id));
1094          $this->assertNotEquals($startcount, $DB->perf_get_reads());
1095  
1096          // Reset the subscription caches.
1097          \mod_forum\subscriptions::reset_forum_cache();
1098          \mod_forum\subscriptions::reset_discussion_cache();
1099  
1100          // Filling the discussion subscription cache should only use a single query.
1101          $startcount = $DB->perf_get_reads();
1102          $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id));
1103          $postfillcount = $DB->perf_get_reads();
1104          $this->assertNotEquals($postfillcount, $startcount);
1105  
1106          // Now fetch some subscriptions from that forum - these should use
1107          // the cache and not perform additional queries.
1108          foreach ($users as $user) {
1109              $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $user->id);
1110              $this->assertIsArray($result);
1111          }
1112          $finalcount = $DB->perf_get_reads();
1113          $this->assertEquals(0, $finalcount - $postfillcount);
1114      }
1115  
1116      /**
1117       * Test that the discussion subscription cache can filled user-at-a-time.
1118       */
1119      public function test_discussion_subscription_cache_fill() {
1120          global $DB;
1121  
1122          $this->resetAfterTest(true);
1123  
1124          // Create a course, with a forum.
1125          $course = $this->getDataGenerator()->create_course();
1126  
1127          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1128          $forum = $this->getDataGenerator()->create_module('forum', $options);
1129  
1130          // Create some users.
1131          $users = $this->helper_create_users($course, 20);
1132  
1133          // Post some discussions to the forum.
1134          $discussions = array();
1135          $author = $users[0];
1136          for ($i = 0; $i < 20; $i++) {
1137              list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1138              $discussions[] = $discussion;
1139          }
1140  
1141          // Unsubscribe half the users from the half the discussions.
1142          $forumcount = 0;
1143          $usercount = 0;
1144          foreach ($discussions as $data) {
1145              if ($forumcount % 2) {
1146                  continue;
1147              }
1148              foreach ($users as $user) {
1149                  if ($usercount % 2) {
1150                      continue;
1151                  }
1152                  \mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion);
1153                  $usercount++;
1154              }
1155              $forumcount++;
1156          }
1157  
1158          // Reset the subscription caches.
1159          \mod_forum\subscriptions::reset_forum_cache();
1160          \mod_forum\subscriptions::reset_discussion_cache();
1161  
1162          $startcount = $DB->perf_get_reads();
1163  
1164          // Now fetch some subscriptions from that forum - these should use
1165          // the cache and not perform additional queries.
1166          foreach ($users as $user) {
1167              $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $user->id);
1168              $this->assertIsArray($result);
1169          }
1170          $finalcount = $DB->perf_get_reads();
1171          $this->assertNotEquals($finalcount, $startcount);
1172      }
1173  
1174      /**
1175       * Test that after toggling the forum subscription as another user,
1176       * the discussion subscription functionality works as expected.
1177       */
1178      public function test_forum_subscribe_toggle_as_other_repeat_subscriptions() {
1179          global $DB;
1180  
1181          $this->resetAfterTest(true);
1182  
1183          // Create a course, with a forum.
1184          $course = $this->getDataGenerator()->create_course();
1185  
1186          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
1187          $forum = $this->getDataGenerator()->create_module('forum', $options);
1188  
1189          // Create a user enrolled in the course as a student.
1190          list($user) = $this->helper_create_users($course, 1);
1191  
1192          // Post a discussion to the forum.
1193          list($discussion, $post) = $this->helper_post_to_forum($forum, $user);
1194  
1195          // Confirm that the user is currently not subscribed to the forum.
1196          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1197  
1198          // Confirm that the user is unsubscribed from the discussion too.
1199          $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion->id));
1200  
1201          // Confirm that we have no records in either of the subscription tables.
1202          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
1203              'userid'        => $user->id,
1204              'forum'         => $forum->id,
1205          )));
1206          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
1207              'userid'        => $user->id,
1208              'discussion'    => $discussion->id,
1209          )));
1210  
1211          // Subscribing to the forum should create a record in the subscriptions table, but not the forum discussion
1212          // subscriptions table.
1213          \mod_forum\subscriptions::subscribe_user($user->id, $forum);
1214          $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
1215              'userid'        => $user->id,
1216              'forum'         => $forum->id,
1217          )));
1218          $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
1219              'userid'        => $user->id,
1220              'discussion'    => $discussion->id,
1221          )));
1222  
1223          // Now unsubscribe from the discussion. This should return true.
1224          $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));
1225  
1226          // Attempting to unsubscribe again should return false because no change was made.
1227          $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));
1228  
1229          // Subscribing to the discussion again should return truthfully as the subscription preference was removed.
1230          $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));
1231  
1232          // Attempting to subscribe again should return false because no change was made.
1233          $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));
1234  
1235          // Now unsubscribe from the discussion. This should return true once more.
1236          $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));
1237  
1238          // And unsubscribing from the forum but not as a request from the user should maintain their preference.
1239          \mod_forum\subscriptions::unsubscribe_user($user->id, $forum);
1240  
1241          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
1242              'userid'        => $user->id,
1243              'forum'         => $forum->id,
1244          )));
1245          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
1246              'userid'        => $user->id,
1247              'discussion'    => $discussion->id,
1248          )));
1249  
1250          // Subscribing to the discussion should return truthfully because a change was made.
1251          $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));
1252          $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
1253              'userid'        => $user->id,
1254              'forum'         => $forum->id,
1255          )));
1256          $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
1257              'userid'        => $user->id,
1258              'discussion'    => $discussion->id,
1259          )));
1260      }
1261  
1262      /**
1263       * Test that providing a context_module instance to is_subscribed does not result in additional lookups to retrieve
1264       * the context_module.
1265       */
1266      public function test_is_subscribed_cm() {
1267          global $DB;
1268  
1269          $this->resetAfterTest(true);
1270  
1271          // Create a course, with a forum.
1272          $course = $this->getDataGenerator()->create_course();
1273  
1274          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1275          $forum = $this->getDataGenerator()->create_module('forum', $options);
1276  
1277          // Create a user enrolled in the course as a student.
1278          list($user) = $this->helper_create_users($course, 1);
1279  
1280          // Retrieve the $cm now.
1281          $cm = get_fast_modinfo($forum->course)->instances['forum'][$forum->id];
1282  
1283          // Reset get_fast_modinfo.
1284          get_fast_modinfo(0, 0, true);
1285  
1286          // Call is_subscribed without passing the $cmid - this should result in a lookup and filling of some of the
1287          // caches. This provides us with consistent data to start from.
1288          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1289          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1290  
1291          // Make a note of the number of DB calls.
1292          $basecount = $DB->perf_get_reads();
1293  
1294          // Call is_subscribed - it should give return the correct result (False), and result in no additional queries.
1295          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, null, $cm));
1296  
1297          // The capability check does require some queries, so we don't test it directly.
1298          // We don't assert here because this is dependant upon linked code which could change at any time.
1299          $suppliedcmcount = $DB->perf_get_reads() - $basecount;
1300  
1301          // Call is_subscribed without passing the $cmid now - this should result in a lookup.
1302          get_fast_modinfo(0, 0, true);
1303          $basecount = $DB->perf_get_reads();
1304          $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1305          $calculatedcmcount = $DB->perf_get_reads() - $basecount;
1306  
1307          // There should be more queries than when we performed the same check a moment ago.
1308          $this->assertGreaterThan($suppliedcmcount, $calculatedcmcount);
1309      }
1310  
1311      public function is_subscribable_forums() {
1312          return [
1313              [
1314                  'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE,
1315              ],
1316              [
1317                  'forcesubscribe' => FORUM_CHOOSESUBSCRIBE,
1318              ],
1319              [
1320                  'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
1321              ],
1322              [
1323                  'forcesubscribe' => FORUM_FORCESUBSCRIBE,
1324              ],
1325          ];
1326      }
1327  
1328      public function is_subscribable_provider() {
1329          $data = [];
1330          foreach ($this->is_subscribable_forums() as $forum) {
1331              $data[] = [$forum];
1332          }
1333  
1334          return $data;
1335      }
1336  
1337      /**
1338       * @dataProvider is_subscribable_provider
1339       */
1340      public function test_is_subscribable_logged_out($options) {
1341          $this->resetAfterTest(true);
1342  
1343          // Create a course, with a forum.
1344          $course = $this->getDataGenerator()->create_course();
1345          $options['course'] = $course->id;
1346          $forum = $this->getDataGenerator()->create_module('forum', $options);
1347  
1348          $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
1349      }
1350  
1351      /**
1352       * @dataProvider is_subscribable_provider
1353       */
1354      public function test_is_subscribable_is_guest($options) {
1355          global $DB;
1356          $this->resetAfterTest(true);
1357  
1358          $guest = $DB->get_record('user', array('username'=>'guest'));
1359          $this->setUser($guest);
1360  
1361          // Create a course, with a forum.
1362          $course = $this->getDataGenerator()->create_course();
1363          $options['course'] = $course->id;
1364          $forum = $this->getDataGenerator()->create_module('forum', $options);
1365  
1366          $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
1367      }
1368  
1369      public function is_subscribable_loggedin_provider() {
1370          return [
1371              [
1372                  ['forcesubscribe' => FORUM_DISALLOWSUBSCRIBE],
1373                  false,
1374              ],
1375              [
1376                  ['forcesubscribe' => FORUM_CHOOSESUBSCRIBE],
1377                  true,
1378              ],
1379              [
1380                  ['forcesubscribe' => FORUM_INITIALSUBSCRIBE],
1381                  true,
1382              ],
1383              [
1384                  ['forcesubscribe' => FORUM_FORCESUBSCRIBE],
1385                  false,
1386              ],
1387          ];
1388      }
1389  
1390      /**
1391       * @dataProvider is_subscribable_loggedin_provider
1392       */
1393      public function test_is_subscribable_loggedin($options, $expect) {
1394          $this->resetAfterTest(true);
1395  
1396          // Create a course, with a forum.
1397          $course = $this->getDataGenerator()->create_course();
1398          $options['course'] = $course->id;
1399          $forum = $this->getDataGenerator()->create_module('forum', $options);
1400  
1401          $user = $this->getDataGenerator()->create_user();
1402          $this->getDataGenerator()->enrol_user($user->id, $course->id);
1403          $this->setUser($user);
1404  
1405          $this->assertEquals($expect, \mod_forum\subscriptions::is_subscribable($forum));
1406      }
1407  
1408      public function test_get_user_default_subscription() {
1409          global $DB;
1410          $this->resetAfterTest(true);
1411  
1412          // Create a course, with a forum.
1413          $course = $this->getDataGenerator()->create_course();
1414          $context = \context_course::instance($course->id);
1415          $options['course'] = $course->id;
1416          $forum = $this->getDataGenerator()->create_module('forum', $options);
1417          $cm = get_coursemodule_from_instance("forum", $forum->id, $course->id);
1418  
1419          // Create a user enrolled in the course as a student.
1420          list($author, $student) = $this->helper_create_users($course, 2, 'student');
1421          // Post a discussion to the forum.
1422          list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1423  
1424          // A guest user.
1425          $this->setUser(0);
1426          $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
1427          $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
1428  
1429          // A user enrolled in the course.
1430          $this->setUser($author->id);
1431          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
1432          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
1433  
1434          // Subscribption disabled.
1435          $this->setUser($student->id);
1436          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_DISALLOWSUBSCRIBE);
1437          $forum = $DB->get_record('forum', array('id' => $forum->id));
1438          $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
1439          $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
1440  
1441          \mod_forum\subscriptions::set_subscription_mode($forum->id, FORUM_FORCESUBSCRIBE);
1442          $forum = $DB->get_record('forum', array('id' => $forum->id));
1443          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
1444          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
1445  
1446          // Admin user.
1447          $this->setAdminUser();
1448          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
1449          $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
1450      }
1451  }