Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 402] [Versions 39 and 311]

   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_generator;
  20  
  21  defined('MOODLE_INTERNAL') || die();
  22  
  23  global $CFG;
  24  require_once($CFG->dirroot . '/mod/forum/lib.php');
  25  require_once($CFG->dirroot . '/mod/forum/locallib.php');
  26  require_once($CFG->dirroot . '/rating/lib.php');
  27  
  28  /**
  29   * The mod_forum lib.php tests.
  30   *
  31   * @package    mod_forum
  32   * @copyright  2013 Frédéric Massart
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class lib_test extends \advanced_testcase {
  36  
  37      public function setUp(): void {
  38          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  39          // tests using these functions.
  40          \mod_forum\subscriptions::reset_forum_cache();
  41      }
  42  
  43      public function tearDown(): void {
  44          // We must clear the subscription caches. This has to be done both before each test, and after in case of other
  45          // tests using these functions.
  46          \mod_forum\subscriptions::reset_forum_cache();
  47      }
  48  
  49      public function test_forum_trigger_content_uploaded_event() {
  50          $this->resetAfterTest();
  51  
  52          $user = $this->getDataGenerator()->create_user();
  53          $course = $this->getDataGenerator()->create_course();
  54          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
  55          $context = \context_module::instance($forum->cmid);
  56  
  57          $this->setUser($user->id);
  58          $fakepost = (object) array('id' => 123, 'message' => 'Yay!', 'discussion' => 100);
  59          $cm = get_coursemodule_from_instance('forum', $forum->id);
  60  
  61          $fs = get_file_storage();
  62          $dummy = (object) array(
  63              'contextid' => $context->id,
  64              'component' => 'mod_forum',
  65              'filearea' => 'attachment',
  66              'itemid' => $fakepost->id,
  67              'filepath' => '/',
  68              'filename' => 'myassignmnent.pdf'
  69          );
  70          $fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
  71  
  72          $data = new \stdClass();
  73          $sink = $this->redirectEvents();
  74          forum_trigger_content_uploaded_event($fakepost, $cm, 'some triggered from value');
  75          $events = $sink->get_events();
  76  
  77          $this->assertCount(1, $events);
  78          $event = reset($events);
  79          $this->assertInstanceOf('\mod_forum\event\assessable_uploaded', $event);
  80          $this->assertEquals($context->id, $event->contextid);
  81          $this->assertEquals($fakepost->id, $event->objectid);
  82          $this->assertEquals($fakepost->message, $event->other['content']);
  83          $this->assertEquals($fakepost->discussion, $event->other['discussionid']);
  84          $this->assertCount(1, $event->other['pathnamehashes']);
  85          $this->assertEquals($fi->get_pathnamehash(), $event->other['pathnamehashes'][0]);
  86          $expected = new \stdClass();
  87          $expected->modulename = 'forum';
  88          $expected->name = 'some triggered from value';
  89          $expected->cmid = $forum->cmid;
  90          $expected->itemid = $fakepost->id;
  91          $expected->courseid = $course->id;
  92          $expected->userid = $user->id;
  93          $expected->content = $fakepost->message;
  94          $expected->pathnamehashes = array($fi->get_pathnamehash());
  95          $this->assertEventLegacyData($expected, $event);
  96          $this->assertEventContextNotUsed($event);
  97      }
  98  
  99      public function test_forum_get_courses_user_posted_in() {
 100          $this->resetAfterTest();
 101  
 102          $user1 = $this->getDataGenerator()->create_user();
 103          $user2 = $this->getDataGenerator()->create_user();
 104          $user3 = $this->getDataGenerator()->create_user();
 105  
 106          $course1 = $this->getDataGenerator()->create_course();
 107          $course2 = $this->getDataGenerator()->create_course();
 108          $course3 = $this->getDataGenerator()->create_course();
 109  
 110          // Create 3 forums, one in each course.
 111          $record = new \stdClass();
 112          $record->course = $course1->id;
 113          $forum1 = $this->getDataGenerator()->create_module('forum', $record);
 114  
 115          $record = new \stdClass();
 116          $record->course = $course2->id;
 117          $forum2 = $this->getDataGenerator()->create_module('forum', $record);
 118  
 119          $record = new \stdClass();
 120          $record->course = $course3->id;
 121          $forum3 = $this->getDataGenerator()->create_module('forum', $record);
 122  
 123          // Add a second forum in course 1.
 124          $record = new \stdClass();
 125          $record->course = $course1->id;
 126          $forum4 = $this->getDataGenerator()->create_module('forum', $record);
 127  
 128          // Add discussions to course 1 started by user1.
 129          $record = new \stdClass();
 130          $record->course = $course1->id;
 131          $record->userid = $user1->id;
 132          $record->forum = $forum1->id;
 133          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 134  
 135          $record = new \stdClass();
 136          $record->course = $course1->id;
 137          $record->userid = $user1->id;
 138          $record->forum = $forum4->id;
 139          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 140  
 141          // Add discussions to course2 started by user1.
 142          $record = new \stdClass();
 143          $record->course = $course2->id;
 144          $record->userid = $user1->id;
 145          $record->forum = $forum2->id;
 146          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 147  
 148          // Add discussions to course 3 started by user2.
 149          $record = new \stdClass();
 150          $record->course = $course3->id;
 151          $record->userid = $user2->id;
 152          $record->forum = $forum3->id;
 153          $discussion3 = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 154  
 155          // Add post to course 3 by user1.
 156          $record = new \stdClass();
 157          $record->course = $course3->id;
 158          $record->userid = $user1->id;
 159          $record->forum = $forum3->id;
 160          $record->discussion = $discussion3->id;
 161          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
 162  
 163          // User 3 hasn't posted anything, so shouldn't get any results.
 164          $user3courses = forum_get_courses_user_posted_in($user3);
 165          $this->assertEmpty($user3courses);
 166  
 167          // User 2 has only posted in course3.
 168          $user2courses = forum_get_courses_user_posted_in($user2);
 169          $this->assertCount(1, $user2courses);
 170          $user2course = array_shift($user2courses);
 171          $this->assertEquals($course3->id, $user2course->id);
 172          $this->assertEquals($course3->shortname, $user2course->shortname);
 173  
 174          // User 1 has posted in all 3 courses.
 175          $user1courses = forum_get_courses_user_posted_in($user1);
 176          $this->assertCount(3, $user1courses);
 177          foreach ($user1courses as $course) {
 178              $this->assertContains($course->id, array($course1->id, $course2->id, $course3->id));
 179              $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname,
 180                  $course3->shortname));
 181  
 182          }
 183  
 184          // User 1 has only started a discussion in course 1 and 2 though.
 185          $user1courses = forum_get_courses_user_posted_in($user1, true);
 186          $this->assertCount(2, $user1courses);
 187          foreach ($user1courses as $course) {
 188              $this->assertContains($course->id, array($course1->id, $course2->id));
 189              $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname));
 190          }
 191      }
 192  
 193      /**
 194       * Test the logic in the forum_tp_can_track_forums() function.
 195       */
 196      public function test_forum_tp_can_track_forums() {
 197          global $CFG;
 198  
 199          $this->resetAfterTest();
 200  
 201          $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
 202          $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
 203          $course = $this->getDataGenerator()->create_course();
 204          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
 205          $forumoff = $this->getDataGenerator()->create_module('forum', $options);
 206  
 207          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
 208          $forumforce = $this->getDataGenerator()->create_module('forum', $options);
 209  
 210          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
 211          $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
 212  
 213          // Allow force.
 214          $CFG->forum_allowforcedreadtracking = 1;
 215  
 216          // User on, forum off, should be off.
 217          $result = forum_tp_can_track_forums($forumoff, $useron);
 218          $this->assertEquals(false, $result);
 219  
 220          // User on, forum on, should be on.
 221          $result = forum_tp_can_track_forums($forumforce, $useron);
 222          $this->assertEquals(true, $result);
 223  
 224          // User on, forum optional, should be on.
 225          $result = forum_tp_can_track_forums($forumoptional, $useron);
 226          $this->assertEquals(true, $result);
 227  
 228          // User off, forum off, should be off.
 229          $result = forum_tp_can_track_forums($forumoff, $useroff);
 230          $this->assertEquals(false, $result);
 231  
 232          // User off, forum force, should be on.
 233          $result = forum_tp_can_track_forums($forumforce, $useroff);
 234          $this->assertEquals(true, $result);
 235  
 236          // User off, forum optional, should be off.
 237          $result = forum_tp_can_track_forums($forumoptional, $useroff);
 238          $this->assertEquals(false, $result);
 239  
 240          // Don't allow force.
 241          $CFG->forum_allowforcedreadtracking = 0;
 242  
 243          // User on, forum off, should be off.
 244          $result = forum_tp_can_track_forums($forumoff, $useron);
 245          $this->assertEquals(false, $result);
 246  
 247          // User on, forum on, should be on.
 248          $result = forum_tp_can_track_forums($forumforce, $useron);
 249          $this->assertEquals(true, $result);
 250  
 251          // User on, forum optional, should be on.
 252          $result = forum_tp_can_track_forums($forumoptional, $useron);
 253          $this->assertEquals(true, $result);
 254  
 255          // User off, forum off, should be off.
 256          $result = forum_tp_can_track_forums($forumoff, $useroff);
 257          $this->assertEquals(false, $result);
 258  
 259          // User off, forum force, should be off.
 260          $result = forum_tp_can_track_forums($forumforce, $useroff);
 261          $this->assertEquals(false, $result);
 262  
 263          // User off, forum optional, should be off.
 264          $result = forum_tp_can_track_forums($forumoptional, $useroff);
 265          $this->assertEquals(false, $result);
 266  
 267      }
 268  
 269      /**
 270       * Test the logic in the test_forum_tp_is_tracked() function.
 271       */
 272      public function test_forum_tp_is_tracked() {
 273          global $CFG;
 274  
 275          $this->resetAfterTest();
 276  
 277          $cache = \cache::make('mod_forum', 'forum_is_tracked');
 278          $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
 279          $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
 280          $course = $this->getDataGenerator()->create_course();
 281          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
 282          $forumoff = $this->getDataGenerator()->create_module('forum', $options);
 283  
 284          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
 285          $forumforce = $this->getDataGenerator()->create_module('forum', $options);
 286  
 287          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
 288          $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
 289  
 290          // Allow force.
 291          $CFG->forum_allowforcedreadtracking = 1;
 292  
 293          // User on, forum off, should be off.
 294          $result = forum_tp_is_tracked($forumoff, $useron);
 295          $this->assertEquals(false, $result);
 296  
 297          // User on, forum force, should be on.
 298          $result = forum_tp_is_tracked($forumforce, $useron);
 299          $this->assertEquals(true, $result);
 300  
 301          // User on, forum optional, should be on.
 302          $result = forum_tp_is_tracked($forumoptional, $useron);
 303          $this->assertEquals(true, $result);
 304  
 305          // User off, forum off, should be off.
 306          $result = forum_tp_is_tracked($forumoff, $useroff);
 307          $this->assertEquals(false, $result);
 308  
 309          // User off, forum force, should be on.
 310          $result = forum_tp_is_tracked($forumforce, $useroff);
 311          $this->assertEquals(true, $result);
 312  
 313          // User off, forum optional, should be off.
 314          $result = forum_tp_is_tracked($forumoptional, $useroff);
 315          $this->assertEquals(false, $result);
 316  
 317          $cache->purge();
 318          // Don't allow force.
 319          $CFG->forum_allowforcedreadtracking = 0;
 320  
 321          // User on, forum off, should be off.
 322          $result = forum_tp_is_tracked($forumoff, $useron);
 323          $this->assertEquals(false, $result);
 324  
 325          // User on, forum force, should be on.
 326          $result = forum_tp_is_tracked($forumforce, $useron);
 327          $this->assertEquals(true, $result);
 328  
 329          // User on, forum optional, should be on.
 330          $result = forum_tp_is_tracked($forumoptional, $useron);
 331          $this->assertEquals(true, $result);
 332  
 333          // User off, forum off, should be off.
 334          $result = forum_tp_is_tracked($forumoff, $useroff);
 335          $this->assertEquals(false, $result);
 336  
 337          // User off, forum force, should be off.
 338          $result = forum_tp_is_tracked($forumforce, $useroff);
 339          $this->assertEquals(false, $result);
 340  
 341          // User off, forum optional, should be off.
 342          $result = forum_tp_is_tracked($forumoptional, $useroff);
 343          $this->assertEquals(false, $result);
 344  
 345          // Stop tracking so we can test again.
 346          forum_tp_stop_tracking($forumforce->id, $useron->id);
 347          forum_tp_stop_tracking($forumoptional->id, $useron->id);
 348          forum_tp_stop_tracking($forumforce->id, $useroff->id);
 349          forum_tp_stop_tracking($forumoptional->id, $useroff->id);
 350  
 351          $cache->purge();
 352          // Allow force.
 353          $CFG->forum_allowforcedreadtracking = 1;
 354  
 355          // User on, preference off, forum force, should be on.
 356          $result = forum_tp_is_tracked($forumforce, $useron);
 357          $this->assertEquals(true, $result);
 358  
 359          // User on, preference off, forum optional, should be on.
 360          $result = forum_tp_is_tracked($forumoptional, $useron);
 361          $this->assertEquals(false, $result);
 362  
 363          // User off, preference off, forum force, should be on.
 364          $result = forum_tp_is_tracked($forumforce, $useroff);
 365          $this->assertEquals(true, $result);
 366  
 367          // User off, preference off, forum optional, should be off.
 368          $result = forum_tp_is_tracked($forumoptional, $useroff);
 369          $this->assertEquals(false, $result);
 370  
 371          $cache->purge();
 372          // Don't allow force.
 373          $CFG->forum_allowforcedreadtracking = 0;
 374  
 375          // User on, preference off, forum force, should be on.
 376          $result = forum_tp_is_tracked($forumforce, $useron);
 377          $this->assertEquals(false, $result);
 378  
 379          // User on, preference off, forum optional, should be on.
 380          $result = forum_tp_is_tracked($forumoptional, $useron);
 381          $this->assertEquals(false, $result);
 382  
 383          // User off, preference off, forum force, should be off.
 384          $result = forum_tp_is_tracked($forumforce, $useroff);
 385          $this->assertEquals(false, $result);
 386  
 387          // User off, preference off, forum optional, should be off.
 388          $result = forum_tp_is_tracked($forumoptional, $useroff);
 389          $this->assertEquals(false, $result);
 390      }
 391  
 392      /**
 393       * Test the logic in the forum_tp_get_course_unread_posts() function.
 394       */
 395      public function test_forum_tp_get_course_unread_posts() {
 396          global $CFG;
 397  
 398          $this->resetAfterTest();
 399  
 400          $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
 401          $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
 402          $course = $this->getDataGenerator()->create_course();
 403          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
 404          $forumoff = $this->getDataGenerator()->create_module('forum', $options);
 405  
 406          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
 407          $forumforce = $this->getDataGenerator()->create_module('forum', $options);
 408  
 409          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
 410          $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
 411  
 412          // Add discussions to the tracking off forum.
 413          $record = new \stdClass();
 414          $record->course = $course->id;
 415          $record->userid = $useron->id;
 416          $record->forum = $forumoff->id;
 417          $discussionoff = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 418  
 419          // Add discussions to the tracking forced forum.
 420          $record = new \stdClass();
 421          $record->course = $course->id;
 422          $record->userid = $useron->id;
 423          $record->forum = $forumforce->id;
 424          $discussionforce = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 425  
 426          // Add post to the tracking forced discussion.
 427          $record = new \stdClass();
 428          $record->course = $course->id;
 429          $record->userid = $useroff->id;
 430          $record->forum = $forumforce->id;
 431          $record->discussion = $discussionforce->id;
 432          $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
 433  
 434          // Add discussions to the tracking optional forum.
 435          $record = new \stdClass();
 436          $record->course = $course->id;
 437          $record->userid = $useron->id;
 438          $record->forum = $forumoptional->id;
 439          $discussionoptional = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
 440  
 441          // Allow force.
 442          $CFG->forum_allowforcedreadtracking = 1;
 443  
 444          $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
 445          $this->assertEquals(2, count($result));
 446          $this->assertEquals(false, isset($result[$forumoff->id]));
 447          $this->assertEquals(true, isset($result[$forumforce->id]));
 448          $this->assertEquals(2, $result[$forumforce->id]->unread);
 449          $this->assertEquals(true, isset($result[$forumoptional->id]));
 450          $this->assertEquals(1, $result[$forumoptional->id]->unread);
 451  
 452          $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
 453          $this->assertEquals(1, count($result));
 454          $this->assertEquals(false, isset($result[$forumoff->id]));
 455          $this->assertEquals(true, isset($result[$forumforce->id]));
 456          $this->assertEquals(2, $result[$forumforce->id]->unread);
 457          $this->assertEquals(false, isset($result[$forumoptional->id]));
 458  
 459          // Don't allow force.
 460          $CFG->forum_allowforcedreadtracking = 0;
 461  
 462          $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
 463          $this->assertEquals(2, count($result));
 464          $this->assertEquals(false, isset($result[$forumoff->id]));
 465          $this->assertEquals(true, isset($result[$forumforce->id]));
 466          $this->assertEquals(2, $result[$forumforce->id]->unread);
 467          $this->assertEquals(true, isset($result[$forumoptional->id]));
 468          $this->assertEquals(1, $result[$forumoptional->id]->unread);
 469  
 470          $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
 471          $this->assertEquals(0, count($result));
 472          $this->assertEquals(false, isset($result[$forumoff->id]));
 473          $this->assertEquals(false, isset($result[$forumforce->id]));
 474          $this->assertEquals(false, isset($result[$forumoptional->id]));
 475  
 476          // Stop tracking so we can test again.
 477          forum_tp_stop_tracking($forumforce->id, $useron->id);
 478          forum_tp_stop_tracking($forumoptional->id, $useron->id);
 479          forum_tp_stop_tracking($forumforce->id, $useroff->id);
 480          forum_tp_stop_tracking($forumoptional->id, $useroff->id);
 481  
 482          // Allow force.
 483          $CFG->forum_allowforcedreadtracking = 1;
 484  
 485          $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
 486          $this->assertEquals(1, count($result));
 487          $this->assertEquals(false, isset($result[$forumoff->id]));
 488          $this->assertEquals(true, isset($result[$forumforce->id]));
 489          $this->assertEquals(2, $result[$forumforce->id]->unread);
 490          $this->assertEquals(false, isset($result[$forumoptional->id]));
 491  
 492          $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
 493          $this->assertEquals(1, count($result));
 494          $this->assertEquals(false, isset($result[$forumoff->id]));
 495          $this->assertEquals(true, isset($result[$forumforce->id]));
 496          $this->assertEquals(2, $result[$forumforce->id]->unread);
 497          $this->assertEquals(false, isset($result[$forumoptional->id]));
 498  
 499          // Don't allow force.
 500          $CFG->forum_allowforcedreadtracking = 0;
 501  
 502          $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
 503          $this->assertEquals(0, count($result));
 504          $this->assertEquals(false, isset($result[$forumoff->id]));
 505          $this->assertEquals(false, isset($result[$forumforce->id]));
 506          $this->assertEquals(false, isset($result[$forumoptional->id]));
 507  
 508          $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
 509          $this->assertEquals(0, count($result));
 510          $this->assertEquals(false, isset($result[$forumoff->id]));
 511          $this->assertEquals(false, isset($result[$forumforce->id]));
 512          $this->assertEquals(false, isset($result[$forumoptional->id]));
 513      }
 514  
 515      /**
 516       * Test the logic in the forum_tp_get_course_unread_posts() function when private replies are present.
 517       *
 518       * @covers ::forum_tp_get_course_unread_posts
 519       */
 520      public function test_forum_tp_get_course_unread_posts_with_private_replies() {
 521          global $DB;
 522  
 523          $this->resetAfterTest();
 524  
 525          $generator = $this->getDataGenerator();
 526  
 527          // Create 3 students.
 528          $s1 = $generator->create_user(['trackforums' => 1]);
 529          $s2 = $generator->create_user(['trackforums' => 1]);
 530          $s3 = $generator->create_user(['trackforums' => 1]);
 531          // Editing teacher.
 532          $t1 = $generator->create_user(['trackforums' => 1]);
 533          // Non-editing teacher.
 534          $t2 = $generator->create_user(['trackforums' => 1]);
 535  
 536          // Create our course.
 537          $course = $generator->create_course();
 538  
 539          // Enrol editing and non-editing teachers.
 540          $generator->enrol_user($t1->id, $course->id, 'editingteacher');
 541          $generator->enrol_user($t2->id, $course->id, 'teacher');
 542  
 543          // Create forums.
 544          $forum1 = $generator->create_module('forum', ['course' => $course->id]);
 545          $forum2 = $generator->create_module('forum', ['course' => $course->id]);
 546          $forumgenerator = $generator->get_plugin_generator('mod_forum');
 547  
 548          // Prevent the non-editing teacher from reading private replies in forum 2.
 549          $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']);
 550          $forum2cm = get_coursemodule_from_instance('forum', $forum2->id);
 551          $forum2context = \context_module::instance($forum2cm->id);
 552          role_change_permission($teacherroleid, $forum2context, 'mod/forum:readprivatereplies', CAP_PREVENT);
 553  
 554          // Create discussion by s1.
 555          $discussiondata = (object)[
 556              'course' => $course->id,
 557              'forum' => $forum1->id,
 558              'userid' => $s1->id,
 559          ];
 560          $discussion1 = $forumgenerator->create_discussion($discussiondata);
 561  
 562          // Create discussion by s2.
 563          $discussiondata->userid = $s2->id;
 564          $discussion2 = $forumgenerator->create_discussion($discussiondata);
 565  
 566          // Create discussion by s3.
 567          $discussiondata->userid = $s3->id;
 568          $discussion3 = $forumgenerator->create_discussion($discussiondata);
 569  
 570          // Post a normal reply to s1's discussion in forum 1 as the editing teacher.
 571          $replydata = (object)[
 572              'course' => $course->id,
 573              'forum' => $forum1->id,
 574              'discussion' => $discussion1->id,
 575              'userid' => $t1->id,
 576          ];
 577          $forumgenerator->create_post($replydata);
 578  
 579          // Post a normal reply to s2's discussion as the editing teacher.
 580          $replydata->discussion = $discussion2->id;
 581          $forumgenerator->create_post($replydata);
 582  
 583          // Post a normal reply to s3's discussion as the editing teacher.
 584          $replydata->discussion = $discussion3->id;
 585          $forumgenerator->create_post($replydata);
 586  
 587          // Post a private reply to s1's discussion in forum 1 as the editing teacher.
 588          $replydata->discussion = $discussion1->id;
 589          $replydata->userid = $t1->id;
 590          $replydata->privatereplyto = $s1->id;
 591          $forumgenerator->create_post($replydata);
 592          // Post another private reply to s1 as the teacher.
 593          $forumgenerator->create_post($replydata);
 594  
 595          // Post a private reply to s2's discussion as the editing teacher.
 596          $replydata->discussion = $discussion2->id;
 597          $replydata->privatereplyto = $s2->id;
 598          $forumgenerator->create_post($replydata);
 599  
 600          // Create discussion by s1 in forum 2.
 601          $discussiondata->forum = $forum2->id;
 602          $discussiondata->userid = $s1->id;
 603          $discussion21 = $forumgenerator->create_discussion($discussiondata);
 604  
 605          // Post a private reply to s1's discussion in forum 2 as the editing teacher.
 606          $replydata->discussion = $discussion21->id;
 607          $replydata->privatereplyto = $s1->id;
 608          $forumgenerator->create_post($replydata);
 609  
 610          // Let's count!
 611          // S1 should see 8 unread posts 3 discussions posts + 2 private replies + 3 normal replies.
 612          $result = forum_tp_get_course_unread_posts($s1->id, $course->id);
 613          $unreadcounts = $result[$forum1->id];
 614          $this->assertEquals(8, $unreadcounts->unread);
 615  
 616          // S2 should see 7 unread posts 3 discussions posts + 1 private reply + 3 normal replies.
 617          $result = forum_tp_get_course_unread_posts($s2->id, $course->id);
 618          $unreadcounts = $result[$forum1->id];
 619          $this->assertEquals(7, $unreadcounts->unread);
 620  
 621          // S3 should see 6 unread posts 3 discussions posts + 3 normal replies. No private replies.
 622          $result = forum_tp_get_course_unread_posts($s3->id, $course->id);
 623          $unreadcounts = $result[$forum1->id];
 624          $this->assertEquals(6, $unreadcounts->unread);
 625  
 626          // The editing teacher should see 9 unread posts in forum 1: 3 discussions posts + 3 normal replies + 3 private replies.
 627          $result = forum_tp_get_course_unread_posts($t1->id, $course->id);
 628          $unreadcounts = $result[$forum1->id];
 629          $this->assertEquals(9, $unreadcounts->unread);
 630  
 631          // Same with the non-editing teacher, since they can read private replies by default.
 632          $result = forum_tp_get_course_unread_posts($t2->id, $course->id);
 633          $unreadcounts = $result[$forum1->id];
 634          $this->assertEquals(9, $unreadcounts->unread);
 635  
 636          // But for forum 2, the non-editing teacher should only see 1 unread which is s1's discussion post.
 637          $unreadcounts = $result[$forum2->id];
 638          $this->assertEquals(1, $unreadcounts->unread);
 639      }
 640  
 641      /**
 642       * Test the logic in the forum_tp_count_forum_unread_posts() function when private replies are present but without
 643       * separate group mode. This should yield the same results returned by forum_tp_get_course_unread_posts().
 644       *
 645       * @covers ::forum_tp_count_forum_unread_posts
 646       */
 647      public function test_forum_tp_count_forum_unread_posts_with_private_replies() {
 648          global $DB;
 649  
 650          $this->resetAfterTest();
 651  
 652          $generator = $this->getDataGenerator();
 653  
 654          // Create 3 students.
 655          $s1 = $generator->create_user(['username' => 's1', 'trackforums' => 1]);
 656          $s2 = $generator->create_user(['username' => 's2', 'trackforums' => 1]);
 657          $s3 = $generator->create_user(['username' => 's3', 'trackforums' => 1]);
 658          // Editing teacher.
 659          $t1 = $generator->create_user(['username' => 't1', 'trackforums' => 1]);
 660          // Non-editing teacher.
 661          $t2 = $generator->create_user(['username' => 't2', 'trackforums' => 1]);
 662  
 663          // Create our course.
 664          $course = $generator->create_course();
 665  
 666          // Enrol editing and non-editing teachers.
 667          $generator->enrol_user($t1->id, $course->id, 'editingteacher');
 668          $generator->enrol_user($t2->id, $course->id, 'teacher');
 669  
 670          // Create forums.
 671          $forum1 = $generator->create_module('forum', ['course' => $course->id]);
 672          $forum2 = $generator->create_module('forum', ['course' => $course->id]);
 673          $forumgenerator = $generator->get_plugin_generator('mod_forum');
 674  
 675          // Prevent the non-editing teacher from reading private replies in forum 2.
 676          $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']);
 677          $forum2cm = get_coursemodule_from_instance('forum', $forum2->id);
 678          $forum2context = \context_module::instance($forum2cm->id);
 679          role_change_permission($teacherroleid, $forum2context, 'mod/forum:readprivatereplies', CAP_PREVENT);
 680  
 681          // Create discussion by s1.
 682          $discussiondata = (object)[
 683              'course' => $course->id,
 684              'forum' => $forum1->id,
 685              'userid' => $s1->id,
 686          ];
 687          $discussion1 = $forumgenerator->create_discussion($discussiondata);
 688  
 689          // Create discussion by s2.
 690          $discussiondata->userid = $s2->id;
 691          $discussion2 = $forumgenerator->create_discussion($discussiondata);
 692  
 693          // Create discussion by s3.
 694          $discussiondata->userid = $s3->id;
 695          $discussion3 = $forumgenerator->create_discussion($discussiondata);
 696  
 697          // Post a normal reply to s1's discussion in forum 1 as the editing teacher.
 698          $replydata = (object)[
 699              'course' => $course->id,
 700              'forum' => $forum1->id,
 701              'discussion' => $discussion1->id,
 702              'userid' => $t1->id,
 703          ];
 704          $forumgenerator->create_post($replydata);
 705  
 706          // Post a normal reply to s2's discussion as the editing teacher.
 707          $replydata->discussion = $discussion2->id;
 708          $forumgenerator->create_post($replydata);
 709  
 710          // Post a normal reply to s3's discussion as the editing teacher.
 711          $replydata->discussion = $discussion3->id;
 712          $forumgenerator->create_post($replydata);
 713  
 714          // Post a private reply to s1's discussion in forum 1 as the editing teacher.
 715          $replydata->discussion = $discussion1->id;
 716          $replydata->userid = $t1->id;
 717          $replydata->privatereplyto = $s1->id;
 718          $forumgenerator->create_post($replydata);
 719          // Post another private reply to s1 as the teacher.
 720          $forumgenerator->create_post($replydata);
 721  
 722          // Post a private reply to s2's discussion as the editing teacher.
 723          $replydata->discussion = $discussion2->id;
 724          $replydata->privatereplyto = $s2->id;
 725          $forumgenerator->create_post($replydata);
 726  
 727          // Create discussion by s1 in forum 2.
 728          $discussiondata->forum = $forum2->id;
 729          $discussiondata->userid = $s1->id;
 730          $discussion11 = $forumgenerator->create_discussion($discussiondata);
 731  
 732          // Post a private reply to s1's discussion in forum 2 as the editing teacher.
 733          $replydata->discussion = $discussion11->id;
 734          $replydata->privatereplyto = $s1->id;
 735          $forumgenerator->create_post($replydata);
 736  
 737          // Let's count!
 738          // S1 should see 8 unread posts 3 discussions posts + 2 private replies + 3 normal replies.
 739          $this->setUser($s1);
 740          $forum1cm = get_coursemodule_from_instance('forum', $forum1->id);
 741          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 742          $this->assertEquals(8, $result);
 743  
 744          // S2 should see 7 unread posts 3 discussions posts + 1 private reply + 3 normal replies.
 745          $this->setUser($s2);
 746          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 747          $this->assertEquals(7, $result);
 748  
 749          // S3 should see 6 unread posts 3 discussions posts + 3 normal replies. No private replies.
 750          $this->setUser($s3);
 751          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 752          $this->assertEquals(6, $result);
 753  
 754          // The editing teacher should see 9 unread posts in forum 1: 3 discussions posts + 3 normal replies + 3 private replies.
 755          $this->setUser($t1);
 756          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 757          $this->assertEquals(9, $result);
 758  
 759          // Same with the non-editing teacher, since they can read private replies by default.
 760          $this->setUser($t2);
 761          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 762          $this->assertEquals(9, $result);
 763  
 764          // But for forum 2, the non-editing teacher should only see 1 unread which is s1's discussion post.
 765          $result = forum_tp_count_forum_unread_posts($forum2cm, $course);
 766          $this->assertEquals(1, $result);
 767      }
 768  
 769      /**
 770       * Test the logic in the forum_tp_count_forum_unread_posts() function when private replies are present and group modes are set.
 771       *
 772       * @covers ::forum_tp_count_forum_unread_posts
 773       */
 774      public function test_forum_tp_count_forum_unread_posts_with_private_replies_and_separate_groups() {
 775          $this->resetAfterTest();
 776  
 777          $generator = $this->getDataGenerator();
 778  
 779          // Create 3 students.
 780          $s1 = $generator->create_user(['username' => 's1', 'trackforums' => 1]);
 781          $s2 = $generator->create_user(['username' => 's2', 'trackforums' => 1]);
 782          // Editing teacher.
 783          $t1 = $generator->create_user(['username' => 't1', 'trackforums' => 1]);
 784  
 785          // Create our course.
 786          $course = $generator->create_course();
 787  
 788          // Enrol students, editing and non-editing teachers.
 789          $generator->enrol_user($s1->id, $course->id, 'student');
 790          $generator->enrol_user($s2->id, $course->id, 'student');
 791          $generator->enrol_user($t1->id, $course->id, 'editingteacher');
 792  
 793          // Create groups.
 794          $g1 = $generator->create_group(['courseid' => $course->id]);
 795          $g2 = $generator->create_group(['courseid' => $course->id]);
 796          $generator->create_group_member(['groupid' => $g1->id, 'userid' => $s1->id]);
 797          $generator->create_group_member(['groupid' => $g2->id, 'userid' => $s2->id]);
 798  
 799          // Create forums.
 800          $forum1 = $generator->create_module('forum', ['course' => $course->id, 'groupmode' => SEPARATEGROUPS]);
 801          $forum2 = $generator->create_module('forum', ['course' => $course->id, 'groupmode' => VISIBLEGROUPS]);
 802          $forumgenerator = $generator->get_plugin_generator('mod_forum');
 803  
 804          // Create discussion by s1.
 805          $discussiondata = (object)[
 806              'course' => $course->id,
 807              'forum' => $forum1->id,
 808              'userid' => $s1->id,
 809              'groupid' => $g1->id,
 810          ];
 811          $discussion1 = $forumgenerator->create_discussion($discussiondata);
 812  
 813          // Create discussion by s2.
 814          $discussiondata->userid = $s2->id;
 815          $discussiondata->groupid = $g2->id;
 816          $discussion2 = $forumgenerator->create_discussion($discussiondata);
 817  
 818          // Post a normal reply to s1's discussion in forum 1 as the editing teacher.
 819          $replydata = (object)[
 820              'course' => $course->id,
 821              'forum' => $forum1->id,
 822              'discussion' => $discussion1->id,
 823              'userid' => $t1->id,
 824          ];
 825          $forumgenerator->create_post($replydata);
 826  
 827          // Post a normal reply to s2's discussion as the editing teacher.
 828          $replydata->discussion = $discussion2->id;
 829          $forumgenerator->create_post($replydata);
 830  
 831          // Post a private reply to s1's discussion in forum 1 as the editing teacher.
 832          $replydata->discussion = $discussion1->id;
 833          $replydata->userid = $t1->id;
 834          $replydata->privatereplyto = $s1->id;
 835          $forumgenerator->create_post($replydata);
 836          // Post another private reply to s1 as the teacher.
 837          $forumgenerator->create_post($replydata);
 838  
 839          // Post a private reply to s2's discussion as the editing teacher.
 840          $replydata->discussion = $discussion2->id;
 841          $replydata->privatereplyto = $s2->id;
 842          $forumgenerator->create_post($replydata);
 843  
 844          // Create discussion by s1 in forum 2.
 845          $discussiondata->forum = $forum2->id;
 846          $discussiondata->userid = $s1->id;
 847          $discussiondata->groupid = $g1->id;
 848          $discussion21 = $forumgenerator->create_discussion($discussiondata);
 849  
 850          // Post a private reply to s1's discussion in forum 2 as the editing teacher.
 851          $replydata->discussion = $discussion21->id;
 852          $replydata->privatereplyto = $s1->id;
 853          $forumgenerator->create_post($replydata);
 854  
 855          // Let's count!
 856          // S1 should see 4 unread posts in forum 1 (1 discussions post + 2 private replies + 1 normal reply).
 857          $this->setUser($s1);
 858          $forum1cm = get_coursemodule_from_instance('forum', $forum1->id);
 859          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 860          $this->assertEquals(4, $result);
 861  
 862          // S2 should see 3 unread posts in forum 1 (1 discussions post + 1 private reply + 1 normal reply).
 863          $this->setUser($s2);
 864          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 865          $this->assertEquals(3, $result);
 866  
 867          // S2 should see 1 unread posts in forum 2 (visible groups, 1 discussion post from s1).
 868          $forum2cm = get_coursemodule_from_instance('forum', $forum2->id);
 869          $result = forum_tp_count_forum_unread_posts($forum2cm, $course, true);
 870          $this->assertEquals(1, $result);
 871  
 872          // The editing teacher should still see 7 unread posts (2 discussions posts + 2 normal replies + 3 private replies)
 873          // in forum 1 since they have the capability to view all groups by default.
 874          $this->setUser($t1);
 875          $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true);
 876          $this->assertEquals(7, $result);
 877      }
 878  
 879      /**
 880       * Test the logic in the test_forum_tp_get_untracked_forums() function.
 881       */
 882      public function test_forum_tp_get_untracked_forums() {
 883          global $CFG;
 884  
 885          $this->resetAfterTest();
 886  
 887          $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
 888          $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
 889          $course = $this->getDataGenerator()->create_course();
 890          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
 891          $forumoff = $this->getDataGenerator()->create_module('forum', $options);
 892  
 893          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
 894          $forumforce = $this->getDataGenerator()->create_module('forum', $options);
 895  
 896          $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
 897          $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
 898  
 899          // Allow force.
 900          $CFG->forum_allowforcedreadtracking = 1;
 901  
 902          // On user with force on.
 903          $result = forum_tp_get_untracked_forums($useron->id, $course->id);
 904          $this->assertEquals(1, count($result));
 905          $this->assertEquals(true, isset($result[$forumoff->id]));
 906  
 907          // Off user with force on.
 908          $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
 909          $this->assertEquals(2, count($result));
 910          $this->assertEquals(true, isset($result[$forumoff->id]));
 911          $this->assertEquals(true, isset($result[$forumoptional->id]));
 912  
 913          // Don't allow force.
 914          $CFG->forum_allowforcedreadtracking = 0;
 915  
 916          // On user with force off.
 917          $result = forum_tp_get_untracked_forums($useron->id, $course->id);
 918          $this->assertEquals(1, count($result));
 919          $this->assertEquals(true, isset($result[$forumoff->id]));
 920  
 921          // Off user with force off.
 922          $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
 923          $this->assertEquals(3, count($result));
 924          $this->assertEquals(true, isset($result[$forumoff->id]));
 925          $this->assertEquals(true, isset($result[$forumoptional->id]));
 926          $this->assertEquals(true, isset($result[$forumforce->id]));
 927  
 928          // Stop tracking so we can test again.
 929          forum_tp_stop_tracking($forumforce->id, $useron->id);
 930          forum_tp_stop_tracking($forumoptional->id, $useron->id);
 931          forum_tp_stop_tracking($forumforce->id, $useroff->id);
 932          forum_tp_stop_tracking($forumoptional->id, $useroff->id);
 933  
 934          // Allow force.
 935          $CFG->forum_allowforcedreadtracking = 1;
 936  
 937          // On user with force on.
 938          $result = forum_tp_get_untracked_forums($useron->id, $course->id);
 939          $this->assertEquals(2, count($result));
 940          $this->assertEquals(true, isset($result[$forumoff->id]));
 941          $this->assertEquals(true, isset($result[$forumoptional->id]));
 942  
 943          // Off user with force on.
 944          $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
 945          $this->assertEquals(2, count($result));
 946          $this->assertEquals(true, isset($result[$forumoff->id]));
 947          $this->assertEquals(true, isset($result[$forumoptional->id]));
 948  
 949          // Don't allow force.
 950          $CFG->forum_allowforcedreadtracking = 0;
 951  
 952          // On user with force off.
 953          $result = forum_tp_get_untracked_forums($useron->id, $course->id);
 954          $this->assertEquals(3, count($result));
 955          $this->assertEquals(true, isset($result[$forumoff->id]));
 956          $this->assertEquals(true, isset($result[$forumoptional->id]));
 957          $this->assertEquals(true, isset($result[$forumforce->id]));
 958  
 959          // Off user with force off.
 960          $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
 961          $this->assertEquals(3, count($result));
 962          $this->assertEquals(true, isset($result[$forumoff->id]));
 963          $this->assertEquals(true, isset($result[$forumoptional->id]));
 964          $this->assertEquals(true, isset($result[$forumforce->id]));
 965      }
 966  
 967      /**
 968       * Test subscription using automatic subscription on create.
 969       */
 970      public function test_forum_auto_subscribe_on_create() {
 971          global $CFG;
 972  
 973          $this->resetAfterTest();
 974  
 975          $usercount = 5;
 976          $course = $this->getDataGenerator()->create_course();
 977          $users = array();
 978  
 979          for ($i = 0; $i < $usercount; $i++) {
 980              $user = $this->getDataGenerator()->create_user();
 981              $users[] = $user;
 982              $this->getDataGenerator()->enrol_user($user->id, $course->id);
 983          }
 984  
 985          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); // Automatic Subscription.
 986          $forum = $this->getDataGenerator()->create_module('forum', $options);
 987  
 988          $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
 989          $this->assertEquals($usercount, count($result));
 990          foreach ($users as $user) {
 991              $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
 992          }
 993      }
 994  
 995      /**
 996       * Test subscription using forced subscription on create.
 997       */
 998      public function test_forum_forced_subscribe_on_create() {
 999          global $CFG;
1000  
1001          $this->resetAfterTest();
1002  
1003          $usercount = 5;
1004          $course = $this->getDataGenerator()->create_course();
1005          $users = array();
1006  
1007          for ($i = 0; $i < $usercount; $i++) {
1008              $user = $this->getDataGenerator()->create_user();
1009              $users[] = $user;
1010              $this->getDataGenerator()->enrol_user($user->id, $course->id);
1011          }
1012  
1013          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); // Forced subscription.
1014          $forum = $this->getDataGenerator()->create_module('forum', $options);
1015  
1016          $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
1017          $this->assertEquals($usercount, count($result));
1018          foreach ($users as $user) {
1019              $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1020          }
1021      }
1022  
1023      /**
1024       * Test subscription using optional subscription on create.
1025       */
1026      public function test_forum_optional_subscribe_on_create() {
1027          global $CFG;
1028  
1029          $this->resetAfterTest();
1030  
1031          $usercount = 5;
1032          $course = $this->getDataGenerator()->create_course();
1033          $users = array();
1034  
1035          for ($i = 0; $i < $usercount; $i++) {
1036              $user = $this->getDataGenerator()->create_user();
1037              $users[] = $user;
1038              $this->getDataGenerator()->enrol_user($user->id, $course->id);
1039          }
1040  
1041          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); // Subscription optional.
1042          $forum = $this->getDataGenerator()->create_module('forum', $options);
1043  
1044          $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
1045          // No subscriptions by default.
1046          $this->assertEquals(0, count($result));
1047          foreach ($users as $user) {
1048              $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1049          }
1050      }
1051  
1052      /**
1053       * Test subscription using disallow subscription on create.
1054       */
1055      public function test_forum_disallow_subscribe_on_create() {
1056          global $CFG;
1057  
1058          $this->resetAfterTest();
1059  
1060          $usercount = 5;
1061          $course = $this->getDataGenerator()->create_course();
1062          $users = array();
1063  
1064          for ($i = 0; $i < $usercount; $i++) {
1065              $user = $this->getDataGenerator()->create_user();
1066              $users[] = $user;
1067              $this->getDataGenerator()->enrol_user($user->id, $course->id);
1068          }
1069  
1070          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); // Subscription prevented.
1071          $forum = $this->getDataGenerator()->create_module('forum', $options);
1072  
1073          $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
1074          // No subscriptions by default.
1075          $this->assertEquals(0, count($result));
1076          foreach ($users as $user) {
1077              $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
1078          }
1079      }
1080  
1081      /**
1082       * Test that context fetching returns the appropriate context.
1083       */
1084      public function test_forum_get_context() {
1085          global $DB, $PAGE;
1086  
1087          $this->resetAfterTest();
1088  
1089          // Setup test data.
1090          $course = $this->getDataGenerator()->create_course();
1091          $coursecontext = \context_course::instance($course->id);
1092  
1093          $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
1094          $forum = $this->getDataGenerator()->create_module('forum', $options);
1095          $forumcm = get_coursemodule_from_instance('forum', $forum->id);
1096          $forumcontext = \context_module::instance($forumcm->id);
1097  
1098          // First check that specifying the context results in the correct context being returned.
1099          // Do this before we set up the page object and we should return from the coursemodule record.
1100          // There should be no DB queries here because the context type was correct.
1101          $startcount = $DB->perf_get_reads();
1102          $result = forum_get_context($forum->id, $forumcontext);
1103          $aftercount = $DB->perf_get_reads();
1104          $this->assertEquals($forumcontext, $result);
1105          $this->assertEquals(0, $aftercount - $startcount);
1106  
1107          // And a context which is not the correct type.
1108          // This tests will result in a DB query to fetch the course_module.
1109          $startcount = $DB->perf_get_reads();
1110          $result = forum_get_context($forum->id, $coursecontext);
1111          $aftercount = $DB->perf_get_reads();
1112          $this->assertEquals($forumcontext, $result);
1113          $this->assertEquals(1, $aftercount - $startcount);
1114  
1115          // Now do not specify a context at all.
1116          // This tests will result in a DB query to fetch the course_module.
1117          $startcount = $DB->perf_get_reads();
1118          $result = forum_get_context($forum->id);
1119          $aftercount = $DB->perf_get_reads();
1120          $this->assertEquals($forumcontext, $result);
1121          $this->assertEquals(1, $aftercount - $startcount);
1122  
1123          // Set up the default page event to use the forum.
1124          $PAGE = new \moodle_page();
1125          $PAGE->set_context($forumcontext);
1126          $PAGE->set_cm($forumcm, $course, $forum);
1127  
1128          // Now specify a context which is not a context_module.
1129          // There should be no DB queries here because we use the PAGE.
1130          $startcount = $DB->perf_get_reads();
1131          $result = forum_get_context($forum->id, $coursecontext);
1132          $aftercount = $DB->perf_get_reads();
1133          $this->assertEquals($forumcontext, $result);
1134          $this->assertEquals(0, $aftercount - $startcount);
1135  
1136          // Now do not specify a context at all.
1137          // There should be no DB queries here because we use the PAGE.
1138          $startcount = $DB->perf_get_reads();
1139          $result = forum_get_context($forum->id);
1140          $aftercount = $DB->perf_get_reads();
1141          $this->assertEquals($forumcontext, $result);
1142          $this->assertEquals(0, $aftercount - $startcount);
1143  
1144          // Now specify the page context of the course instead..
1145          $PAGE = new \moodle_page();
1146          $PAGE->set_context($coursecontext);
1147  
1148          // Now specify a context which is not a context_module.
1149          // This tests will result in a DB query to fetch the course_module.
1150          $startcount = $DB->perf_get_reads();
1151          $result = forum_get_context($forum->id, $coursecontext);
1152          $aftercount = $DB->perf_get_reads();
1153          $this->assertEquals($forumcontext, $result);
1154          $this->assertEquals(1, $aftercount - $startcount);
1155  
1156          // Now do not specify a context at all.
1157          // This tests will result in a DB query to fetch the course_module.
1158          $startcount = $DB->perf_get_reads();
1159          $result = forum_get_context($forum->id);
1160          $aftercount = $DB->perf_get_reads();
1161          $this->assertEquals($forumcontext, $result);
1162          $this->assertEquals(1, $aftercount - $startcount);
1163      }
1164  
1165      /**
1166       * Test getting the neighbour threads of a discussion.
1167       */
1168      public function test_forum_get_neighbours() {
1169          global $CFG, $DB;
1170          $this->resetAfterTest();
1171  
1172          $timenow = time();
1173          $timenext = $timenow;
1174  
1175          // Setup test data.
1176          $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1177          $course = $this->getDataGenerator()->create_course();
1178          $user = $this->getDataGenerator()->create_user();
1179          $user2 = $this->getDataGenerator()->create_user();
1180  
1181          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1182          $cm = get_coursemodule_from_instance('forum', $forum->id);
1183          $context = \context_module::instance($cm->id);
1184  
1185          $record = new \stdClass();
1186          $record->course = $course->id;
1187          $record->userid = $user->id;
1188          $record->forum = $forum->id;
1189          $record->timemodified = time();
1190          $disc1 = $forumgen->create_discussion($record);
1191          $record->timemodified++;
1192          $disc2 = $forumgen->create_discussion($record);
1193          $record->timemodified++;
1194          $disc3 = $forumgen->create_discussion($record);
1195          $record->timemodified++;
1196          $disc4 = $forumgen->create_discussion($record);
1197          $record->timemodified++;
1198          $disc5 = $forumgen->create_discussion($record);
1199  
1200          // Getting the neighbours.
1201          $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1202          $this->assertEmpty($neighbours['prev']);
1203          $this->assertEquals($disc2->id, $neighbours['next']->id);
1204  
1205          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1206          $this->assertEquals($disc1->id, $neighbours['prev']->id);
1207          $this->assertEquals($disc3->id, $neighbours['next']->id);
1208  
1209          $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1210          $this->assertEquals($disc2->id, $neighbours['prev']->id);
1211          $this->assertEquals($disc4->id, $neighbours['next']->id);
1212  
1213          $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
1214          $this->assertEquals($disc3->id, $neighbours['prev']->id);
1215          $this->assertEquals($disc5->id, $neighbours['next']->id);
1216  
1217          $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
1218          $this->assertEquals($disc4->id, $neighbours['prev']->id);
1219          $this->assertEmpty($neighbours['next']);
1220  
1221          // Post in some discussions. We manually update the discussion record because
1222          // the data generator plays with timemodified in a way that would break this test.
1223          $record->timemodified++;
1224          $disc1->timemodified = $record->timemodified;
1225          $DB->update_record('forum_discussions', $disc1);
1226  
1227          $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
1228          $this->assertEquals($disc4->id, $neighbours['prev']->id);
1229          $this->assertEquals($disc1->id, $neighbours['next']->id);
1230  
1231          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1232          $this->assertEmpty($neighbours['prev']);
1233          $this->assertEquals($disc3->id, $neighbours['next']->id);
1234  
1235          $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1236          $this->assertEquals($disc5->id, $neighbours['prev']->id);
1237          $this->assertEmpty($neighbours['next']);
1238  
1239          // After some discussions were created.
1240          $record->timemodified++;
1241          $disc6 = $forumgen->create_discussion($record);
1242          $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
1243          $this->assertEquals($disc1->id, $neighbours['prev']->id);
1244          $this->assertEmpty($neighbours['next']);
1245  
1246          $record->timemodified++;
1247          $disc7 = $forumgen->create_discussion($record);
1248          $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum);
1249          $this->assertEquals($disc6->id, $neighbours['prev']->id);
1250          $this->assertEmpty($neighbours['next']);
1251  
1252          // Adding timed discussions.
1253          $CFG->forum_enabletimedposts = true;
1254          $now = $record->timemodified;
1255          $past = $now - 600;
1256          $future = $now + 600;
1257  
1258          $record = new \stdClass();
1259          $record->course = $course->id;
1260          $record->userid = $user->id;
1261          $record->forum = $forum->id;
1262          $record->timestart = $past;
1263          $record->timeend = $future;
1264          $record->timemodified = $now;
1265          $record->timemodified++;
1266          $disc8 = $forumgen->create_discussion($record);
1267          $record->timemodified++;
1268          $record->timestart = $future;
1269          $record->timeend = 0;
1270          $disc9 = $forumgen->create_discussion($record);
1271          $record->timemodified++;
1272          $record->timestart = 0;
1273          $record->timeend = 0;
1274          $disc10 = $forumgen->create_discussion($record);
1275          $record->timemodified++;
1276          $record->timestart = 0;
1277          $record->timeend = $past;
1278          $disc11 = $forumgen->create_discussion($record);
1279          $record->timemodified++;
1280          $record->timestart = $past;
1281          $record->timeend = $future;
1282          $disc12 = $forumgen->create_discussion($record);
1283          $record->timemodified++;
1284          $record->timestart = $future + 1; // Should be last post for those that can see it.
1285          $record->timeend = 0;
1286          $disc13 = $forumgen->create_discussion($record);
1287  
1288          // Admin user ignores the timed settings of discussions.
1289          // Post ordering taking into account timestart:
1290          //  8 = t
1291          // 10 = t+3
1292          // 11 = t+4
1293          // 12 = t+5
1294          //  9 = t+60
1295          // 13 = t+61.
1296          $this->setAdminUser();
1297          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1298          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1299          $this->assertEquals($disc10->id, $neighbours['next']->id);
1300  
1301          $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1302          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1303          $this->assertEquals($disc13->id, $neighbours['next']->id);
1304  
1305          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1306          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1307          $this->assertEquals($disc11->id, $neighbours['next']->id);
1308  
1309          $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1310          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1311          $this->assertEquals($disc12->id, $neighbours['next']->id);
1312  
1313          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1314          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1315          $this->assertEquals($disc9->id, $neighbours['next']->id);
1316  
1317          $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum);
1318          $this->assertEquals($disc9->id, $neighbours['prev']->id);
1319          $this->assertEmpty($neighbours['next']);
1320  
1321          // Normal user can see their own timed discussions.
1322          $this->setUser($user);
1323          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1324          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1325          $this->assertEquals($disc10->id, $neighbours['next']->id);
1326  
1327          $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1328          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1329          $this->assertEquals($disc13->id, $neighbours['next']->id);
1330  
1331          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1332          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1333          $this->assertEquals($disc11->id, $neighbours['next']->id);
1334  
1335          $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1336          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1337          $this->assertEquals($disc12->id, $neighbours['next']->id);
1338  
1339          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1340          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1341          $this->assertEquals($disc9->id, $neighbours['next']->id);
1342  
1343          $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum);
1344          $this->assertEquals($disc9->id, $neighbours['prev']->id);
1345          $this->assertEmpty($neighbours['next']);
1346  
1347          // Normal user does not ignore timed settings.
1348          $this->setUser($user2);
1349          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1350          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1351          $this->assertEquals($disc10->id, $neighbours['next']->id);
1352  
1353          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1354          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1355          $this->assertEquals($disc12->id, $neighbours['next']->id);
1356  
1357          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1358          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1359          $this->assertEmpty($neighbours['next']);
1360  
1361          // Reset to normal mode.
1362          $CFG->forum_enabletimedposts = false;
1363          $this->setAdminUser();
1364  
1365          // Two discussions with identical timemodified will sort by id.
1366          $record->timemodified += 25;
1367          $DB->update_record('forum_discussions', (object) array('id' => $disc3->id, 'timemodified' => $record->timemodified));
1368          $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified));
1369          $DB->update_record('forum_discussions', (object) array('id' => $disc12->id, 'timemodified' => $record->timemodified - 5));
1370          $disc2 = $DB->get_record('forum_discussions', array('id' => $disc2->id));
1371          $disc3 = $DB->get_record('forum_discussions', array('id' => $disc3->id));
1372  
1373          $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1374          $this->assertEquals($disc2->id, $neighbours['prev']->id);
1375          $this->assertEmpty($neighbours['next']);
1376  
1377          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1378          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1379          $this->assertEquals($disc3->id, $neighbours['next']->id);
1380  
1381          // Set timemodified to not be identical.
1382          $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified - 1));
1383  
1384          // Test pinned posts behave correctly.
1385          $disc8->pinned = FORUM_DISCUSSION_PINNED;
1386          $DB->update_record('forum_discussions', (object) array('id' => $disc8->id, 'pinned' => $disc8->pinned));
1387          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1388          $this->assertEquals($disc3->id, $neighbours['prev']->id);
1389          $this->assertEmpty($neighbours['next']);
1390  
1391          $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1392          $this->assertEquals($disc2->id, $neighbours['prev']->id);
1393          $this->assertEquals($disc8->id, $neighbours['next']->id);
1394  
1395          // Test 3 pinned posts.
1396          $disc6->pinned = FORUM_DISCUSSION_PINNED;
1397          $DB->update_record('forum_discussions', (object) array('id' => $disc6->id, 'pinned' => $disc6->pinned));
1398          $disc4->pinned = FORUM_DISCUSSION_PINNED;
1399          $DB->update_record('forum_discussions', (object) array('id' => $disc4->id, 'pinned' => $disc4->pinned));
1400  
1401          $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
1402          $this->assertEquals($disc4->id, $neighbours['prev']->id);
1403          $this->assertEquals($disc8->id, $neighbours['next']->id);
1404  
1405          $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
1406          $this->assertEquals($disc3->id, $neighbours['prev']->id);
1407          $this->assertEquals($disc6->id, $neighbours['next']->id);
1408  
1409          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1410          $this->assertEquals($disc6->id, $neighbours['prev']->id);
1411          $this->assertEmpty($neighbours['next']);
1412      }
1413  
1414      /**
1415       * Test getting the neighbour threads of a blog-like forum.
1416       */
1417      public function test_forum_get_neighbours_blog() {
1418          global $CFG, $DB;
1419          $this->resetAfterTest();
1420  
1421          $timenow = time();
1422          $timenext = $timenow;
1423  
1424          // Setup test data.
1425          $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1426          $course = $this->getDataGenerator()->create_course();
1427          $user = $this->getDataGenerator()->create_user();
1428          $user2 = $this->getDataGenerator()->create_user();
1429  
1430          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog'));
1431          $cm = get_coursemodule_from_instance('forum', $forum->id);
1432          $context = \context_module::instance($cm->id);
1433  
1434          $record = new \stdClass();
1435          $record->course = $course->id;
1436          $record->userid = $user->id;
1437          $record->forum = $forum->id;
1438          $record->timemodified = time();
1439          $disc1 = $forumgen->create_discussion($record);
1440          $record->timemodified++;
1441          $disc2 = $forumgen->create_discussion($record);
1442          $record->timemodified++;
1443          $disc3 = $forumgen->create_discussion($record);
1444          $record->timemodified++;
1445          $disc4 = $forumgen->create_discussion($record);
1446          $record->timemodified++;
1447          $disc5 = $forumgen->create_discussion($record);
1448  
1449          // Getting the neighbours.
1450          $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1451          $this->assertEmpty($neighbours['prev']);
1452          $this->assertEquals($disc2->id, $neighbours['next']->id);
1453  
1454          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1455          $this->assertEquals($disc1->id, $neighbours['prev']->id);
1456          $this->assertEquals($disc3->id, $neighbours['next']->id);
1457  
1458          $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1459          $this->assertEquals($disc2->id, $neighbours['prev']->id);
1460          $this->assertEquals($disc4->id, $neighbours['next']->id);
1461  
1462          $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
1463          $this->assertEquals($disc3->id, $neighbours['prev']->id);
1464          $this->assertEquals($disc5->id, $neighbours['next']->id);
1465  
1466          $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
1467          $this->assertEquals($disc4->id, $neighbours['prev']->id);
1468          $this->assertEmpty($neighbours['next']);
1469  
1470          // Make sure that the thread's timemodified does not affect the order.
1471          $record->timemodified++;
1472          $disc1->timemodified = $record->timemodified;
1473          $DB->update_record('forum_discussions', $disc1);
1474  
1475          $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1476          $this->assertEmpty($neighbours['prev']);
1477          $this->assertEquals($disc2->id, $neighbours['next']->id);
1478  
1479          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1480          $this->assertEquals($disc1->id, $neighbours['prev']->id);
1481          $this->assertEquals($disc3->id, $neighbours['next']->id);
1482  
1483          // Add another blog post.
1484          $record->timemodified++;
1485          $disc6 = $forumgen->create_discussion($record);
1486          $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
1487          $this->assertEquals($disc5->id, $neighbours['prev']->id);
1488          $this->assertEmpty($neighbours['next']);
1489  
1490          $record->timemodified++;
1491          $disc7 = $forumgen->create_discussion($record);
1492          $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum);
1493          $this->assertEquals($disc6->id, $neighbours['prev']->id);
1494          $this->assertEmpty($neighbours['next']);
1495  
1496          // Adding timed discussions.
1497          $CFG->forum_enabletimedposts = true;
1498          $now = $record->timemodified;
1499          $past = $now - 600;
1500          $future = $now + 600;
1501  
1502          $record = new \stdClass();
1503          $record->course = $course->id;
1504          $record->userid = $user->id;
1505          $record->forum = $forum->id;
1506          $record->timestart = $past;
1507          $record->timeend = $future;
1508          $record->timemodified = $now;
1509          $record->timemodified++;
1510          $disc8 = $forumgen->create_discussion($record);
1511          $record->timemodified++;
1512          $record->timestart = $future;
1513          $record->timeend = 0;
1514          $disc9 = $forumgen->create_discussion($record);
1515          $record->timemodified++;
1516          $record->timestart = 0;
1517          $record->timeend = 0;
1518          $disc10 = $forumgen->create_discussion($record);
1519          $record->timemodified++;
1520          $record->timestart = 0;
1521          $record->timeend = $past;
1522          $disc11 = $forumgen->create_discussion($record);
1523          $record->timemodified++;
1524          $record->timestart = $past;
1525          $record->timeend = $future;
1526          $disc12 = $forumgen->create_discussion($record);
1527  
1528          // Admin user ignores the timed settings of discussions.
1529          $this->setAdminUser();
1530          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1531          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1532          $this->assertEquals($disc9->id, $neighbours['next']->id);
1533  
1534          $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1535          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1536          $this->assertEquals($disc10->id, $neighbours['next']->id);
1537  
1538          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1539          $this->assertEquals($disc9->id, $neighbours['prev']->id);
1540          $this->assertEquals($disc11->id, $neighbours['next']->id);
1541  
1542          $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1543          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1544          $this->assertEquals($disc12->id, $neighbours['next']->id);
1545  
1546          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1547          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1548          $this->assertEmpty($neighbours['next']);
1549  
1550          // Normal user can see their own timed discussions.
1551          $this->setUser($user);
1552          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1553          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1554          $this->assertEquals($disc9->id, $neighbours['next']->id);
1555  
1556          $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1557          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1558          $this->assertEquals($disc10->id, $neighbours['next']->id);
1559  
1560          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1561          $this->assertEquals($disc9->id, $neighbours['prev']->id);
1562          $this->assertEquals($disc11->id, $neighbours['next']->id);
1563  
1564          $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1565          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1566          $this->assertEquals($disc12->id, $neighbours['next']->id);
1567  
1568          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1569          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1570          $this->assertEmpty($neighbours['next']);
1571  
1572          // Normal user does not ignore timed settings.
1573          $this->setUser($user2);
1574          $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1575          $this->assertEquals($disc7->id, $neighbours['prev']->id);
1576          $this->assertEquals($disc10->id, $neighbours['next']->id);
1577  
1578          $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1579          $this->assertEquals($disc8->id, $neighbours['prev']->id);
1580          $this->assertEquals($disc12->id, $neighbours['next']->id);
1581  
1582          $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1583          $this->assertEquals($disc10->id, $neighbours['prev']->id);
1584          $this->assertEmpty($neighbours['next']);
1585  
1586          // Reset to normal mode.
1587          $CFG->forum_enabletimedposts = false;
1588          $this->setAdminUser();
1589  
1590          $record->timemodified++;
1591          // Two blog posts with identical creation time will sort by id.
1592          $DB->update_record('forum_posts', (object) array('id' => $disc2->firstpost, 'created' => $record->timemodified));
1593          $DB->update_record('forum_posts', (object) array('id' => $disc3->firstpost, 'created' => $record->timemodified));
1594  
1595          $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1596          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1597          $this->assertEquals($disc3->id, $neighbours['next']->id);
1598  
1599          $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1600          $this->assertEquals($disc2->id, $neighbours['prev']->id);
1601          $this->assertEmpty($neighbours['next']);
1602      }
1603  
1604      /**
1605       * Test getting the neighbour threads of a discussion.
1606       */
1607      public function test_forum_get_neighbours_with_groups() {
1608          $this->resetAfterTest();
1609  
1610          $timenow = time();
1611          $timenext = $timenow;
1612  
1613          // Setup test data.
1614          $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1615          $course = $this->getDataGenerator()->create_course();
1616          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1617          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1618          $user1 = $this->getDataGenerator()->create_user();
1619          $user2 = $this->getDataGenerator()->create_user();
1620          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1621          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
1622          $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id));
1623  
1624          $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => VISIBLEGROUPS));
1625          $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => SEPARATEGROUPS));
1626          $cm1 = get_coursemodule_from_instance('forum', $forum1->id);
1627          $cm2 = get_coursemodule_from_instance('forum', $forum2->id);
1628          $context1 = \context_module::instance($cm1->id);
1629          $context2 = \context_module::instance($cm2->id);
1630  
1631          // Creating discussions in both forums.
1632          $record = new \stdClass();
1633          $record->course = $course->id;
1634          $record->userid = $user1->id;
1635          $record->forum = $forum1->id;
1636          $record->groupid = $group1->id;
1637          $record->timemodified = time();
1638          $disc11 = $forumgen->create_discussion($record);
1639          $record->forum = $forum2->id;
1640          $record->timemodified++;
1641          $disc21 = $forumgen->create_discussion($record);
1642  
1643          $record->timemodified++;
1644          $record->userid = $user2->id;
1645          $record->forum = $forum1->id;
1646          $record->groupid = $group2->id;
1647          $disc12 = $forumgen->create_discussion($record);
1648          $record->forum = $forum2->id;
1649          $disc22 = $forumgen->create_discussion($record);
1650  
1651          $record->timemodified++;
1652          $record->userid = $user1->id;
1653          $record->forum = $forum1->id;
1654          $record->groupid = null;
1655          $disc13 = $forumgen->create_discussion($record);
1656          $record->forum = $forum2->id;
1657          $disc23 = $forumgen->create_discussion($record);
1658  
1659          $record->timemodified++;
1660          $record->userid = $user2->id;
1661          $record->forum = $forum1->id;
1662          $record->groupid = $group2->id;
1663          $disc14 = $forumgen->create_discussion($record);
1664          $record->forum = $forum2->id;
1665          $disc24 = $forumgen->create_discussion($record);
1666  
1667          $record->timemodified++;
1668          $record->userid = $user1->id;
1669          $record->forum = $forum1->id;
1670          $record->groupid = $group1->id;
1671          $disc15 = $forumgen->create_discussion($record);
1672          $record->forum = $forum2->id;
1673          $disc25 = $forumgen->create_discussion($record);
1674  
1675          // Admin user can see all groups.
1676          $this->setAdminUser();
1677          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1678          $this->assertEmpty($neighbours['prev']);
1679          $this->assertEquals($disc12->id, $neighbours['next']->id);
1680          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1681          $this->assertEmpty($neighbours['prev']);
1682          $this->assertEquals($disc22->id, $neighbours['next']->id);
1683  
1684          $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1685          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1686          $this->assertEquals($disc13->id, $neighbours['next']->id);
1687          $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1688          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1689          $this->assertEquals($disc23->id, $neighbours['next']->id);
1690  
1691          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1692          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1693          $this->assertEquals($disc14->id, $neighbours['next']->id);
1694          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1695          $this->assertEquals($disc22->id, $neighbours['prev']->id);
1696          $this->assertEquals($disc24->id, $neighbours['next']->id);
1697  
1698          $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1);
1699          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1700          $this->assertEquals($disc15->id, $neighbours['next']->id);
1701          $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1702          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1703          $this->assertEquals($disc25->id, $neighbours['next']->id);
1704  
1705          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1706          $this->assertEquals($disc14->id, $neighbours['prev']->id);
1707          $this->assertEmpty($neighbours['next']);
1708          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1709          $this->assertEquals($disc24->id, $neighbours['prev']->id);
1710          $this->assertEmpty($neighbours['next']);
1711  
1712          // Admin user is only viewing group 1.
1713          $_POST['group'] = $group1->id;
1714          $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1715          $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1716  
1717          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1718          $this->assertEmpty($neighbours['prev']);
1719          $this->assertEquals($disc13->id, $neighbours['next']->id);
1720          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1721          $this->assertEmpty($neighbours['prev']);
1722          $this->assertEquals($disc23->id, $neighbours['next']->id);
1723  
1724          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1725          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1726          $this->assertEquals($disc15->id, $neighbours['next']->id);
1727          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1728          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1729          $this->assertEquals($disc25->id, $neighbours['next']->id);
1730  
1731          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1732          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1733          $this->assertEmpty($neighbours['next']);
1734          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1735          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1736          $this->assertEmpty($neighbours['next']);
1737  
1738          // Normal user viewing non-grouped posts (this is only possible in visible groups).
1739          $this->setUser($user1);
1740          $_POST['group'] = 0;
1741          $this->assertEquals(0, groups_get_activity_group($cm1, true));
1742  
1743          // They can see anything in visible groups.
1744          $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1745          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1746          $this->assertEquals($disc13->id, $neighbours['next']->id);
1747          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1748          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1749          $this->assertEquals($disc14->id, $neighbours['next']->id);
1750  
1751          // Normal user, orphan of groups, can only see non-grouped posts in separate groups.
1752          $this->setUser($user2);
1753          $_POST['group'] = 0;
1754          $this->assertEquals(0, groups_get_activity_group($cm2, true));
1755  
1756          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1757          $this->assertEmpty($neighbours['prev']);
1758          $this->assertEmpty($neighbours['next']);
1759  
1760          $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1761          $this->assertEmpty($neighbours['prev']);
1762          $this->assertEquals($disc23->id, $neighbours['next']->id);
1763  
1764          $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1765          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1766          $this->assertEmpty($neighbours['next']);
1767  
1768          // Switching to viewing group 1.
1769          $this->setUser($user1);
1770          $_POST['group'] = $group1->id;
1771          $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1772          $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1773  
1774          // They can see non-grouped or same group.
1775          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1776          $this->assertEmpty($neighbours['prev']);
1777          $this->assertEquals($disc13->id, $neighbours['next']->id);
1778          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1779          $this->assertEmpty($neighbours['prev']);
1780          $this->assertEquals($disc23->id, $neighbours['next']->id);
1781  
1782          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1783          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1784          $this->assertEquals($disc15->id, $neighbours['next']->id);
1785          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1786          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1787          $this->assertEquals($disc25->id, $neighbours['next']->id);
1788  
1789          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1790          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1791          $this->assertEmpty($neighbours['next']);
1792          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1793          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1794          $this->assertEmpty($neighbours['next']);
1795  
1796          // Querying the neighbours of a discussion passing the wrong CM.
1797          $this->expectException('coding_exception');
1798          forum_get_discussion_neighbours($cm2, $disc11, $forum2);
1799      }
1800  
1801      /**
1802       * Test getting the neighbour threads of a blog-like forum with groups involved.
1803       */
1804      public function test_forum_get_neighbours_with_groups_blog() {
1805          $this->resetAfterTest();
1806  
1807          $timenow = time();
1808          $timenext = $timenow;
1809  
1810          // Setup test data.
1811          $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1812          $course = $this->getDataGenerator()->create_course();
1813          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1814          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1815          $user1 = $this->getDataGenerator()->create_user();
1816          $user2 = $this->getDataGenerator()->create_user();
1817          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1818          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
1819          $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id));
1820  
1821          $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog',
1822                  'groupmode' => VISIBLEGROUPS));
1823          $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog',
1824                  'groupmode' => SEPARATEGROUPS));
1825          $cm1 = get_coursemodule_from_instance('forum', $forum1->id);
1826          $cm2 = get_coursemodule_from_instance('forum', $forum2->id);
1827          $context1 = \context_module::instance($cm1->id);
1828          $context2 = \context_module::instance($cm2->id);
1829  
1830          // Creating blog posts in both forums.
1831          $record = new \stdClass();
1832          $record->course = $course->id;
1833          $record->userid = $user1->id;
1834          $record->forum = $forum1->id;
1835          $record->groupid = $group1->id;
1836          $record->timemodified = time();
1837          $disc11 = $forumgen->create_discussion($record);
1838          $record->timenow = $timenext++;
1839          $record->forum = $forum2->id;
1840          $record->timemodified++;
1841          $disc21 = $forumgen->create_discussion($record);
1842  
1843          $record->timemodified++;
1844          $record->userid = $user2->id;
1845          $record->forum = $forum1->id;
1846          $record->groupid = $group2->id;
1847          $disc12 = $forumgen->create_discussion($record);
1848          $record->forum = $forum2->id;
1849          $disc22 = $forumgen->create_discussion($record);
1850  
1851          $record->timemodified++;
1852          $record->userid = $user1->id;
1853          $record->forum = $forum1->id;
1854          $record->groupid = null;
1855          $disc13 = $forumgen->create_discussion($record);
1856          $record->forum = $forum2->id;
1857          $disc23 = $forumgen->create_discussion($record);
1858  
1859          $record->timemodified++;
1860          $record->userid = $user2->id;
1861          $record->forum = $forum1->id;
1862          $record->groupid = $group2->id;
1863          $disc14 = $forumgen->create_discussion($record);
1864          $record->forum = $forum2->id;
1865          $disc24 = $forumgen->create_discussion($record);
1866  
1867          $record->timemodified++;
1868          $record->userid = $user1->id;
1869          $record->forum = $forum1->id;
1870          $record->groupid = $group1->id;
1871          $disc15 = $forumgen->create_discussion($record);
1872          $record->forum = $forum2->id;
1873          $disc25 = $forumgen->create_discussion($record);
1874  
1875          // Admin user can see all groups.
1876          $this->setAdminUser();
1877          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1878          $this->assertEmpty($neighbours['prev']);
1879          $this->assertEquals($disc12->id, $neighbours['next']->id);
1880          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1881          $this->assertEmpty($neighbours['prev']);
1882          $this->assertEquals($disc22->id, $neighbours['next']->id);
1883  
1884          $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1885          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1886          $this->assertEquals($disc13->id, $neighbours['next']->id);
1887          $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1888          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1889          $this->assertEquals($disc23->id, $neighbours['next']->id);
1890  
1891          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1892          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1893          $this->assertEquals($disc14->id, $neighbours['next']->id);
1894          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1895          $this->assertEquals($disc22->id, $neighbours['prev']->id);
1896          $this->assertEquals($disc24->id, $neighbours['next']->id);
1897  
1898          $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1);
1899          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1900          $this->assertEquals($disc15->id, $neighbours['next']->id);
1901          $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1902          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1903          $this->assertEquals($disc25->id, $neighbours['next']->id);
1904  
1905          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1906          $this->assertEquals($disc14->id, $neighbours['prev']->id);
1907          $this->assertEmpty($neighbours['next']);
1908          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1909          $this->assertEquals($disc24->id, $neighbours['prev']->id);
1910          $this->assertEmpty($neighbours['next']);
1911  
1912          // Admin user is only viewing group 1.
1913          $_POST['group'] = $group1->id;
1914          $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1915          $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1916  
1917          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1918          $this->assertEmpty($neighbours['prev']);
1919          $this->assertEquals($disc13->id, $neighbours['next']->id);
1920          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1921          $this->assertEmpty($neighbours['prev']);
1922          $this->assertEquals($disc23->id, $neighbours['next']->id);
1923  
1924          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1925          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1926          $this->assertEquals($disc15->id, $neighbours['next']->id);
1927          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1928          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1929          $this->assertEquals($disc25->id, $neighbours['next']->id);
1930  
1931          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1932          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1933          $this->assertEmpty($neighbours['next']);
1934          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1935          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1936          $this->assertEmpty($neighbours['next']);
1937  
1938          // Normal user viewing non-grouped posts (this is only possible in visible groups).
1939          $this->setUser($user1);
1940          $_POST['group'] = 0;
1941          $this->assertEquals(0, groups_get_activity_group($cm1, true));
1942  
1943          // They can see anything in visible groups.
1944          $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1945          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1946          $this->assertEquals($disc13->id, $neighbours['next']->id);
1947          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1948          $this->assertEquals($disc12->id, $neighbours['prev']->id);
1949          $this->assertEquals($disc14->id, $neighbours['next']->id);
1950  
1951          // Normal user, orphan of groups, can only see non-grouped posts in separate groups.
1952          $this->setUser($user2);
1953          $_POST['group'] = 0;
1954          $this->assertEquals(0, groups_get_activity_group($cm2, true));
1955  
1956          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1957          $this->assertEmpty($neighbours['prev']);
1958          $this->assertEmpty($neighbours['next']);
1959  
1960          $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1961          $this->assertEmpty($neighbours['prev']);
1962          $this->assertEquals($disc23->id, $neighbours['next']->id);
1963  
1964          $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1965          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1966          $this->assertEmpty($neighbours['next']);
1967  
1968          // Switching to viewing group 1.
1969          $this->setUser($user1);
1970          $_POST['group'] = $group1->id;
1971          $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1972          $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1973  
1974          // They can see non-grouped or same group.
1975          $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1976          $this->assertEmpty($neighbours['prev']);
1977          $this->assertEquals($disc13->id, $neighbours['next']->id);
1978          $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1979          $this->assertEmpty($neighbours['prev']);
1980          $this->assertEquals($disc23->id, $neighbours['next']->id);
1981  
1982          $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1983          $this->assertEquals($disc11->id, $neighbours['prev']->id);
1984          $this->assertEquals($disc15->id, $neighbours['next']->id);
1985          $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1986          $this->assertEquals($disc21->id, $neighbours['prev']->id);
1987          $this->assertEquals($disc25->id, $neighbours['next']->id);
1988  
1989          $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1990          $this->assertEquals($disc13->id, $neighbours['prev']->id);
1991          $this->assertEmpty($neighbours['next']);
1992          $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1993          $this->assertEquals($disc23->id, $neighbours['prev']->id);
1994          $this->assertEmpty($neighbours['next']);
1995  
1996          // Querying the neighbours of a discussion passing the wrong CM.
1997          $this->expectException('coding_exception');
1998          forum_get_discussion_neighbours($cm2, $disc11, $forum2);
1999      }
2000  
2001      public function test_count_discussion_replies_basic() {
2002          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2003  
2004          // Count the discussion replies in the forum.
2005          $result = forum_count_discussion_replies($forum->id);
2006          $this->assertCount(10, $result);
2007      }
2008  
2009      public function test_count_discussion_replies_limited() {
2010          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2011          // Adding limits shouldn't make a difference.
2012          $result = forum_count_discussion_replies($forum->id, "", 20);
2013          $this->assertCount(10, $result);
2014      }
2015  
2016      public function test_count_discussion_replies_paginated() {
2017          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2018          // Adding paging shouldn't make any difference.
2019          $result = forum_count_discussion_replies($forum->id, "", -1, 0, 100);
2020          $this->assertCount(10, $result);
2021      }
2022  
2023      public function test_count_discussion_replies_paginated_sorted() {
2024          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2025          // Specifying the forumsort should also give a good result. This follows a different path.
2026          $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 100);
2027          $this->assertCount(10, $result);
2028          foreach ($result as $row) {
2029              // Grab the first discussionid.
2030              $discussionid = array_shift($discussionids);
2031              $this->assertEquals($discussionid, $row->discussion);
2032          }
2033      }
2034  
2035      public function test_count_discussion_replies_limited_sorted() {
2036          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2037          // Adding limits, and a forumsort shouldn't make a difference.
2038          $result = forum_count_discussion_replies($forum->id, "d.id asc", 20);
2039          $this->assertCount(10, $result);
2040          foreach ($result as $row) {
2041              // Grab the first discussionid.
2042              $discussionid = array_shift($discussionids);
2043              $this->assertEquals($discussionid, $row->discussion);
2044          }
2045      }
2046  
2047      public function test_count_discussion_replies_paginated_sorted_small() {
2048          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2049          // Grabbing a smaller subset and they should be ordered as expected.
2050          $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 5);
2051          $this->assertCount(5, $result);
2052          foreach ($result as $row) {
2053              // Grab the first discussionid.
2054              $discussionid = array_shift($discussionids);
2055              $this->assertEquals($discussionid, $row->discussion);
2056          }
2057      }
2058  
2059      public function test_count_discussion_replies_paginated_sorted_small_reverse() {
2060          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2061          // Grabbing a smaller subset and they should be ordered as expected.
2062          $result = forum_count_discussion_replies($forum->id, "d.id desc", -1, 0, 5);
2063          $this->assertCount(5, $result);
2064          foreach ($result as $row) {
2065              // Grab the last discussionid.
2066              $discussionid = array_pop($discussionids);
2067              $this->assertEquals($discussionid, $row->discussion);
2068          }
2069      }
2070  
2071      public function test_count_discussion_replies_limited_sorted_small_reverse() {
2072          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2073          // Adding limits, and a forumsort shouldn't make a difference.
2074          $result = forum_count_discussion_replies($forum->id, "d.id desc", 5);
2075          $this->assertCount(5, $result);
2076          foreach ($result as $row) {
2077              // Grab the last discussionid.
2078              $discussionid = array_pop($discussionids);
2079              $this->assertEquals($discussionid, $row->discussion);
2080          }
2081      }
2082  
2083      /**
2084       * Test the reply count when used with private replies.
2085       */
2086      public function test_forum_count_discussion_replies_private() {
2087          global $DB;
2088          $this->resetAfterTest();
2089  
2090          $course = $this->getDataGenerator()->create_course();
2091          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
2092          $context = \context_module::instance($forum->cmid);
2093          $cm = get_coursemodule_from_instance('forum', $forum->id);
2094  
2095          $student = $this->getDataGenerator()->create_user();
2096          $this->getDataGenerator()->enrol_user($student->id, $course->id);
2097  
2098          $teacher = $this->getDataGenerator()->create_user();
2099          $this->getDataGenerator()->enrol_user($teacher->id, $course->id);
2100  
2101          $privilegeduser = $this->getDataGenerator()->create_user();
2102          $this->getDataGenerator()->enrol_user($privilegeduser->id, $course->id, 'editingteacher');
2103  
2104          $otheruser = $this->getDataGenerator()->create_user();
2105          $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
2106  
2107          $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
2108  
2109          // Create a discussion with some replies.
2110          $record = new \stdClass();
2111          $record->course = $forum->course;
2112          $record->forum = $forum->id;
2113          $record->userid = $student->id;
2114          $discussion = $generator->create_discussion($record);
2115          $replycount = 5;
2116          $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
2117  
2118          // Create a couple of standard replies.
2119          $post = new \stdClass();
2120          $post->userid = $student->id;
2121          $post->discussion = $discussion->id;
2122          $post->parent = $replyto->id;
2123  
2124          for ($i = 0; $i < $replycount; $i++) {
2125              $post = $generator->create_post($post);
2126          }
2127  
2128          // Create a private reply post from the teacher back to the student.
2129          $reply = new \stdClass();
2130          $reply->userid = $teacher->id;
2131          $reply->discussion = $discussion->id;
2132          $reply->parent = $replyto->id;
2133          $reply->privatereplyto = $replyto->userid;
2134          $generator->create_post($reply);
2135  
2136          // The user is the author of the private reply.
2137          $this->setUser($teacher->id);
2138          $counts = forum_count_discussion_replies($forum->id);
2139          $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies);
2140  
2141          // The user is the intended recipient.
2142          $this->setUser($student->id);
2143          $counts = forum_count_discussion_replies($forum->id);
2144          $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies);
2145  
2146          // The user is not the author or recipient, but does have the readprivatereplies capability.
2147          $this->setUser($privilegeduser->id);
2148          $counts = forum_count_discussion_replies($forum->id, "", -1, -1, 0, true);
2149          $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies);
2150  
2151          // The user is not allowed to view this post.
2152          $this->setUser($otheruser->id);
2153          $counts = forum_count_discussion_replies($forum->id);
2154          $this->assertEquals($replycount, $counts[$discussion->id]->replies);
2155      }
2156  
2157      public function test_discussion_pinned_sort() {
2158          list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
2159          $cm = get_coursemodule_from_instance('forum', $forum->id);
2160          $discussions = forum_get_discussions($cm);
2161          // First discussion should be pinned.
2162          $first = reset($discussions);
2163          $this->assertEquals(1, $first->pinned, "First discussion should be pinned discussion");
2164      }
2165      public function test_forum_view() {
2166          global $CFG;
2167  
2168          $CFG->enablecompletion = 1;
2169          $this->resetAfterTest();
2170  
2171          // Setup test data.
2172          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
2173          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
2174                                                              array('completion' => 2, 'completionview' => 1));
2175          $context = \context_module::instance($forum->cmid);
2176          $cm = get_coursemodule_from_instance('forum', $forum->id);
2177  
2178          // Trigger and capture the event.
2179          $sink = $this->redirectEvents();
2180  
2181          $this->setAdminUser();
2182          forum_view($forum, $course, $cm, $context);
2183  
2184          $events = $sink->get_events();
2185          // 2 addition