See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace mod_forum; 18 19 use mod_forum_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 additional events thanks to completion. 2186 $this->assertCount(3, $events); 2187 $event = array_pop($events); 2188 2189 // Checking that the event contains the expected values. 2190 $this->assertInstanceOf('\mod_forum\event\course_module_viewed', $event); 2191 $this->assertEquals($context, $event->get_context()); 2192 $url = new \moodle_url('/mod/forum/view.php', array('f' => $forum->id)); 2193 $this->assertEquals($url, $event->get_url()); 2194 $this->assertEventContextNotUsed($event); 2195 $this->assertNotEmpty($event->get_name()); 2196 2197 // Check completion status. 2198 $completion = new \completion_info($course); 2199 $completiondata = $completion->get_data($cm); 2200 $this->assertEquals(1, $completiondata->completionstate); 2201 2202 } 2203 2204 /** 2205 * Test forum_discussion_view. 2206 */ 2207 public function test_forum_discussion_view() { 2208 global $CFG, $USER; 2209 2210 $this->resetAfterTest(); 2211 2212 // Setup test data. 2213 $course = $this->getDataGenerator()->create_course(); 2214 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 2215 $discussion = $this->create_single_discussion_with_replies($forum, $USER, 2); 2216 2217 $context = \context_module::instance($forum->cmid); 2218 $cm = get_coursemodule_from_instance('forum', $forum->id); 2219 2220 // Trigger and capture the event. 2221 $sink = $this->redirectEvents(); 2222 2223 $this->setAdminUser(); 2224 forum_discussion_view($context, $forum, $discussion); 2225 2226 $events = $sink->get_events(); 2227 $this->assertCount(1, $events); 2228 $event = array_pop($events); 2229 2230 // Checking that the event contains the expected values. 2231 $this->assertInstanceOf('\mod_forum\event\discussion_viewed', $event); 2232 $this->assertEquals($context, $event->get_context()); 2233 $expected = array($course->id, 'forum', 'view discussion', "discuss.php?d={$discussion->id}", 2234 $discussion->id, $forum->cmid); 2235 $this->assertEventLegacyLogData($expected, $event); 2236 $this->assertEventContextNotUsed($event); 2237 2238 $this->assertNotEmpty($event->get_name()); 2239 2240 } 2241 2242 /** 2243 * Create a new course, forum, and user with a number of discussions and replies. 2244 * 2245 * @param int $discussioncount The number of discussions to create 2246 * @param int $replycount The number of replies to create in each discussion 2247 * @return array Containing the created forum object, and the ids of the created discussions. 2248 */ 2249 protected function create_multiple_discussions_with_replies($discussioncount, $replycount) { 2250 $this->resetAfterTest(); 2251 2252 // Setup the content. 2253 $user = $this->getDataGenerator()->create_user(); 2254 $course = $this->getDataGenerator()->create_course(); 2255 $record = new \stdClass(); 2256 $record->course = $course->id; 2257 $forum = $this->getDataGenerator()->create_module('forum', $record); 2258 2259 // Create 10 discussions with replies. 2260 $discussionids = array(); 2261 for ($i = 0; $i < $discussioncount; $i++) { 2262 // Pin 3rd discussion. 2263 if ($i == 3) { 2264 $discussion = $this->create_single_discussion_pinned_with_replies($forum, $user, $replycount); 2265 } else { 2266 $discussion = $this->create_single_discussion_with_replies($forum, $user, $replycount); 2267 } 2268 2269 $discussionids[] = $discussion->id; 2270 } 2271 return array($forum, $discussionids); 2272 } 2273 2274 /** 2275 * Create a discussion with a number of replies. 2276 * 2277 * @param object $forum The forum which has been created 2278 * @param object $user The user making the discussion and replies 2279 * @param int $replycount The number of replies 2280 * @return object $discussion 2281 */ 2282 protected function create_single_discussion_with_replies($forum, $user, $replycount) { 2283 global $DB; 2284 2285 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2286 2287 $record = new \stdClass(); 2288 $record->course = $forum->course; 2289 $record->forum = $forum->id; 2290 $record->userid = $user->id; 2291 $discussion = $generator->create_discussion($record); 2292 2293 // Retrieve the first post. 2294 $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2295 2296 // Create the replies. 2297 $post = new \stdClass(); 2298 $post->userid = $user->id; 2299 $post->discussion = $discussion->id; 2300 $post->parent = $replyto->id; 2301 2302 for ($i = 0; $i < $replycount; $i++) { 2303 $generator->create_post($post); 2304 } 2305 2306 return $discussion; 2307 } 2308 /** 2309 * Create a discussion with a number of replies. 2310 * 2311 * @param object $forum The forum which has been created 2312 * @param object $user The user making the discussion and replies 2313 * @param int $replycount The number of replies 2314 * @return object $discussion 2315 */ 2316 protected function create_single_discussion_pinned_with_replies($forum, $user, $replycount) { 2317 global $DB; 2318 2319 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2320 2321 $record = new \stdClass(); 2322 $record->course = $forum->course; 2323 $record->forum = $forum->id; 2324 $record->userid = $user->id; 2325 $record->pinned = FORUM_DISCUSSION_PINNED; 2326 $discussion = $generator->create_discussion($record); 2327 2328 // Retrieve the first post. 2329 $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2330 2331 // Create the replies. 2332 $post = new \stdClass(); 2333 $post->userid = $user->id; 2334 $post->discussion = $discussion->id; 2335 $post->parent = $replyto->id; 2336 2337 for ($i = 0; $i < $replycount; $i++) { 2338 $generator->create_post($post); 2339 } 2340 2341 return $discussion; 2342 } 2343 2344 /** 2345 * Tests for mod_forum_rating_can_see_item_ratings(). 2346 * 2347 * @throws coding_exception 2348 * @throws rating_exception 2349 */ 2350 public function test_mod_forum_rating_can_see_item_ratings() { 2351 global $DB; 2352 2353 $this->resetAfterTest(); 2354 2355 // Setup test data. 2356 $course = new \stdClass(); 2357 $course->groupmode = SEPARATEGROUPS; 2358 $course->groupmodeforce = true; 2359 $course = $this->getDataGenerator()->create_course($course); 2360 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 2361 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2362 $cm = get_coursemodule_from_instance('forum', $forum->id); 2363 $context = \context_module::instance($cm->id); 2364 2365 // Create users. 2366 $user1 = $this->getDataGenerator()->create_user(); 2367 $user2 = $this->getDataGenerator()->create_user(); 2368 $user3 = $this->getDataGenerator()->create_user(); 2369 $user4 = $this->getDataGenerator()->create_user(); 2370 2371 // Groups and stuff. 2372 $role = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST); 2373 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $role->id); 2374 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 2375 $this->getDataGenerator()->enrol_user($user3->id, $course->id, $role->id); 2376 $this->getDataGenerator()->enrol_user($user4->id, $course->id, $role->id); 2377 2378 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2379 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2380 groups_add_member($group1, $user1); 2381 groups_add_member($group1, $user2); 2382 groups_add_member($group2, $user3); 2383 groups_add_member($group2, $user4); 2384 2385 $record = new \stdClass(); 2386 $record->course = $forum->course; 2387 $record->forum = $forum->id; 2388 $record->userid = $user1->id; 2389 $record->groupid = $group1->id; 2390 $discussion = $generator->create_discussion($record); 2391 2392 // Retrieve the first post. 2393 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2394 2395 $ratingoptions = new \stdClass; 2396 $ratingoptions->context = $context; 2397 $ratingoptions->ratingarea = 'post'; 2398 $ratingoptions->component = 'mod_forum'; 2399 $ratingoptions->itemid = $post->id; 2400 $ratingoptions->scaleid = 2; 2401 $ratingoptions->userid = $user2->id; 2402 $rating = new \rating($ratingoptions); 2403 $rating->update_rating(2); 2404 2405 // Now try to access it as various users. 2406 unassign_capability('moodle/site:accessallgroups', $role->id); 2407 $params = array('contextid' => 2, 2408 'component' => 'mod_forum', 2409 'ratingarea' => 'post', 2410 'itemid' => $post->id, 2411 'scaleid' => 2); 2412 $this->setUser($user1); 2413 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2414 $this->setUser($user2); 2415 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2416 $this->setUser($user3); 2417 $this->assertFalse(mod_forum_rating_can_see_item_ratings($params)); 2418 $this->setUser($user4); 2419 $this->assertFalse(mod_forum_rating_can_see_item_ratings($params)); 2420 2421 // Now try with accessallgroups cap and make sure everything is visible. 2422 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $role->id, $context->id); 2423 $this->setUser($user1); 2424 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2425 $this->setUser($user2); 2426 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2427 $this->setUser($user3); 2428 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2429 $this->setUser($user4); 2430 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2431 2432 // Change group mode and verify visibility. 2433 $course->groupmode = VISIBLEGROUPS; 2434 $DB->update_record('course', $course); 2435 unassign_capability('moodle/site:accessallgroups', $role->id); 2436 $this->setUser($user1); 2437 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2438 $this->setUser($user2); 2439 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2440 $this->setUser($user3); 2441 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2442 $this->setUser($user4); 2443 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2444 2445 } 2446 2447 /** 2448 * Test forum_get_discussions 2449 */ 2450 public function test_forum_get_discussions_with_groups() { 2451 global $DB; 2452 2453 $this->resetAfterTest(true); 2454 2455 // Create course to add the module. 2456 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0)); 2457 $user1 = self::getDataGenerator()->create_user(); 2458 $user2 = self::getDataGenerator()->create_user(); 2459 $user3 = self::getDataGenerator()->create_user(); 2460 2461 $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2462 self::getDataGenerator()->enrol_user($user1->id, $course->id, $role->id); 2463 self::getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 2464 self::getDataGenerator()->enrol_user($user3->id, $course->id, $role->id); 2465 2466 // Forum forcing separate gropus. 2467 $record = new \stdClass(); 2468 $record->course = $course->id; 2469 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); 2470 $cm = get_coursemodule_from_instance('forum', $forum->id); 2471 2472 // Create groups. 2473 $group1 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group1')); 2474 $group2 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group2')); 2475 $group3 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group3')); 2476 2477 // Add the user1 to g1 and g2 groups. 2478 groups_add_member($group1->id, $user1->id); 2479 groups_add_member($group2->id, $user1->id); 2480 2481 // Add the user 2 and 3 to only one group. 2482 groups_add_member($group1->id, $user2->id); 2483 groups_add_member($group3->id, $user3->id); 2484 2485 // Add a few discussions. 2486 $record = array(); 2487 $record['course'] = $course->id; 2488 $record['forum'] = $forum->id; 2489 $record['userid'] = $user1->id; 2490 $record['groupid'] = $group1->id; 2491 $discussiong1u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2492 2493 $record['groupid'] = $group2->id; 2494 $discussiong2u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2495 2496 $record['userid'] = $user2->id; 2497 $record['groupid'] = $group1->id; 2498 $discussiong1u2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2499 2500 $record['userid'] = $user3->id; 2501 $record['groupid'] = $group3->id; 2502 $discussiong3u3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2503 2504 self::setUser($user1); 2505 2506 // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions. 2507 $discussions = forum_get_discussions($cm); 2508 self::assertCount(2, $discussions); 2509 foreach ($discussions as $discussion) { 2510 self::assertEquals($group1->id, $discussion->groupid); 2511 } 2512 2513 // Get all my discussions. 2514 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0); 2515 self::assertCount(3, $discussions); 2516 2517 // Get all my g1 discussions. 2518 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group1->id); 2519 self::assertCount(2, $discussions); 2520 foreach ($discussions as $discussion) { 2521 self::assertEquals($group1->id, $discussion->groupid); 2522 } 2523 2524 // Get all my g2 discussions. 2525 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id); 2526 self::assertCount(1, $discussions); 2527 $discussion = array_shift($discussions); 2528 self::assertEquals($group2->id, $discussion->groupid); 2529 self::assertEquals($user1->id, $discussion->userid); 2530 self::assertEquals($discussiong2u1->id, $discussion->discussion); 2531 2532 // Get all my g3 discussions (I'm not enrolled in that group). 2533 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id); 2534 self::assertCount(0, $discussions); 2535 2536 // This group does not exist. 2537 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id + 1000); 2538 self::assertCount(0, $discussions); 2539 2540 self::setUser($user2); 2541 2542 // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions. 2543 $discussions = forum_get_discussions($cm); 2544 self::assertCount(2, $discussions); 2545 foreach ($discussions as $discussion) { 2546 self::assertEquals($group1->id, $discussion->groupid); 2547 } 2548 2549 // Get all my viewable discussions. 2550 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0); 2551 self::assertCount(2, $discussions); 2552 foreach ($discussions as $discussion) { 2553 self::assertEquals($group1->id, $discussion->groupid); 2554 } 2555 2556 // Get all my g2 discussions (I'm not enrolled in that group). 2557 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id); 2558 self::assertCount(0, $discussions); 2559 2560 // Get all my g3 discussions (I'm not enrolled in that group). 2561 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id); 2562 self::assertCount(0, $discussions); 2563 2564 } 2565 2566 /** 2567 * Test forum_user_can_post_discussion 2568 */ 2569 public function test_forum_user_can_post_discussion() { 2570 global $DB; 2571 2572 $this->resetAfterTest(true); 2573 2574 // Create course to add the module. 2575 $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1)); 2576 $user = self::getDataGenerator()->create_user(); 2577 $this->getDataGenerator()->enrol_user($user->id, $course->id); 2578 2579 // Forum forcing separate gropus. 2580 $record = new \stdClass(); 2581 $record->course = $course->id; 2582 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); 2583 $cm = get_coursemodule_from_instance('forum', $forum->id); 2584 $context = \context_module::instance($cm->id); 2585 2586 self::setUser($user); 2587 2588 // The user is not enroled in any group, try to post in a forum with separate groups. 2589 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2590 $this->assertFalse($can); 2591 2592 // Create a group. 2593 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2594 2595 // Try to post in a group the user is not enrolled. 2596 $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context); 2597 $this->assertFalse($can); 2598 2599 // Add the user to a group. 2600 groups_add_member($group->id, $user->id); 2601 2602 // Try to post in a group the user is not enrolled. 2603 $can = forum_user_can_post_discussion($forum, $group->id + 1, -1, $cm, $context); 2604 $this->assertFalse($can); 2605 2606 // Now try to post in the user group. (null means it will guess the group). 2607 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2608 $this->assertTrue($can); 2609 2610 $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context); 2611 $this->assertTrue($can); 2612 2613 // Test all groups. 2614 $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context); 2615 $this->assertFalse($can); 2616 2617 $this->setAdminUser(); 2618 $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context); 2619 $this->assertTrue($can); 2620 2621 // Change forum type. 2622 $forum->type = 'news'; 2623 $DB->update_record('forum', $forum); 2624 2625 // Admin can post news. 2626 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2627 $this->assertTrue($can); 2628 2629 // Normal users don't. 2630 self::setUser($user); 2631 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2632 $this->assertFalse($can); 2633 2634 // Change forum type. 2635 $forum->type = 'eachuser'; 2636 $DB->update_record('forum', $forum); 2637 2638 // I didn't post yet, so I should be able to post. 2639 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2640 $this->assertTrue($can); 2641 2642 // Post now. 2643 $record = new \stdClass(); 2644 $record->course = $course->id; 2645 $record->userid = $user->id; 2646 $record->forum = $forum->id; 2647 $record->groupid = $group->id; 2648 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2649 2650 // I already posted, I shouldn't be able to post. 2651 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2652 $this->assertFalse($can); 2653 2654 // Last check with no groups, normal forum and course. 2655 $course->groupmode = NOGROUPS; 2656 $course->groupmodeforce = 0; 2657 $DB->update_record('course', $course); 2658 2659 $forum->type = 'general'; 2660 $forum->groupmode = NOGROUPS; 2661 $DB->update_record('forum', $forum); 2662 2663 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2664 $this->assertTrue($can); 2665 } 2666 2667 /** 2668 * Test forum_user_can_post_discussion_after_cutoff 2669 */ 2670 public function test_forum_user_can_post_discussion_after_cutoff() { 2671 $this->resetAfterTest(true); 2672 2673 // Create course to add the module. 2674 $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1)); 2675 $student = self::getDataGenerator()->create_user(); 2676 $teacher = self::getDataGenerator()->create_user(); 2677 $this->getDataGenerator()->enrol_user($student->id, $course->id); 2678 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher'); 2679 2680 // Forum forcing separate gropus. 2681 $record = new \stdClass(); 2682 $record->course = $course->id; 2683 $record->cutoffdate = time() - 1; 2684 $forum = self::getDataGenerator()->create_module('forum', $record); 2685 $cm = get_coursemodule_from_instance('forum', $forum->id); 2686 $context = \context_module::instance($cm->id); 2687 2688 self::setUser($student); 2689 2690 // Students usually don't have the mod/forum:canoverridecutoff capability. 2691 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2692 $this->assertFalse($can); 2693 2694 self::setUser($teacher); 2695 2696 // Teachers usually have the mod/forum:canoverridecutoff capability. 2697 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2698 $this->assertTrue($can); 2699 } 2700 2701 /** 2702 * Test forum_user_has_posted_discussion with no groups. 2703 */ 2704 public function test_forum_user_has_posted_discussion_no_groups() { 2705 global $CFG; 2706 2707 $this->resetAfterTest(true); 2708 2709 $course = self::getDataGenerator()->create_course(); 2710 $author = self::getDataGenerator()->create_user(); 2711 $other = self::getDataGenerator()->create_user(); 2712 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2713 $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2714 2715 self::setUser($author); 2716 2717 // Neither user has posted. 2718 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id)); 2719 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id)); 2720 2721 // Post in the forum. 2722 $record = new \stdClass(); 2723 $record->course = $course->id; 2724 $record->userid = $author->id; 2725 $record->forum = $forum->id; 2726 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2727 2728 // The author has now posted, but the other user has not. 2729 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2730 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id)); 2731 } 2732 2733 /** 2734 * Test forum_user_has_posted_discussion with multiple forums 2735 */ 2736 public function test_forum_user_has_posted_discussion_multiple_forums() { 2737 global $CFG; 2738 2739 $this->resetAfterTest(true); 2740 2741 $course = self::getDataGenerator()->create_course(); 2742 $author = self::getDataGenerator()->create_user(); 2743 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2744 $forum1 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2745 $forum2 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2746 2747 self::setUser($author); 2748 2749 // No post in either forum. 2750 $this->assertFalse(forum_user_has_posted_discussion($forum1->id, $author->id)); 2751 $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id)); 2752 2753 // Post in the forum. 2754 $record = new \stdClass(); 2755 $record->course = $course->id; 2756 $record->userid = $author->id; 2757 $record->forum = $forum1->id; 2758 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2759 2760 // The author has now posted in forum1, but not forum2. 2761 $this->assertTrue(forum_user_has_posted_discussion($forum1->id, $author->id)); 2762 $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id)); 2763 } 2764 2765 /** 2766 * Test forum_user_has_posted_discussion with multiple groups. 2767 */ 2768 public function test_forum_user_has_posted_discussion_multiple_groups() { 2769 global $CFG; 2770 2771 $this->resetAfterTest(true); 2772 2773 $course = self::getDataGenerator()->create_course(); 2774 $author = self::getDataGenerator()->create_user(); 2775 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2776 2777 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2778 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2779 groups_add_member($group1->id, $author->id); 2780 groups_add_member($group2->id, $author->id); 2781 2782 $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ], [ 2783 'groupmode' => SEPARATEGROUPS, 2784 ]); 2785 2786 self::setUser($author); 2787 2788 // The user has not posted in either group. 2789 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id)); 2790 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2791 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2792 2793 // Post in one group. 2794 $record = new \stdClass(); 2795 $record->course = $course->id; 2796 $record->userid = $author->id; 2797 $record->forum = $forum->id; 2798 $record->groupid = $group1->id; 2799 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2800 2801 // The author has now posted in one group, but the other user has not. 2802 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2803 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2804 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2805 2806 // Post in the other group. 2807 $record = new \stdClass(); 2808 $record->course = $course->id; 2809 $record->userid = $author->id; 2810 $record->forum = $forum->id; 2811 $record->groupid = $group2->id; 2812 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2813 2814 // The author has now posted in one group, but the other user has not. 2815 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2816 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2817 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2818 } 2819 2820 /** 2821 * Tests the mod_forum_myprofile_navigation() function. 2822 */ 2823 public function test_mod_forum_myprofile_navigation() { 2824 $this->resetAfterTest(true); 2825 2826 // Set up the test. 2827 $tree = new \core_user\output\myprofile\tree(); 2828 $user = $this->getDataGenerator()->create_user(); 2829 $course = $this->getDataGenerator()->create_course(); 2830 $iscurrentuser = true; 2831 2832 // Set as the current user. 2833 $this->setUser($user); 2834 2835 // Check the node tree is correct. 2836 mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course); 2837 $reflector = new \ReflectionObject($tree); 2838 $nodes = $reflector->getProperty('nodes'); 2839 $nodes->setAccessible(true); 2840 $this->assertArrayHasKey('forumposts', $nodes->getValue($tree)); 2841 $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree)); 2842 } 2843 2844 /** 2845 * Tests the mod_forum_myprofile_navigation() function as a guest. 2846 */ 2847 public function test_mod_forum_myprofile_navigation_as_guest() { 2848 global $USER; 2849 2850 $this->resetAfterTest(true); 2851 2852 // Set up the test. 2853 $tree = new \core_user\output\myprofile\tree(); 2854 $course = $this->getDataGenerator()->create_course(); 2855 $iscurrentuser = true; 2856 2857 // Set user as guest. 2858 $this->setGuestUser(); 2859 2860 // Check the node tree is correct. 2861 mod_forum_myprofile_navigation($tree, $USER, $iscurrentuser, $course); 2862 $reflector = new \ReflectionObject($tree); 2863 $nodes = $reflector->getProperty('nodes'); 2864 $nodes->setAccessible(true); 2865 $this->assertArrayNotHasKey('forumposts', $nodes->getValue($tree)); 2866 $this->assertArrayNotHasKey('forumdiscussions', $nodes->getValue($tree)); 2867 } 2868 2869 /** 2870 * Tests the mod_forum_myprofile_navigation() function as a user viewing another user's profile. 2871 */ 2872 public function test_mod_forum_myprofile_navigation_different_user() { 2873 $this->resetAfterTest(true); 2874 2875 // Set up the test. 2876 $tree = new \core_user\output\myprofile\tree(); 2877 $user = $this->getDataGenerator()->create_user(); 2878 $user2 = $this->getDataGenerator()->create_user(); 2879 $course = $this->getDataGenerator()->create_course(); 2880 $iscurrentuser = true; 2881 2882 // Set to different user's profile. 2883 $this->setUser($user2); 2884 2885 // Check the node tree is correct. 2886 mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course); 2887 $reflector = new \ReflectionObject($tree); 2888 $nodes = $reflector->getProperty('nodes'); 2889 $nodes->setAccessible(true); 2890 $this->assertArrayHasKey('forumposts', $nodes->getValue($tree)); 2891 $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree)); 2892 } 2893 2894 /** 2895 * Test test_pinned_discussion_with_group. 2896 */ 2897 public function test_pinned_discussion_with_group() { 2898 global $SESSION; 2899 2900 $this->resetAfterTest(); 2901 $course1 = $this->getDataGenerator()->create_course(); 2902 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); 2903 2904 // Create an author user. 2905 $author = $this->getDataGenerator()->create_user(); 2906 $this->getDataGenerator()->enrol_user($author->id, $course1->id); 2907 2908 // Create two viewer users - one in a group, one not. 2909 $viewer1 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1)); 2910 $this->getDataGenerator()->enrol_user($viewer1->id, $course1->id); 2911 2912 $viewer2 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1)); 2913 $this->getDataGenerator()->enrol_user($viewer2->id, $course1->id); 2914 $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group1->id)); 2915 2916 $forum1 = $this->getDataGenerator()->create_module('forum', (object) array( 2917 'course' => $course1->id, 2918 'groupmode' => SEPARATEGROUPS, 2919 )); 2920 2921 $coursemodule = get_coursemodule_from_instance('forum', $forum1->id); 2922 2923 $alldiscussions = array(); 2924 $group1discussions = array(); 2925 2926 // Create 4 discussions in all participants group and group1, where the first 2927 // discussion is pinned in each group. 2928 $allrecord = new \stdClass(); 2929 $allrecord->course = $course1->id; 2930 $allrecord->userid = $author->id; 2931 $allrecord->forum = $forum1->id; 2932 $allrecord->pinned = FORUM_DISCUSSION_PINNED; 2933 2934 $group1record = new \stdClass(); 2935 $group1record->course = $course1->id; 2936 $group1record->userid = $author->id; 2937 $group1record->forum = $forum1->id; 2938 $group1record->groupid = $group1->id; 2939 $group1record->pinned = FORUM_DISCUSSION_PINNED; 2940 2941 $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord); 2942 $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record); 2943 2944 // Create unpinned discussions. 2945 $allrecord->pinned = FORUM_DISCUSSION_UNPINNED; 2946 $group1record->pinned = FORUM_DISCUSSION_UNPINNED; 2947 for ($i = 0; $i < 3; $i++) { 2948 $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord); 2949 $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record); 2950 } 2951 2952 // As viewer1 (no group). This user shouldn't see any of group1's discussions 2953 // so their expected discussion order is (where rightmost is highest priority): 2954 // Ad1, ad2, ad3, ad0. 2955 $this->setUser($viewer1->id); 2956 2957 // CHECK 1. 2958 // Take the neighbours of ad3, which should be prev: ad2 and next: ad0. 2959 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1); 2960 // Ad2 check. 2961 $this->assertEquals($alldiscussions[2]->id, $neighbours['prev']->id); 2962 // Ad0 check. 2963 $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id); 2964 2965 // CHECK 2. 2966 // Take the neighbours of ad0, which should be prev: ad3 and next: null. 2967 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[0], $forum1); 2968 // Ad3 check. 2969 $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id); 2970 // Null check. 2971 $this->assertEmpty($neighbours['next']); 2972 2973 // CHECK 3. 2974 // Take the neighbours of ad1, which should be prev: null and next: ad2. 2975 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1); 2976 // Null check. 2977 $this->assertEmpty($neighbours['prev']); 2978 // Ad2 check. 2979 $this->assertEquals($alldiscussions[2]->id, $neighbours['next']->id); 2980 2981 // Temporary hack to workaround for MDL-52656. 2982 $SESSION->currentgroup = null; 2983 2984 // As viewer2 (group1). This user should see all of group1's posts and the all participants group. 2985 // The expected discussion order is (rightmost is highest priority): 2986 // Ad1, gd1, ad2, gd2, ad3, gd3, ad0, gd0. 2987 $this->setUser($viewer2->id); 2988 2989 // CHECK 1. 2990 // Take the neighbours of ad1, which should be prev: null and next: gd1. 2991 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1); 2992 // Null check. 2993 $this->assertEmpty($neighbours['prev']); 2994 // Gd1 check. 2995 $this->assertEquals($group1discussions[1]->id, $neighbours['next']->id); 2996 2997 // CHECK 2. 2998 // Take the neighbours of ad3, which should be prev: gd2 and next: gd3. 2999 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1); 3000 // Gd2 check. 3001 $this->assertEquals($group1discussions[2]->id, $neighbours['prev']->id); 3002 // Gd3 check. 3003 $this->assertEquals($group1discussions[3]->id, $neighbours['next']->id); 3004 3005 // CHECK 3. 3006 // Take the neighbours of gd3, which should be prev: ad3 and next: ad0. 3007 $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[3], $forum1); 3008 // Ad3 check. 3009 $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id); 3010 // Ad0 check. 3011 $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id); 3012 3013 // CHECK 4. 3014 // Take the neighbours of gd0, which should be prev: ad0 and next: null. 3015 $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[0], $forum1); 3016 // Ad0 check. 3017 $this->assertEquals($alldiscussions[0]->id, $neighbours['prev']->id); 3018 // Null check. 3019 $this->assertEmpty($neighbours['next']); 3020 } 3021 3022 /** 3023 * Test test_pinned_with_timed_discussions. 3024 */ 3025 public function test_pinned_with_timed_discussions() { 3026 global $CFG; 3027 3028 $CFG->forum_enabletimedposts = true; 3029 3030 $this->resetAfterTest(); 3031 $course = $this->getDataGenerator()->create_course(); 3032 3033 // Create an user. 3034 $user = $this->getDataGenerator()->create_user(); 3035 $this->getDataGenerator()->enrol_user($user->id, $course->id); 3036 3037 // Create a forum. 3038 $record = new \stdClass(); 3039 $record->course = $course->id; 3040 $forum = $this->getDataGenerator()->create_module('forum', (object) array( 3041 'course' => $course->id, 3042 'groupmode' => SEPARATEGROUPS, 3043 )); 3044 3045 $coursemodule = get_coursemodule_from_instance('forum', $forum->id); 3046 $now = time(); 3047 $discussions = array(); 3048 $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3049 3050 $record = new \stdClass(); 3051 $record->course = $course->id; 3052 $record->userid = $user->id; 3053 $record->forum = $forum->id; 3054 $record->pinned = FORUM_DISCUSSION_PINNED; 3055 $record->timemodified = $now; 3056 3057 $discussions[] = $discussiongenerator->create_discussion($record); 3058 3059 $record->pinned = FORUM_DISCUSSION_UNPINNED; 3060 $record->timestart = $now + 10; 3061 3062 $discussions[] = $discussiongenerator->create_discussion($record); 3063 3064 $record->timestart = $now; 3065 3066 $discussions[] = $discussiongenerator->create_discussion($record); 3067 3068 // Expected order of discussions: 3069 // D2, d1, d0. 3070 $this->setUser($user->id); 3071 3072 // CHECK 1. 3073 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum); 3074 // Null check. 3075 $this->assertEmpty($neighbours['prev']); 3076 // D1 check. 3077 $this->assertEquals($discussions[1]->id, $neighbours['next']->id); 3078 3079 // CHECK 2. 3080 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum); 3081 // D2 check. 3082 $this->assertEquals($discussions[2]->id, $neighbours['prev']->id); 3083 // D0 check. 3084 $this->assertEquals($discussions[0]->id, $neighbours['next']->id); 3085 3086 // CHECK 3. 3087 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum); 3088 // D2 check. 3089 $this->assertEquals($discussions[1]->id, $neighbours['prev']->id); 3090 // Null check. 3091 $this->assertEmpty($neighbours['next']); 3092 } 3093 3094 /** 3095 * Test test_pinned_timed_discussions_with_timed_discussions. 3096 */ 3097 public function test_pinned_timed_discussions_with_timed_discussions() { 3098 global $CFG; 3099 3100 $CFG->forum_enabletimedposts = true; 3101 3102 $this->resetAfterTest(); 3103 $course = $this->getDataGenerator()->create_course(); 3104 3105 // Create an user. 3106 $user = $this->getDataGenerator()->create_user(); 3107 $this->getDataGenerator()->enrol_user($user->id, $course->id); 3108 3109 // Create a forum. 3110 $record = new \stdClass(); 3111 $record->course = $course->id; 3112 $forum = $this->getDataGenerator()->create_module('forum', (object) array( 3113 'course' => $course->id, 3114 'groupmode' => SEPARATEGROUPS, 3115 )); 3116 3117 $coursemodule = get_coursemodule_from_instance('forum', $forum->id); 3118 $now = time(); 3119 $discussions = array(); 3120 $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3121 3122 $record = new \stdClass(); 3123 $record->course = $course->id; 3124 $record->userid = $user->id; 3125 $record->forum = $forum->id; 3126 $record->pinned = FORUM_DISCUSSION_PINNED; 3127 $record->timemodified = $now; 3128 $record->timestart = $now + 10; 3129 3130 $discussions[] = $discussiongenerator->create_discussion($record); 3131 3132 $record->pinned = FORUM_DISCUSSION_UNPINNED; 3133 3134 $discussions[] = $discussiongenerator->create_discussion($record); 3135 3136 $record->timestart = $now; 3137 3138 $discussions[] = $discussiongenerator->create_discussion($record); 3139 3140 $record->pinned = FORUM_DISCUSSION_PINNED; 3141 3142 $discussions[] = $discussiongenerator->create_discussion($record); 3143 3144 // Expected order of discussions: 3145 // D2, d1, d3, d0. 3146 $this->setUser($user->id); 3147 3148 // CHECK 1. 3149 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum); 3150 // Null check. 3151 $this->assertEmpty($neighbours['prev']); 3152 // D1 check. 3153 $this->assertEquals($discussions[1]->id, $neighbours['next']->id); 3154 3155 // CHECK 2. 3156 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum); 3157 // D2 check. 3158 $this->assertEquals($discussions[2]->id, $neighbours['prev']->id); 3159 // D3 check. 3160 $this->assertEquals($discussions[3]->id, $neighbours['next']->id); 3161 3162 // CHECK 3. 3163 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[3], $forum); 3164 // D1 check. 3165 $this->assertEquals($discussions[1]->id, $neighbours['prev']->id); 3166 // D0 check. 3167 $this->assertEquals($discussions[0]->id, $neighbours['next']->id); 3168 3169 // CHECK 4. 3170 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum); 3171 // D3 check. 3172 $this->assertEquals($discussions[3]->id, $neighbours['prev']->id); 3173 // Null check. 3174 $this->assertEmpty($neighbours['next']); 3175 } 3176 3177 /** 3178 * Test for forum_is_author_hidden. 3179 */ 3180 public function test_forum_is_author_hidden() { 3181 // First post, different forum type. 3182 $post = (object) ['parent' => 0]; 3183 $forum = (object) ['type' => 'standard']; 3184 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3185 3186 // Child post, different forum type. 3187 $post->parent = 1; 3188 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3189 3190 // First post, single simple discussion forum type. 3191 $post->parent = 0; 3192 $forum->type = 'single'; 3193 $this->assertTrue(forum_is_author_hidden($post, $forum)); 3194 3195 // Child post, single simple discussion forum type. 3196 $post->parent = 1; 3197 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3198 3199 // Incorrect parameters: $post. 3200 $this->expectException('coding_exception'); 3201 $this->expectExceptionMessage('$post->parent must be set.'); 3202 unset($post->parent); 3203 forum_is_author_hidden($post, $forum); 3204 3205 // Incorrect parameters: $forum. 3206 $this->expectException('coding_exception'); 3207 $this->expectExceptionMessage('$forum->type must be set.'); 3208 unset($forum->type); 3209 forum_is_author_hidden($post, $forum); 3210 } 3211 3212 /** 3213 * Test the forum_discussion_is_locked function. 3214 * 3215 * @dataProvider forum_discussion_is_locked_provider 3216 * @param \stdClass $forum 3217 * @param \stdClass $discussion 3218 * @param bool $expect 3219 */ 3220 public function test_forum_discussion_is_locked($forum, $discussion, $expect) { 3221 $this->resetAfterTest(); 3222 3223 $datagenerator = $this->getDataGenerator(); 3224 $plugingenerator = $datagenerator->get_plugin_generator('mod_forum'); 3225 3226 $course = $datagenerator->create_course(); 3227 $user = $datagenerator->create_user(); 3228 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3229 'course' => $course->id 3230 ], $forum)); 3231 $discussion = $plugingenerator->create_discussion((object) array_merge([ 3232 'course' => $course->id, 3233 'userid' => $user->id, 3234 'forum' => $forum->id, 3235 ], $discussion)); 3236 3237 $this->assertEquals($expect, forum_discussion_is_locked($forum, $discussion)); 3238 } 3239 3240 /** 3241 * Dataprovider for forum_discussion_is_locked tests. 3242 * 3243 * @return array 3244 */ 3245 public function forum_discussion_is_locked_provider() { 3246 return [ 3247 'Unlocked: lockdiscussionafter is false' => [ 3248 ['lockdiscussionafter' => false], 3249 [], 3250 false 3251 ], 3252 'Unlocked: lockdiscussionafter is set; forum is of type single; post is recent' => [ 3253 ['lockdiscussionafter' => DAYSECS, 'type' => 'single'], 3254 ['timemodified' => time()], 3255 false 3256 ], 3257 'Unlocked: lockdiscussionafter is set; forum is of type single; post is old' => [ 3258 ['lockdiscussionafter' => MINSECS, 'type' => 'single'], 3259 ['timemodified' => time() - DAYSECS], 3260 false 3261 ], 3262 'Unlocked: lockdiscussionafter is set; forum is of type eachuser; post is recent' => [ 3263 ['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'], 3264 ['timemodified' => time()], 3265 false 3266 ], 3267 'Locked: lockdiscussionafter is set; forum is of type eachuser; post is old' => [ 3268 ['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'], 3269 ['timemodified' => time() - DAYSECS], 3270 true 3271 ], 3272 ]; 3273 } 3274 3275 /** 3276 * Test the forum_is_cutoff_date_reached function. 3277 * 3278 * @dataProvider forum_is_cutoff_date_reached_provider 3279 * @param array $forum 3280 * @param bool $expect 3281 */ 3282 public function test_forum_is_cutoff_date_reached($forum, $expect) { 3283 $this->resetAfterTest(); 3284 3285 $datagenerator = $this->getDataGenerator(); 3286 $course = $datagenerator->create_course(); 3287 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3288 'course' => $course->id 3289 ], $forum)); 3290 3291 $this->assertEquals($expect, forum_is_cutoff_date_reached($forum)); 3292 } 3293 3294 /** 3295 * Dataprovider for forum_is_cutoff_date_reached tests. 3296 * 3297 * @return array 3298 */ 3299 public function forum_is_cutoff_date_reached_provider() { 3300 $now = time(); 3301 return [ 3302 'cutoffdate is unset' => [ 3303 [], 3304 false 3305 ], 3306 'cutoffdate is 0' => [ 3307 ['cutoffdate' => 0], 3308 false 3309 ], 3310 'cutoffdate is set and is in future' => [ 3311 ['cutoffdate' => $now + 86400], 3312 false 3313 ], 3314 'cutoffdate is set and is in past' => [ 3315 ['cutoffdate' => $now - 86400], 3316 true 3317 ], 3318 ]; 3319 } 3320 3321 /** 3322 * Test the forum_is_due_date_reached function. 3323 * 3324 * @dataProvider forum_is_due_date_reached_provider 3325 * @param \stdClass $forum 3326 * @param bool $expect 3327 */ 3328 public function test_forum_is_due_date_reached($forum, $expect) { 3329 $this->resetAfterTest(); 3330 3331 $this->setAdminUser(); 3332 3333 $datagenerator = $this->getDataGenerator(); 3334 $course = $datagenerator->create_course(); 3335 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3336 'course' => $course->id 3337 ], $forum)); 3338 3339 $this->assertEquals($expect, forum_is_due_date_reached($forum)); 3340 } 3341 3342 /** 3343 * Dataprovider for forum_is_due_date_reached tests. 3344 * 3345 * @return array 3346 */ 3347 public function forum_is_due_date_reached_provider() { 3348 $now = time(); 3349 return [ 3350 'duedate is unset' => [ 3351 [], 3352 false 3353 ], 3354 'duedate is 0' => [ 3355 ['duedate' => 0], 3356 false 3357 ], 3358 'duedate is set and is in future' => [ 3359 ['duedate' => $now + 86400], 3360 false 3361 ], 3362 'duedate is set and is in past' => [ 3363 ['duedate' => $now - 86400], 3364 true 3365 ], 3366 ]; 3367 } 3368 3369 /** 3370 * Test that {@link forum_update_post()} keeps correct forum_discussions usermodified. 3371 */ 3372 public function test_forum_update_post_keeps_discussions_usermodified() { 3373 global $DB; 3374 3375 $this->resetAfterTest(); 3376 3377 // Let there be light. 3378 $teacher = self::getDataGenerator()->create_user(); 3379 $student = self::getDataGenerator()->create_user(); 3380 $course = self::getDataGenerator()->create_course(); 3381 3382 $forum = self::getDataGenerator()->create_module('forum', (object)[ 3383 'course' => $course->id, 3384 ]); 3385 3386 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 3387 3388 // Let the teacher start a discussion. 3389 $discussion = $generator->create_discussion((object)[ 3390 'course' => $course->id, 3391 'userid' => $teacher->id, 3392 'forum' => $forum->id, 3393 ]); 3394 3395 // On this freshly created discussion, the teacher is the author of the last post. 3396 $this->assertEquals($teacher->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3397 3398 // Fetch modified timestamp of the discussion. 3399 $discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]); 3400 $pasttime = $discussionmodified - 3600; 3401 3402 // Adjust the discussion modified timestamp back an hour, so it's in the past. 3403 $adjustment = (object)[ 3404 'id' => $discussion->id, 3405 'timemodified' => $pasttime, 3406 ]; 3407 $DB->update_record('forum_discussions', $adjustment); 3408 3409 // Let the student reply to the teacher's post. 3410 $reply = $generator->create_post((object)[ 3411 'course' => $course->id, 3412 'userid' => $student->id, 3413 'forum' => $forum->id, 3414 'discussion' => $discussion->id, 3415 'parent' => $discussion->firstpost, 3416 ]); 3417 3418 // The student should now be the last post's author. 3419 $this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3420 3421 // Fetch modified timestamp of the discussion and student's post. 3422 $discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]); 3423 $postmodified = $DB->get_field('forum_posts', 'modified', ['id' => $reply->id]); 3424 3425 // Discussion modified time should be updated to be equal to the newly created post's time. 3426 $this->assertEquals($discussionmodified, $postmodified); 3427 3428 // Adjust the discussion and post timestamps, so they are in the past. 3429 $adjustment = (object)[ 3430 'id' => $discussion->id, 3431 'timemodified' => $pasttime, 3432 ]; 3433 $DB->update_record('forum_discussions', $adjustment); 3434 3435 $adjustment = (object)[ 3436 'id' => $reply->id, 3437 'modified' => $pasttime, 3438 ]; 3439 $DB->update_record('forum_posts', $adjustment); 3440 3441 // The discussion and student's post time should now be an hour in the past. 3442 $this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id])); 3443 $this->assertEquals($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id])); 3444 3445 // Let the teacher edit the student's reply. 3446 $this->setUser($teacher->id); 3447 $newpost = (object)[ 3448 'id' => $reply->id, 3449 'itemid' => 0, 3450 'subject' => 'Amended subject', 3451 ]; 3452 forum_update_post($newpost, null); 3453 3454 // The student should still be the last post's author. 3455 $this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3456 3457 // The discussion modified time should not have changed. 3458 $this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id])); 3459 3460 // The post time should be updated. 3461 $this->assertGreaterThan($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id])); 3462 } 3463 3464 public function test_forum_core_calendar_provide_event_action() { 3465 $this->resetAfterTest(); 3466 $this->setAdminUser(); 3467 3468 // Create the activity. 3469 $course = $this->getDataGenerator()->create_course(); 3470 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3471 'completionreplies' => 5, 'completiondiscussions' => 2)); 3472 3473 // Create a calendar event. 3474 $event = $this->create_action_event($course->id, $forum->id, 3475 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3476 3477 // Create an action factory. 3478 $factory = new \core_calendar\action_factory(); 3479 3480 // Decorate action event. 3481 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3482 3483 // Confirm the event was decorated. 3484 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 3485 $this->assertEquals(get_string('view'), $actionevent->get_name()); 3486 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 3487 $this->assertEquals(7, $actionevent->get_item_count()); 3488 $this->assertTrue($actionevent->is_actionable()); 3489 } 3490 3491 public function test_forum_core_calendar_provide_event_action_in_hidden_section() { 3492 global $CFG; 3493 3494 $this->resetAfterTest(); 3495 $this->setAdminUser(); 3496 3497 // Create a course. 3498 $course = $this->getDataGenerator()->create_course(); 3499 3500 // Create a student. 3501 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3502 3503 // Create the activity. 3504 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3505 'completionreplies' => 5, 'completiondiscussions' => 2)); 3506 3507 // Create a calendar event. 3508 $event = $this->create_action_event($course->id, $forum->id, 3509 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3510 3511 // Set sections 0 as hidden. 3512 set_section_visible($course->id, 0, 0); 3513 3514 // Now, log out. 3515 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 3516 $this->setUser(); 3517 3518 // Create an action factory. 3519 $factory = new \core_calendar\action_factory(); 3520 3521 // Decorate action event for the student. 3522 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3523 3524 // Confirm the event is not shown at all. 3525 $this->assertNull($actionevent); 3526 } 3527 3528 public function test_forum_core_calendar_provide_event_action_for_user() { 3529 global $CFG; 3530 3531 $this->resetAfterTest(); 3532 $this->setAdminUser(); 3533 3534 // Create a course. 3535 $course = $this->getDataGenerator()->create_course(); 3536 3537 // Create a student. 3538 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3539 3540 // Create the activity. 3541 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3542 'completionreplies' => 5, 'completiondiscussions' => 2)); 3543 3544 // Create a calendar event. 3545 $event = $this->create_action_event($course->id, $forum->id, 3546 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3547 3548 // Now log out. 3549 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 3550 $this->setUser(); 3551 3552 // Create an action factory. 3553 $factory = new \core_calendar\action_factory(); 3554 3555 // Decorate action event for the student. 3556 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3557 3558 // Confirm the event was decorated. 3559 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 3560 $this->assertEquals(get_string('view'), $actionevent->get_name()); 3561 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 3562 $this->assertEquals(7, $actionevent->get_item_count()); 3563 $this->assertTrue($actionevent->is_actionable()); 3564 } 3565 3566 public function test_forum_core_calendar_provide_event_action_as_non_user() { 3567 global $CFG; 3568 3569 $this->resetAfterTest(); 3570 $this->setAdminUser(); 3571 3572 // Create the activity. 3573 $course = $this->getDataGenerator()->create_course(); 3574 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 3575 3576 // Create a calendar event. 3577 $event = $this->create_action_event($course->id, $forum->id, 3578 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3579 3580 // Log out the user and set force login to true. 3581 \core\session\manager::init_empty_session(); 3582 $CFG->forcelogin = true; 3583 3584 // Create an action factory. 3585 $factory = new \core_calendar\action_factory(); 3586 3587 // Decorate action event. 3588 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3589 3590 // Ensure result was null. 3591 $this->assertNull($actionevent); 3592 } 3593 3594 public function test_forum_core_calendar_provide_event_action_already_completed() { 3595 global $CFG; 3596 3597 $this->resetAfterTest(); 3598 $this->setAdminUser(); 3599 3600 $CFG->enablecompletion = 1; 3601 3602 // Create the activity. 3603 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 3604 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 3605 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 3606 3607 // Get some additional data. 3608 $cm = get_coursemodule_from_instance('forum', $forum->id); 3609 3610 // Create a calendar event. 3611 $event = $this->create_action_event($course->id, $forum->id, 3612 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3613 3614 // Mark the activity as completed. 3615 $completion = new \completion_info($course); 3616 $completion->set_module_viewed($cm); 3617 3618 // Create an action factory. 3619 $factory = new \core_calendar\action_factory(); 3620 3621 // Decorate action event. 3622 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3623 3624 // Ensure result was null. 3625 $this->assertNull($actionevent); 3626 } 3627 3628 public function test_forum_core_calendar_provide_event_action_already_completed_for_user() { 3629 global $CFG; 3630 3631 $this->resetAfterTest(); 3632 $this->setAdminUser(); 3633 3634 $CFG->enablecompletion = 1; 3635 3636 // Create a course. 3637 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 3638 3639 // Create a student. 3640 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3641 3642 // Create the activity. 3643 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 3644 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 3645 3646 // Get some additional data. 3647 $cm = get_coursemodule_from_instance('forum', $forum->id); 3648 3649 // Create a calendar event. 3650 $event = $this->create_action_event($course->id, $forum->id, 3651 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3652 3653 // Mark the activity as completed for the student. 3654 $completion = new \completion_info($course); 3655 $completion->set_module_viewed($cm, $student->id); 3656 3657 // Create an action factory. 3658 $factory = new \core_calendar\action_factory(); 3659 3660 // Decorate action event. 3661 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3662 3663 // Ensure result was null. 3664 $this->assertNull($actionevent); 3665 } 3666 3667 public function test_mod_forum_get_tagged_posts() { 3668 global $DB; 3669 3670 $this->resetAfterTest(); 3671 $this->setAdminUser(); 3672 3673 // Setup test data. 3674 $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3675 $course3 = $this->getDataGenerator()->create_course(); 3676 $course2 = $this->getDataGenerator()->create_course(); 3677 $course1 = $this->getDataGenerator()->create_course(); 3678 $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id)); 3679 $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id)); 3680 $forum3 = $this->getDataGenerator()->create_module('forum', array('course' => $course3->id)); 3681 $post11 = $forumgenerator->create_content($forum1, array('tags' => array('Cats', 'Dogs'))); 3682 $post12 = $forumgenerator->create_content($forum1, array('tags' => array('Cats', 'mice'))); 3683 $post13 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'))); 3684 $post14 = $forumgenerator->create_content($forum1); 3685 $post15 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'))); 3686 $post16 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'), 'hidden' => true)); 3687 $post21 = $forumgenerator->create_content($forum2, array('tags' => array('Cats'))); 3688 $post22 = $forumgenerator->create_content($forum2, array('tags' => array('Cats', 'Dogs'))); 3689 $post23 = $forumgenerator->create_content($forum2, array('tags' => array('mice', 'Cats'))); 3690 $post31 = $forumgenerator->create_content($forum3, array('tags' => array('mice', 'Cats'))); 3691 3692 $tag = \core_tag_tag::get_by_name(0, 'Cats'); 3693 3694 // Admin can see everything. 3695 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3696 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */0); 3697 $this->assertMatchesRegularExpression('/'.$post11->subject.'</', $res->content); 3698 $this->assertMatchesRegularExpression('/'.$post12->subject.'</', $res->content); 3699 $this->assertMatchesRegularExpression('/'.$post13->subject.'</', $res->content); 3700 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'</', $res->content); 3701 $this->assertMatchesRegularExpression('/'.$post15->subject.'</', $res->content); 3702 $this->assertMatchesRegularExpression('/'.$post16->subject.'</', $res->content); 3703 $this->assertDoesNotMatchRegularExpression('/'.$post21->subject.'</', $res->content); 3704 $this->assertDoesNotMatchRegularExpression('/'.$post22->subject.'</', $res->content); 3705 $this->assertDoesNotMatchRegularExpression('/'.$post23->subject.'</', $res->content); 3706 $this->assertDoesNotMatchRegularExpression('/'.$post31->subject.'</', $res->content); 3707 $this->assertEmpty($res->prevpageurl); 3708 $this->assertNotEmpty($res->nextpageurl); 3709 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3710 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */1); 3711 $this->assertDoesNotMatchRegularExpression('/'.$post11->subject.'</', $res->content); 3712 $this->assertDoesNotMatchRegularExpression('/'.$post12->subject.'</', $res->content); 3713 $this->assertDoesNotMatchRegularExpression('/'.$post13->subject.'</', $res->content); 3714 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'</', $res->content); 3715 $this->assertDoesNotMatchRegularExpression('/'.$post15->subject.'</', $res->content); 3716 $this->assertDoesNotMatchRegularExpression('/'.$post16->subject.'</', $res->content); 3717 $this->assertMatchesRegularExpression('/'.$post21->subject.'</', $res->content); 3718 $this->assertMatchesRegularExpression('/'.$post22->subject.'</', $res->content); 3719 $this->assertMatchesRegularExpression('/'.$post23->subject.'</', $res->content); 3720 $this->assertMatchesRegularExpression('/'.$post31->subject.'</', $res->content); 3721 $this->assertNotEmpty($res->prevpageurl); 3722 $this->assertEmpty($res->nextpageurl); 3723 3724 // Create and enrol a user. 3725 $student = self::getDataGenerator()->create_user(); 3726 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 3727 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); 3728 $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual'); 3729 $this->setUser($student); 3730 \core_tag_index_builder::reset_caches(); 3731 3732 // User can not see posts in course 3 because he is not enrolled. 3733 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3734 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */1); 3735 $this->assertMatchesRegularExpression('/'.$post22->subject.'/', $res->content); 3736 $this->assertMatchesRegularExpression('/'.$post23->subject.'/', $res->content); 3737 $this->assertDoesNotMatchRegularExpression('/'.$post31->subject.'/', $res->content); 3738 3739 // User can search forum posts inside a course. 3740 $coursecontext = \context_course::instance($course1->id); 3741 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3742 /*$fromctx = */0, /*$ctx = */$coursecontext->id, /*$rec = */1, /*$post = */0); 3743 $this->assertMatchesRegularExpression('/'.$post11->subject.'/', $res->content); 3744 $this->assertMatchesRegularExpression('/'.$post12->subject.'/', $res->content); 3745 $this->assertMatchesRegularExpression('/'.$post13->subject.'/', $res->content); 3746 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'/', $res->content); 3747 $this->assertMatchesRegularExpression('/'.$post15->subject.'/', $res->content); 3748 $this->assertMatchesRegularExpression('/'.$post16->subject.'/', $res->content); 3749 $this->assertDoesNotMatchRegularExpression('/'.$post21->subject.'/', $res->content); 3750 $this->assertDoesNotMatchRegularExpression('/'.$post22->subject.'/', $res->content); 3751 $this->assertDoesNotMatchRegularExpression('/'.$post23->subject.'/', $res->content); 3752 $this->assertEmpty($res->nextpageurl); 3753 } 3754 3755 /** 3756 * Creates an action event. 3757 * 3758 * @param int $courseid The course id. 3759 * @param int $instanceid The instance id. 3760 * @param string $eventtype The event type. 3761 * @return bool|calendar_event 3762 */ 3763 private function create_action_event($courseid, $instanceid, $eventtype) { 3764 $event = new \stdClass(); 3765 $event->name = 'Calendar event'; 3766 $event->modulename = 'forum'; 3767 $event->courseid = $courseid; 3768 $event->instance = $instanceid; 3769 $event->type = CALENDAR_EVENT_TYPE_ACTION; 3770 $event->eventtype = $eventtype; 3771 $event->timestart = time(); 3772 3773 return \calendar_event::create($event); 3774 } 3775 3776 /** 3777 * Test the callback responsible for returning the completion rule descriptions. 3778 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 3779 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 3780 */ 3781 public function test_mod_forum_completion_get_active_rule_descriptions() { 3782 $this->resetAfterTest(); 3783 $this->setAdminUser(); 3784 3785 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 3786 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 3787 $forum1 = $this->getDataGenerator()->create_module('forum', [ 3788 'course' => $course->id, 3789 'completion' => 2, 3790 'completiondiscussions' => 3, 3791 'completionreplies' => 3, 3792 'completionposts' => 3 3793 ]); 3794 $forum2 = $this->getDataGenerator()->create_module('forum', [ 3795 'course' => $course->id, 3796 'completion' => 2, 3797 'completiondiscussions' => 0, 3798 'completionreplies' => 0, 3799 'completionposts' => 0 3800 ]); 3801 $cm1 = \cm_info::create(get_coursemodule_from_instance('forum', $forum1->id)); 3802 $cm2 = \cm_info::create(get_coursemodule_from_instance('forum', $forum2->id)); 3803 3804 // Data for the stdClass input type. 3805 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 3806 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 3807 $moddefaults = new \stdClass(); 3808 $moddefaults->customdata = ['customcompletionrules' => [ 3809 'completiondiscussions' => 3, 3810 'completionreplies' => 3, 3811 'completionposts' => 3 3812 ]]; 3813 $moddefaults->completion = 2; 3814 3815 $activeruledescriptions = [ 3816 get_string('completiondiscussionsdesc', 'forum', 3), 3817 get_string('completionrepliesdesc', 'forum', 3), 3818 get_string('completionpostsdesc', 'forum', 3) 3819 ]; 3820 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 3821 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($cm2), []); 3822 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 3823 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions(new \stdClass()), []); 3824 } 3825 3826 /** 3827 * Test the forum_post_is_visible_privately function used in private replies. 3828 */ 3829 public function test_forum_post_is_visible_privately() { 3830 $this->resetAfterTest(); 3831 3832 $course = $this->getDataGenerator()->create_course(); 3833 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 3834 $context = \context_module::instance($forum->cmid); 3835 $cm = get_coursemodule_from_instance('forum', $forum->id); 3836 3837 $author = $this->getDataGenerator()->create_user(); 3838 $this->getDataGenerator()->enrol_user($author->id, $course->id); 3839 3840 $recipient = $this->getDataGenerator()->create_user(); 3841 $this->getDataGenerator()->enrol_user($recipient->id, $course->id); 3842 3843 $privilegeduser = $this->getDataGenerator()->create_user(); 3844 $this->getDataGenerator()->enrol_user($privilegeduser->id, $course->id, 'editingteacher'); 3845 3846 $otheruser = $this->getDataGenerator()->create_user(); 3847 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id); 3848 3849 // Fake a post - this does not need to be persisted to the DB. 3850 $post = new \stdClass(); 3851 $post->userid = $author->id; 3852 $post->privatereplyto = $recipient->id; 3853 3854 // The user is the author. 3855 $this->setUser($author->id); 3856 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3857 3858 // The user is the intended recipient. 3859 $this->setUser($recipient->id); 3860 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3861 3862 // The user is not the author or recipient, but does have the readprivatereplies capability. 3863 $this->setUser($privilegeduser->id); 3864 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3865 3866 // The user is not allowed to view this post. 3867 $this->setUser($otheruser->id); 3868 $this->assertFalse(forum_post_is_visible_privately($post, $cm)); 3869 } 3870 3871 /** 3872 * An unkown event type should not have any limits 3873 */ 3874 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_unknown_event() { 3875 global $CFG; 3876 require_once($CFG->dirroot . "/calendar/lib.php"); 3877 3878 $this->resetAfterTest(true); 3879 $this->setAdminUser(); 3880 $generator = $this->getDataGenerator(); 3881 $course = $generator->create_course(); 3882 $duedate = time() + DAYSECS; 3883 $forum = new \stdClass(); 3884 $forum->duedate = $duedate; 3885 3886 // Create a valid event. 3887 $event = new \calendar_event([ 3888 'name' => 'Test event', 3889 'description' => '', 3890 'format' => 1, 3891 'courseid' => $course->id, 3892 'groupid' => 0, 3893 'userid' => 2, 3894 'modulename' => 'forum', 3895 'instance' => 1, 3896 'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE", 3897 'timestart' => 1, 3898 'timeduration' => 86400, 3899 'visible' => 1 3900 ]); 3901 3902 list ($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 3903 $this->assertNull($min); 3904 $this->assertNull($max); 3905 } 3906 3907 /** 3908 * Forums configured without a cutoff date should not have any limits applied. 3909 */ 3910 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_no_limit() { 3911 global $CFG; 3912 require_once($CFG->dirroot . '/calendar/lib.php'); 3913 3914 $this->resetAfterTest(true); 3915 $this->setAdminUser(); 3916 $generator = $this->getDataGenerator(); 3917 $course = $generator->create_course(); 3918 $duedate = time() + DAYSECS; 3919 $forum = new \stdClass(); 3920 $forum->duedate = $duedate; 3921 3922 // Create a valid event. 3923 $event = new \calendar_event([ 3924 'name' => 'Test event', 3925 'description' => '', 3926 'format' => 1, 3927 'courseid' => $course->id, 3928 'groupid' => 0, 3929 'userid' => 2, 3930 'modulename' => 'forum', 3931 'instance' => 1, 3932 'eventtype' => FORUM_EVENT_TYPE_DUE, 3933 'timestart' => 1, 3934 'timeduration' => 86400, 3935 'visible' => 1 3936 ]); 3937 3938 list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 3939 $this->assertNull($min); 3940 $this->assertNull($max); 3941 } 3942 3943 /** 3944 * Forums should be top bound by the cutoff date. 3945 */ 3946 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_with_limits() { 3947 global $CFG; 3948 require_once($CFG->dirroot . '/calendar/lib.php'); 3949 3950 $this->resetAfterTest(true); 3951 $this->setAdminUser(); 3952 $generator = $this->getDataGenerator(); 3953 $course = $generator->create_course(); 3954 $duedate = time() + DAYSECS; 3955 $cutoffdate = $duedate + DAYSECS; 3956 $forum = new \stdClass(); 3957 $forum->duedate = $duedate; 3958 $forum->cutoffdate = $cutoffdate; 3959 3960 // Create a valid event. 3961 $event = new \calendar_event([ 3962 'name' => 'Test event', 3963 'description' => '', 3964 'format' => 1, 3965 'courseid' => $course->id, 3966 'groupid' => 0, 3967 'userid' => 2, 3968 'modulename' => 'forum', 3969 'instance' => 1, 3970 'eventtype' => FORUM_EVENT_TYPE_DUE, 3971 'timestart' => 1, 3972 'timeduration' => 86400, 3973 'visible' => 1 3974 ]); 3975 3976 list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 3977 $this->assertNull($min); 3978 $this->assertEquals($cutoffdate, $max[0]); 3979 $this->assertNotEmpty($max[1]); 3980 } 3981 3982 /** 3983 * An unknown event type should not change the forum instance. 3984 */ 3985 public function test_mod_forum_core_calendar_event_timestart_updated_unknown_event() { 3986 global $CFG, $DB; 3987 require_once($CFG->dirroot . "/calendar/lib.php"); 3988 3989 $this->resetAfterTest(true); 3990 $this->setAdminUser(); 3991 $generator = $this->getDataGenerator(); 3992 $course = $generator->create_course(); 3993 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 3994 $duedate = time() + DAYSECS; 3995 $cutoffdate = $duedate + DAYSECS; 3996 $forum = $forumgenerator->create_instance(['course' => $course->id]); 3997 $forum->duedate = $duedate; 3998 $forum->cutoffdate = $cutoffdate; 3999 $DB->update_record('forum', $forum); 4000 4001 // Create a valid event. 4002 $event = new \calendar_event([ 4003 'name' => 'Test event', 4004 'description' => '', 4005 'format' => 1, 4006 'courseid' => $course->id, 4007 'groupid' => 0, 4008 'userid' => 2, 4009 'modulename' => 'forum', 4010 'instance' => $forum->id, 4011 'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE", 4012 'timestart' => 1, 4013 'timeduration' => 86400, 4014 'visible' => 1 4015 ]); 4016 4017 mod_forum_core_calendar_event_timestart_updated($event, $forum); 4018 4019 $forum = $DB->get_record('forum', ['id' => $forum->id]); 4020 $this->assertEquals($duedate, $forum->duedate); 4021 $this->assertEquals($cutoffdate, $forum->cutoffdate); 4022 } 4023 4024 /** 4025 * Due date events should update the forum due date. 4026 */ 4027 public function test_mod_forum_core_calendar_event_timestart_updated_due_event() { 4028 global $CFG, $DB; 4029 require_once($CFG->dirroot . "/calendar/lib.php"); 4030 4031 $this->resetAfterTest(true); 4032 $this->setAdminUser(); 4033 $generator = $this->getDataGenerator(); 4034 $course = $generator->create_course(); 4035 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4036 $duedate = time() + DAYSECS; 4037 $cutoffdate = $duedate + DAYSECS; 4038 $newduedate = $duedate + 1; 4039 $forum = $forumgenerator->create_instance(['course' => $course->id]); 4040 $forum->duedate = $duedate; 4041 $forum->cutoffdate = $cutoffdate; 4042 $DB->update_record('forum', $forum); 4043 4044 // Create a valid event. 4045 $event = new \calendar_event([ 4046 'name' => 'Test event', 4047 'description' => '', 4048 'format' => 1, 4049 'courseid' => $course->id, 4050 'groupid' => 0, 4051 'userid' => 2, 4052 'modulename' => 'forum', 4053 'instance' => $forum->id, 4054 'eventtype' => FORUM_EVENT_TYPE_DUE, 4055 'timestart' => $newduedate, 4056 'timeduration' => 86400, 4057 'visible' => 1 4058 ]); 4059 4060 mod_forum_core_calendar_event_timestart_updated($event, $forum); 4061 4062 $forum = $DB->get_record('forum', ['id' => $forum->id]); 4063 $this->assertEquals($newduedate, $forum->duedate); 4064 $this->assertEquals($cutoffdate, $forum->cutoffdate); 4065 } 4066 4067 /** 4068 * Test forum_get_layout_modes function. 4069 */ 4070 public function test_forum_get_layout_modes() { 4071 $expectednormal = [ 4072 FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), 4073 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), 4074 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), 4075 FORUM_MODE_NESTED => get_string('modenested', 'forum') 4076 ]; 4077 $expectedexperimental = [ 4078 FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), 4079 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), 4080 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), 4081 FORUM_MODE_NESTED_V2 => get_string('modenestedv2', 'forum') 4082 ]; 4083 4084 $this->assertEquals($expectednormal, forum_get_layout_modes()); 4085 $this->assertEquals($expectednormal, forum_get_layout_modes(false)); 4086 $this->assertEquals($expectedexperimental, forum_get_layout_modes(true)); 4087 } 4088 4089 /** 4090 * Provides data for tests that cause forum_check_throttling to return early. 4091 * 4092 * @return array 4093 */ 4094 public function forum_check_throttling_early_returns_provider() { 4095 return [ 4096 'Empty blockafter' => [(object)['id' => 1, 'course' => SITEID, 'blockafter' => 0]], 4097 'Empty blockperiod' => [(object)['id' => 1, 'course' => SITEID, 'blockafter' => DAYSECS, 'blockperiod' => 0]], 4098 ]; 4099 } 4100 4101 /** 4102 * Tests the early return scenarios of forum_check_throttling. 4103 * 4104 * @dataProvider forum_check_throttling_early_returns_provider 4105 * @covers ::forum_check_throttling 4106 * @param \stdClass $forum The forum data. 4107 */ 4108 public function test_forum_check_throttling_early_returns(\stdClass $forum) { 4109 $this->assertFalse(forum_check_throttling($forum)); 4110 } 4111 4112 /** 4113 * Provides data for tests that cause forum_check_throttling to throw exceptions early. 4114 * 4115 * @return array 4116 */ 4117 public function forum_check_throttling_early_exceptions_provider() { 4118 return [ 4119 'Non-object forum' => ['a'], 4120 'Forum ID not set' => [(object)['id' => false]], 4121 'Course ID not set' => [(object)['id' => 1]], 4122 ]; 4123 } 4124 4125 /** 4126 * Tests the early exception scenarios of forum_check_throttling. 4127 * 4128 * @dataProvider forum_check_throttling_early_exceptions_provider 4129 * @covers ::forum_check_throttling 4130 * @param mixed $forum The forum data. 4131 */ 4132 public function test_forum_check_throttling_early_exceptions($forum) { 4133 $this->expectException(\coding_exception::class); 4134 $this->assertFalse(forum_check_throttling($forum)); 4135 } 4136 4137 /** 4138 * Tests forum_check_throttling when a non-existent numeric ID is passed for its forum parameter. 4139 * 4140 * @covers ::forum_check_throttling 4141 */ 4142 public function test_forum_check_throttling_nonexistent_numeric_id() { 4143 $this->resetAfterTest(); 4144 4145 $this->expectException(\moodle_exception::class); 4146 forum_check_throttling(1); 4147 } 4148 4149 /** 4150 * Tests forum_check_throttling when a non-existent forum record is passed for its forum parameter. 4151 * 4152 * @covers ::forum_check_throttling 4153 */ 4154 public function test_forum_check_throttling_nonexistent_forum_cm() { 4155 $this->resetAfterTest(); 4156 4157 $dummyforum = (object)[ 4158 'id' => 1, 4159 'course' => SITEID, 4160 'blockafter' => 2, 4161 'blockperiod' => DAYSECS, 4162 ]; 4163 $this->expectException(\moodle_exception::class); 4164 forum_check_throttling($dummyforum); 4165 } 4166 4167 /** 4168 * Tests forum_check_throttling when a user with the 'mod/forum:postwithoutthrottling' capability. 4169 * 4170 * @covers ::forum_check_throttling 4171 */ 4172 public function test_forum_check_throttling_teacher() { 4173 $this->resetAfterTest(); 4174 4175 $generator = $this->getDataGenerator(); 4176 $course = $generator->create_course(); 4177 $teacher = $generator->create_and_enrol($course, 'teacher'); 4178 4179 /** @var mod_forum_generator $forumgenerator */ 4180 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4181 // Forum that limits students from creating more than two posts per day. 4182 $forum = $forumgenerator->create_instance( 4183 [ 4184 'course' => $course->id, 4185 'blockafter' => 2, 4186 'blockperiod' => DAYSECS, 4187 ] 4188 ); 4189 4190 $this->setUser($teacher); 4191 $discussionrecord = [ 4192 'course' => $course->id, 4193 'forum' => $forum->id, 4194 'userid' => $teacher->id, 4195 ]; 4196 $discussion = $forumgenerator->create_discussion($discussionrecord); 4197 // Create a forum post as the teacher. 4198 $postrecord = [ 4199 'userid' => $teacher->id, 4200 'discussion' => $discussion->id, 4201 ]; 4202 $forumgenerator->create_post($postrecord); 4203 // Create another forum post. 4204 $forumgenerator->create_post($postrecord); 4205 4206 $this->assertFalse(forum_check_throttling($forum)); 4207 } 4208 4209 /** 4210 * Tests forum_check_throttling for students. 4211 * 4212 * @covers ::forum_check_throttling 4213 */ 4214 public function test_forum_check_throttling_student() { 4215 $this->resetAfterTest(); 4216 4217 $generator = $this->getDataGenerator(); 4218 $course = $generator->create_course(); 4219 $student = $generator->create_and_enrol($course, 'student'); 4220 4221 /** @var mod_forum_generator $forumgenerator */ 4222 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4223 // Forum that limits students from creating more than two posts per day. 4224 $forum = $forumgenerator->create_instance( 4225 [ 4226 'course' => $course->id, 4227 'blockafter' => 2, 4228 'blockperiod' => DAYSECS, 4229 'warnafter' => 1, 4230 ] 4231 ); 4232 4233 $this->setUser($student); 4234 4235 // Student hasn't posted yet so no warning will be shown. 4236 $throttling = forum_check_throttling($forum); 4237 $this->assertFalse($throttling); 4238 4239 // Create a discussion. 4240 $discussionrecord = [ 4241 'course' => $course->id, 4242 'forum' => $forum->id, 4243 'userid' => $student->id, 4244 ]; 4245 $discussion = $forumgenerator->create_discussion($discussionrecord); 4246 4247 // A warning will be shown to the student, but they should still be able to post. 4248 $throttling = forum_check_throttling($forum); 4249 $this->assertIsObject($throttling); 4250 $this->assertTrue($throttling->canpost); 4251 4252 // Create another forum post as the student. 4253 $postrecord = [ 4254 'userid' => $student->id, 4255 'discussion' => $discussion->id, 4256 ]; 4257 $forumgenerator->create_post($postrecord); 4258 4259 // Student should now be unable to post after their second post. 4260 $throttling = forum_check_throttling($forum); 4261 $this->assertIsObject($throttling); 4262 $this->assertFalse($throttling->canpost); 4263 } 4264 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body