Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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