Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace mod_forum; 18 19 use mod_forum_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->assertEventContextNotUsed($event); 96 } 97 98 public function test_forum_get_courses_user_posted_in() { 99 $this->resetAfterTest(); 100 101 $user1 = $this->getDataGenerator()->create_user(); 102 $user2 = $this->getDataGenerator()->create_user(); 103 $user3 = $this->getDataGenerator()->create_user(); 104 105 $course1 = $this->getDataGenerator()->create_course(); 106 $course2 = $this->getDataGenerator()->create_course(); 107 $course3 = $this->getDataGenerator()->create_course(); 108 109 // Create 3 forums, one in each course. 110 $record = new \stdClass(); 111 $record->course = $course1->id; 112 $forum1 = $this->getDataGenerator()->create_module('forum', $record); 113 114 $record = new \stdClass(); 115 $record->course = $course2->id; 116 $forum2 = $this->getDataGenerator()->create_module('forum', $record); 117 118 $record = new \stdClass(); 119 $record->course = $course3->id; 120 $forum3 = $this->getDataGenerator()->create_module('forum', $record); 121 122 // Add a second forum in course 1. 123 $record = new \stdClass(); 124 $record->course = $course1->id; 125 $forum4 = $this->getDataGenerator()->create_module('forum', $record); 126 127 // Add discussions to course 1 started by user1. 128 $record = new \stdClass(); 129 $record->course = $course1->id; 130 $record->userid = $user1->id; 131 $record->forum = $forum1->id; 132 $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 133 134 $record = new \stdClass(); 135 $record->course = $course1->id; 136 $record->userid = $user1->id; 137 $record->forum = $forum4->id; 138 $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 139 140 // Add discussions to course2 started by user1. 141 $record = new \stdClass(); 142 $record->course = $course2->id; 143 $record->userid = $user1->id; 144 $record->forum = $forum2->id; 145 $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 146 147 // Add discussions to course 3 started by user2. 148 $record = new \stdClass(); 149 $record->course = $course3->id; 150 $record->userid = $user2->id; 151 $record->forum = $forum3->id; 152 $discussion3 = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 153 154 // Add post to course 3 by user1. 155 $record = new \stdClass(); 156 $record->course = $course3->id; 157 $record->userid = $user1->id; 158 $record->forum = $forum3->id; 159 $record->discussion = $discussion3->id; 160 $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 161 162 // User 3 hasn't posted anything, so shouldn't get any results. 163 $user3courses = forum_get_courses_user_posted_in($user3); 164 $this->assertEmpty($user3courses); 165 166 // User 2 has only posted in course3. 167 $user2courses = forum_get_courses_user_posted_in($user2); 168 $this->assertCount(1, $user2courses); 169 $user2course = array_shift($user2courses); 170 $this->assertEquals($course3->id, $user2course->id); 171 $this->assertEquals($course3->shortname, $user2course->shortname); 172 173 // User 1 has posted in all 3 courses. 174 $user1courses = forum_get_courses_user_posted_in($user1); 175 $this->assertCount(3, $user1courses); 176 foreach ($user1courses as $course) { 177 $this->assertContains($course->id, array($course1->id, $course2->id, $course3->id)); 178 $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname, 179 $course3->shortname)); 180 181 } 182 183 // User 1 has only started a discussion in course 1 and 2 though. 184 $user1courses = forum_get_courses_user_posted_in($user1, true); 185 $this->assertCount(2, $user1courses); 186 foreach ($user1courses as $course) { 187 $this->assertContains($course->id, array($course1->id, $course2->id)); 188 $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname)); 189 } 190 } 191 192 /** 193 * Test the logic in the forum_tp_can_track_forums() function. 194 */ 195 public function test_forum_tp_can_track_forums() { 196 global $CFG; 197 198 $this->resetAfterTest(); 199 200 $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1)); 201 $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0)); 202 $course = $this->getDataGenerator()->create_course(); 203 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off. 204 $forumoff = $this->getDataGenerator()->create_module('forum', $options); 205 206 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On. 207 $forumforce = $this->getDataGenerator()->create_module('forum', $options); 208 209 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional. 210 $forumoptional = $this->getDataGenerator()->create_module('forum', $options); 211 212 // Allow force. 213 $CFG->forum_allowforcedreadtracking = 1; 214 215 // User on, forum off, should be off. 216 $result = forum_tp_can_track_forums($forumoff, $useron); 217 $this->assertEquals(false, $result); 218 219 // User on, forum on, should be on. 220 $result = forum_tp_can_track_forums($forumforce, $useron); 221 $this->assertEquals(true, $result); 222 223 // User on, forum optional, should be on. 224 $result = forum_tp_can_track_forums($forumoptional, $useron); 225 $this->assertEquals(true, $result); 226 227 // User off, forum off, should be off. 228 $result = forum_tp_can_track_forums($forumoff, $useroff); 229 $this->assertEquals(false, $result); 230 231 // User off, forum force, should be on. 232 $result = forum_tp_can_track_forums($forumforce, $useroff); 233 $this->assertEquals(true, $result); 234 235 // User off, forum optional, should be off. 236 $result = forum_tp_can_track_forums($forumoptional, $useroff); 237 $this->assertEquals(false, $result); 238 239 // Don't allow force. 240 $CFG->forum_allowforcedreadtracking = 0; 241 242 // User on, forum off, should be off. 243 $result = forum_tp_can_track_forums($forumoff, $useron); 244 $this->assertEquals(false, $result); 245 246 // User on, forum on, should be on. 247 $result = forum_tp_can_track_forums($forumforce, $useron); 248 $this->assertEquals(true, $result); 249 250 // User on, forum optional, should be on. 251 $result = forum_tp_can_track_forums($forumoptional, $useron); 252 $this->assertEquals(true, $result); 253 254 // User off, forum off, should be off. 255 $result = forum_tp_can_track_forums($forumoff, $useroff); 256 $this->assertEquals(false, $result); 257 258 // User off, forum force, should be off. 259 $result = forum_tp_can_track_forums($forumforce, $useroff); 260 $this->assertEquals(false, $result); 261 262 // User off, forum optional, should be off. 263 $result = forum_tp_can_track_forums($forumoptional, $useroff); 264 $this->assertEquals(false, $result); 265 266 } 267 268 /** 269 * Test the logic in the test_forum_tp_is_tracked() function. 270 */ 271 public function test_forum_tp_is_tracked() { 272 global $CFG; 273 274 $this->resetAfterTest(); 275 276 $cache = \cache::make('mod_forum', 'forum_is_tracked'); 277 $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1)); 278 $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0)); 279 $course = $this->getDataGenerator()->create_course(); 280 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off. 281 $forumoff = $this->getDataGenerator()->create_module('forum', $options); 282 283 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On. 284 $forumforce = $this->getDataGenerator()->create_module('forum', $options); 285 286 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional. 287 $forumoptional = $this->getDataGenerator()->create_module('forum', $options); 288 289 // Allow force. 290 $CFG->forum_allowforcedreadtracking = 1; 291 292 // User on, forum off, should be off. 293 $result = forum_tp_is_tracked($forumoff, $useron); 294 $this->assertEquals(false, $result); 295 296 // User on, forum force, should be on. 297 $result = forum_tp_is_tracked($forumforce, $useron); 298 $this->assertEquals(true, $result); 299 300 // User on, forum optional, should be on. 301 $result = forum_tp_is_tracked($forumoptional, $useron); 302 $this->assertEquals(true, $result); 303 304 // User off, forum off, should be off. 305 $result = forum_tp_is_tracked($forumoff, $useroff); 306 $this->assertEquals(false, $result); 307 308 // User off, forum force, should be on. 309 $result = forum_tp_is_tracked($forumforce, $useroff); 310 $this->assertEquals(true, $result); 311 312 // User off, forum optional, should be off. 313 $result = forum_tp_is_tracked($forumoptional, $useroff); 314 $this->assertEquals(false, $result); 315 316 $cache->purge(); 317 // Don't allow force. 318 $CFG->forum_allowforcedreadtracking = 0; 319 320 // User on, forum off, should be off. 321 $result = forum_tp_is_tracked($forumoff, $useron); 322 $this->assertEquals(false, $result); 323 324 // User on, forum force, should be on. 325 $result = forum_tp_is_tracked($forumforce, $useron); 326 $this->assertEquals(true, $result); 327 328 // User on, forum optional, should be on. 329 $result = forum_tp_is_tracked($forumoptional, $useron); 330 $this->assertEquals(true, $result); 331 332 // User off, forum off, should be off. 333 $result = forum_tp_is_tracked($forumoff, $useroff); 334 $this->assertEquals(false, $result); 335 336 // User off, forum force, should be off. 337 $result = forum_tp_is_tracked($forumforce, $useroff); 338 $this->assertEquals(false, $result); 339 340 // User off, forum optional, should be off. 341 $result = forum_tp_is_tracked($forumoptional, $useroff); 342 $this->assertEquals(false, $result); 343 344 // Stop tracking so we can test again. 345 forum_tp_stop_tracking($forumforce->id, $useron->id); 346 forum_tp_stop_tracking($forumoptional->id, $useron->id); 347 forum_tp_stop_tracking($forumforce->id, $useroff->id); 348 forum_tp_stop_tracking($forumoptional->id, $useroff->id); 349 350 $cache->purge(); 351 // Allow force. 352 $CFG->forum_allowforcedreadtracking = 1; 353 354 // User on, preference off, forum force, should be on. 355 $result = forum_tp_is_tracked($forumforce, $useron); 356 $this->assertEquals(true, $result); 357 358 // User on, preference off, forum optional, should be on. 359 $result = forum_tp_is_tracked($forumoptional, $useron); 360 $this->assertEquals(false, $result); 361 362 // User off, preference off, forum force, should be on. 363 $result = forum_tp_is_tracked($forumforce, $useroff); 364 $this->assertEquals(true, $result); 365 366 // User off, preference off, forum optional, should be off. 367 $result = forum_tp_is_tracked($forumoptional, $useroff); 368 $this->assertEquals(false, $result); 369 370 $cache->purge(); 371 // Don't allow force. 372 $CFG->forum_allowforcedreadtracking = 0; 373 374 // User on, preference off, forum force, should be on. 375 $result = forum_tp_is_tracked($forumforce, $useron); 376 $this->assertEquals(false, $result); 377 378 // User on, preference off, forum optional, should be on. 379 $result = forum_tp_is_tracked($forumoptional, $useron); 380 $this->assertEquals(false, $result); 381 382 // User off, preference off, forum force, should be off. 383 $result = forum_tp_is_tracked($forumforce, $useroff); 384 $this->assertEquals(false, $result); 385 386 // User off, preference off, forum optional, should be off. 387 $result = forum_tp_is_tracked($forumoptional, $useroff); 388 $this->assertEquals(false, $result); 389 } 390 391 /** 392 * Test the logic in the forum_tp_get_course_unread_posts() function. 393 */ 394 public function test_forum_tp_get_course_unread_posts() { 395 global $CFG; 396 397 $this->resetAfterTest(); 398 399 $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1)); 400 $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0)); 401 $course = $this->getDataGenerator()->create_course(); 402 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off. 403 $forumoff = $this->getDataGenerator()->create_module('forum', $options); 404 405 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On. 406 $forumforce = $this->getDataGenerator()->create_module('forum', $options); 407 408 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional. 409 $forumoptional = $this->getDataGenerator()->create_module('forum', $options); 410 411 // Add discussions to the tracking off forum. 412 $record = new \stdClass(); 413 $record->course = $course->id; 414 $record->userid = $useron->id; 415 $record->forum = $forumoff->id; 416 $discussionoff = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 417 418 // Add discussions to the tracking forced forum. 419 $record = new \stdClass(); 420 $record->course = $course->id; 421 $record->userid = $useron->id; 422 $record->forum = $forumforce->id; 423 $discussionforce = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 424 425 // Add post to the tracking forced discussion. 426 $record = new \stdClass(); 427 $record->course = $course->id; 428 $record->userid = $useroff->id; 429 $record->forum = $forumforce->id; 430 $record->discussion = $discussionforce->id; 431 $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 432 433 // Add discussions to the tracking optional forum. 434 $record = new \stdClass(); 435 $record->course = $course->id; 436 $record->userid = $useron->id; 437 $record->forum = $forumoptional->id; 438 $discussionoptional = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 439 440 // Allow force. 441 $CFG->forum_allowforcedreadtracking = 1; 442 443 $result = forum_tp_get_course_unread_posts($useron->id, $course->id); 444 $this->assertEquals(2, count($result)); 445 $this->assertEquals(false, isset($result[$forumoff->id])); 446 $this->assertEquals(true, isset($result[$forumforce->id])); 447 $this->assertEquals(2, $result[$forumforce->id]->unread); 448 $this->assertEquals(true, isset($result[$forumoptional->id])); 449 $this->assertEquals(1, $result[$forumoptional->id]->unread); 450 451 $result = forum_tp_get_course_unread_posts($useroff->id, $course->id); 452 $this->assertEquals(1, count($result)); 453 $this->assertEquals(false, isset($result[$forumoff->id])); 454 $this->assertEquals(true, isset($result[$forumforce->id])); 455 $this->assertEquals(2, $result[$forumforce->id]->unread); 456 $this->assertEquals(false, isset($result[$forumoptional->id])); 457 458 // Don't allow force. 459 $CFG->forum_allowforcedreadtracking = 0; 460 461 $result = forum_tp_get_course_unread_posts($useron->id, $course->id); 462 $this->assertEquals(2, count($result)); 463 $this->assertEquals(false, isset($result[$forumoff->id])); 464 $this->assertEquals(true, isset($result[$forumforce->id])); 465 $this->assertEquals(2, $result[$forumforce->id]->unread); 466 $this->assertEquals(true, isset($result[$forumoptional->id])); 467 $this->assertEquals(1, $result[$forumoptional->id]->unread); 468 469 $result = forum_tp_get_course_unread_posts($useroff->id, $course->id); 470 $this->assertEquals(0, count($result)); 471 $this->assertEquals(false, isset($result[$forumoff->id])); 472 $this->assertEquals(false, isset($result[$forumforce->id])); 473 $this->assertEquals(false, isset($result[$forumoptional->id])); 474 475 // Stop tracking so we can test again. 476 forum_tp_stop_tracking($forumforce->id, $useron->id); 477 forum_tp_stop_tracking($forumoptional->id, $useron->id); 478 forum_tp_stop_tracking($forumforce->id, $useroff->id); 479 forum_tp_stop_tracking($forumoptional->id, $useroff->id); 480 481 // Allow force. 482 $CFG->forum_allowforcedreadtracking = 1; 483 484 $result = forum_tp_get_course_unread_posts($useron->id, $course->id); 485 $this->assertEquals(1, count($result)); 486 $this->assertEquals(false, isset($result[$forumoff->id])); 487 $this->assertEquals(true, isset($result[$forumforce->id])); 488 $this->assertEquals(2, $result[$forumforce->id]->unread); 489 $this->assertEquals(false, isset($result[$forumoptional->id])); 490 491 $result = forum_tp_get_course_unread_posts($useroff->id, $course->id); 492 $this->assertEquals(1, count($result)); 493 $this->assertEquals(false, isset($result[$forumoff->id])); 494 $this->assertEquals(true, isset($result[$forumforce->id])); 495 $this->assertEquals(2, $result[$forumforce->id]->unread); 496 $this->assertEquals(false, isset($result[$forumoptional->id])); 497 498 // Don't allow force. 499 $CFG->forum_allowforcedreadtracking = 0; 500 501 $result = forum_tp_get_course_unread_posts($useron->id, $course->id); 502 $this->assertEquals(0, count($result)); 503 $this->assertEquals(false, isset($result[$forumoff->id])); 504 $this->assertEquals(false, isset($result[$forumforce->id])); 505 $this->assertEquals(false, isset($result[$forumoptional->id])); 506 507 $result = forum_tp_get_course_unread_posts($useroff->id, $course->id); 508 $this->assertEquals(0, count($result)); 509 $this->assertEquals(false, isset($result[$forumoff->id])); 510 $this->assertEquals(false, isset($result[$forumforce->id])); 511 $this->assertEquals(false, isset($result[$forumoptional->id])); 512 } 513 514 /** 515 * Test the logic in the forum_tp_get_course_unread_posts() function when private replies are present. 516 * 517 * @covers ::forum_tp_get_course_unread_posts 518 */ 519 public function test_forum_tp_get_course_unread_posts_with_private_replies() { 520 global $DB; 521 522 $this->resetAfterTest(); 523 524 $generator = $this->getDataGenerator(); 525 526 // Create 3 students. 527 $s1 = $generator->create_user(['trackforums' => 1]); 528 $s2 = $generator->create_user(['trackforums' => 1]); 529 $s3 = $generator->create_user(['trackforums' => 1]); 530 // Editing teacher. 531 $t1 = $generator->create_user(['trackforums' => 1]); 532 // Non-editing teacher. 533 $t2 = $generator->create_user(['trackforums' => 1]); 534 535 // Create our course. 536 $course = $generator->create_course(); 537 538 // Enrol editing and non-editing teachers. 539 $generator->enrol_user($t1->id, $course->id, 'editingteacher'); 540 $generator->enrol_user($t2->id, $course->id, 'teacher'); 541 542 // Create forums. 543 $forum1 = $generator->create_module('forum', ['course' => $course->id]); 544 $forum2 = $generator->create_module('forum', ['course' => $course->id]); 545 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 546 547 // Prevent the non-editing teacher from reading private replies in forum 2. 548 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']); 549 $forum2cm = get_coursemodule_from_instance('forum', $forum2->id); 550 $forum2context = \context_module::instance($forum2cm->id); 551 role_change_permission($teacherroleid, $forum2context, 'mod/forum:readprivatereplies', CAP_PREVENT); 552 553 // Create discussion by s1. 554 $discussiondata = (object)[ 555 'course' => $course->id, 556 'forum' => $forum1->id, 557 'userid' => $s1->id, 558 ]; 559 $discussion1 = $forumgenerator->create_discussion($discussiondata); 560 561 // Create discussion by s2. 562 $discussiondata->userid = $s2->id; 563 $discussion2 = $forumgenerator->create_discussion($discussiondata); 564 565 // Create discussion by s3. 566 $discussiondata->userid = $s3->id; 567 $discussion3 = $forumgenerator->create_discussion($discussiondata); 568 569 // Post a normal reply to s1's discussion in forum 1 as the editing teacher. 570 $replydata = (object)[ 571 'course' => $course->id, 572 'forum' => $forum1->id, 573 'discussion' => $discussion1->id, 574 'userid' => $t1->id, 575 ]; 576 $forumgenerator->create_post($replydata); 577 578 // Post a normal reply to s2's discussion as the editing teacher. 579 $replydata->discussion = $discussion2->id; 580 $forumgenerator->create_post($replydata); 581 582 // Post a normal reply to s3's discussion as the editing teacher. 583 $replydata->discussion = $discussion3->id; 584 $forumgenerator->create_post($replydata); 585 586 // Post a private reply to s1's discussion in forum 1 as the editing teacher. 587 $replydata->discussion = $discussion1->id; 588 $replydata->userid = $t1->id; 589 $replydata->privatereplyto = $s1->id; 590 $forumgenerator->create_post($replydata); 591 // Post another private reply to s1 as the teacher. 592 $forumgenerator->create_post($replydata); 593 594 // Post a private reply to s2's discussion as the editing teacher. 595 $replydata->discussion = $discussion2->id; 596 $replydata->privatereplyto = $s2->id; 597 $forumgenerator->create_post($replydata); 598 599 // Create discussion by s1 in forum 2. 600 $discussiondata->forum = $forum2->id; 601 $discussiondata->userid = $s1->id; 602 $discussion21 = $forumgenerator->create_discussion($discussiondata); 603 604 // Post a private reply to s1's discussion in forum 2 as the editing teacher. 605 $replydata->discussion = $discussion21->id; 606 $replydata->privatereplyto = $s1->id; 607 $forumgenerator->create_post($replydata); 608 609 // Let's count! 610 // S1 should see 8 unread posts 3 discussions posts + 2 private replies + 3 normal replies. 611 $result = forum_tp_get_course_unread_posts($s1->id, $course->id); 612 $unreadcounts = $result[$forum1->id]; 613 $this->assertEquals(8, $unreadcounts->unread); 614 615 // S2 should see 7 unread posts 3 discussions posts + 1 private reply + 3 normal replies. 616 $result = forum_tp_get_course_unread_posts($s2->id, $course->id); 617 $unreadcounts = $result[$forum1->id]; 618 $this->assertEquals(7, $unreadcounts->unread); 619 620 // S3 should see 6 unread posts 3 discussions posts + 3 normal replies. No private replies. 621 $result = forum_tp_get_course_unread_posts($s3->id, $course->id); 622 $unreadcounts = $result[$forum1->id]; 623 $this->assertEquals(6, $unreadcounts->unread); 624 625 // The editing teacher should see 9 unread posts in forum 1: 3 discussions posts + 3 normal replies + 3 private replies. 626 $result = forum_tp_get_course_unread_posts($t1->id, $course->id); 627 $unreadcounts = $result[$forum1->id]; 628 $this->assertEquals(9, $unreadcounts->unread); 629 630 // Same with the non-editing teacher, since they can read private replies by default. 631 $result = forum_tp_get_course_unread_posts($t2->id, $course->id); 632 $unreadcounts = $result[$forum1->id]; 633 $this->assertEquals(9, $unreadcounts->unread); 634 635 // But for forum 2, the non-editing teacher should only see 1 unread which is s1's discussion post. 636 $unreadcounts = $result[$forum2->id]; 637 $this->assertEquals(1, $unreadcounts->unread); 638 } 639 640 /** 641 * Test the logic in the forum_tp_count_forum_unread_posts() function when private replies are present but without 642 * separate group mode. This should yield the same results returned by forum_tp_get_course_unread_posts(). 643 * 644 * @covers ::forum_tp_count_forum_unread_posts 645 */ 646 public function test_forum_tp_count_forum_unread_posts_with_private_replies() { 647 global $DB; 648 649 $this->resetAfterTest(); 650 651 $generator = $this->getDataGenerator(); 652 653 // Create 3 students. 654 $s1 = $generator->create_user(['username' => 's1', 'trackforums' => 1]); 655 $s2 = $generator->create_user(['username' => 's2', 'trackforums' => 1]); 656 $s3 = $generator->create_user(['username' => 's3', 'trackforums' => 1]); 657 // Editing teacher. 658 $t1 = $generator->create_user(['username' => 't1', 'trackforums' => 1]); 659 // Non-editing teacher. 660 $t2 = $generator->create_user(['username' => 't2', 'trackforums' => 1]); 661 662 // Create our course. 663 $course = $generator->create_course(); 664 665 // Enrol editing and non-editing teachers. 666 $generator->enrol_user($t1->id, $course->id, 'editingteacher'); 667 $generator->enrol_user($t2->id, $course->id, 'teacher'); 668 669 // Create forums. 670 $forum1 = $generator->create_module('forum', ['course' => $course->id]); 671 $forum2 = $generator->create_module('forum', ['course' => $course->id]); 672 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 673 674 // Prevent the non-editing teacher from reading private replies in forum 2. 675 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']); 676 $forum2cm = get_coursemodule_from_instance('forum', $forum2->id); 677 $forum2context = \context_module::instance($forum2cm->id); 678 role_change_permission($teacherroleid, $forum2context, 'mod/forum:readprivatereplies', CAP_PREVENT); 679 680 // Create discussion by s1. 681 $discussiondata = (object)[ 682 'course' => $course->id, 683 'forum' => $forum1->id, 684 'userid' => $s1->id, 685 ]; 686 $discussion1 = $forumgenerator->create_discussion($discussiondata); 687 688 // Create discussion by s2. 689 $discussiondata->userid = $s2->id; 690 $discussion2 = $forumgenerator->create_discussion($discussiondata); 691 692 // Create discussion by s3. 693 $discussiondata->userid = $s3->id; 694 $discussion3 = $forumgenerator->create_discussion($discussiondata); 695 696 // Post a normal reply to s1's discussion in forum 1 as the editing teacher. 697 $replydata = (object)[ 698 'course' => $course->id, 699 'forum' => $forum1->id, 700 'discussion' => $discussion1->id, 701 'userid' => $t1->id, 702 ]; 703 $forumgenerator->create_post($replydata); 704 705 // Post a normal reply to s2's discussion as the editing teacher. 706 $replydata->discussion = $discussion2->id; 707 $forumgenerator->create_post($replydata); 708 709 // Post a normal reply to s3's discussion as the editing teacher. 710 $replydata->discussion = $discussion3->id; 711 $forumgenerator->create_post($replydata); 712 713 // Post a private reply to s1's discussion in forum 1 as the editing teacher. 714 $replydata->discussion = $discussion1->id; 715 $replydata->userid = $t1->id; 716 $replydata->privatereplyto = $s1->id; 717 $forumgenerator->create_post($replydata); 718 // Post another private reply to s1 as the teacher. 719 $forumgenerator->create_post($replydata); 720 721 // Post a private reply to s2's discussion as the editing teacher. 722 $replydata->discussion = $discussion2->id; 723 $replydata->privatereplyto = $s2->id; 724 $forumgenerator->create_post($replydata); 725 726 // Create discussion by s1 in forum 2. 727 $discussiondata->forum = $forum2->id; 728 $discussiondata->userid = $s1->id; 729 $discussion11 = $forumgenerator->create_discussion($discussiondata); 730 731 // Post a private reply to s1's discussion in forum 2 as the editing teacher. 732 $replydata->discussion = $discussion11->id; 733 $replydata->privatereplyto = $s1->id; 734 $forumgenerator->create_post($replydata); 735 736 // Let's count! 737 // S1 should see 8 unread posts 3 discussions posts + 2 private replies + 3 normal replies. 738 $this->setUser($s1); 739 $forum1cm = get_coursemodule_from_instance('forum', $forum1->id); 740 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 741 $this->assertEquals(8, $result); 742 743 // S2 should see 7 unread posts 3 discussions posts + 1 private reply + 3 normal replies. 744 $this->setUser($s2); 745 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 746 $this->assertEquals(7, $result); 747 748 // S3 should see 6 unread posts 3 discussions posts + 3 normal replies. No private replies. 749 $this->setUser($s3); 750 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 751 $this->assertEquals(6, $result); 752 753 // The editing teacher should see 9 unread posts in forum 1: 3 discussions posts + 3 normal replies + 3 private replies. 754 $this->setUser($t1); 755 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 756 $this->assertEquals(9, $result); 757 758 // Same with the non-editing teacher, since they can read private replies by default. 759 $this->setUser($t2); 760 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 761 $this->assertEquals(9, $result); 762 763 // But for forum 2, the non-editing teacher should only see 1 unread which is s1's discussion post. 764 $result = forum_tp_count_forum_unread_posts($forum2cm, $course); 765 $this->assertEquals(1, $result); 766 } 767 768 /** 769 * Test the logic in the forum_tp_count_forum_unread_posts() function when private replies are present and group modes are set. 770 * 771 * @covers ::forum_tp_count_forum_unread_posts 772 */ 773 public function test_forum_tp_count_forum_unread_posts_with_private_replies_and_separate_groups() { 774 $this->resetAfterTest(); 775 776 $generator = $this->getDataGenerator(); 777 778 // Create 3 students. 779 $s1 = $generator->create_user(['username' => 's1', 'trackforums' => 1]); 780 $s2 = $generator->create_user(['username' => 's2', 'trackforums' => 1]); 781 // Editing teacher. 782 $t1 = $generator->create_user(['username' => 't1', 'trackforums' => 1]); 783 784 // Create our course. 785 $course = $generator->create_course(); 786 787 // Enrol students, editing and non-editing teachers. 788 $generator->enrol_user($s1->id, $course->id, 'student'); 789 $generator->enrol_user($s2->id, $course->id, 'student'); 790 $generator->enrol_user($t1->id, $course->id, 'editingteacher'); 791 792 // Create groups. 793 $g1 = $generator->create_group(['courseid' => $course->id]); 794 $g2 = $generator->create_group(['courseid' => $course->id]); 795 $generator->create_group_member(['groupid' => $g1->id, 'userid' => $s1->id]); 796 $generator->create_group_member(['groupid' => $g2->id, 'userid' => $s2->id]); 797 798 // Create forums. 799 $forum1 = $generator->create_module('forum', ['course' => $course->id, 'groupmode' => SEPARATEGROUPS]); 800 $forum2 = $generator->create_module('forum', ['course' => $course->id, 'groupmode' => VISIBLEGROUPS]); 801 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 802 803 // Create discussion by s1. 804 $discussiondata = (object)[ 805 'course' => $course->id, 806 'forum' => $forum1->id, 807 'userid' => $s1->id, 808 'groupid' => $g1->id, 809 ]; 810 $discussion1 = $forumgenerator->create_discussion($discussiondata); 811 812 // Create discussion by s2. 813 $discussiondata->userid = $s2->id; 814 $discussiondata->groupid = $g2->id; 815 $discussion2 = $forumgenerator->create_discussion($discussiondata); 816 817 // Post a normal reply to s1's discussion in forum 1 as the editing teacher. 818 $replydata = (object)[ 819 'course' => $course->id, 820 'forum' => $forum1->id, 821 'discussion' => $discussion1->id, 822 'userid' => $t1->id, 823 ]; 824 $forumgenerator->create_post($replydata); 825 826 // Post a normal reply to s2's discussion as the editing teacher. 827 $replydata->discussion = $discussion2->id; 828 $forumgenerator->create_post($replydata); 829 830 // Post a private reply to s1's discussion in forum 1 as the editing teacher. 831 $replydata->discussion = $discussion1->id; 832 $replydata->userid = $t1->id; 833 $replydata->privatereplyto = $s1->id; 834 $forumgenerator->create_post($replydata); 835 // Post another private reply to s1 as the teacher. 836 $forumgenerator->create_post($replydata); 837 838 // Post a private reply to s2's discussion as the editing teacher. 839 $replydata->discussion = $discussion2->id; 840 $replydata->privatereplyto = $s2->id; 841 $forumgenerator->create_post($replydata); 842 843 // Create discussion by s1 in forum 2. 844 $discussiondata->forum = $forum2->id; 845 $discussiondata->userid = $s1->id; 846 $discussiondata->groupid = $g1->id; 847 $discussion21 = $forumgenerator->create_discussion($discussiondata); 848 849 // Post a private reply to s1's discussion in forum 2 as the editing teacher. 850 $replydata->discussion = $discussion21->id; 851 $replydata->privatereplyto = $s1->id; 852 $forumgenerator->create_post($replydata); 853 854 // Let's count! 855 // S1 should see 4 unread posts in forum 1 (1 discussions post + 2 private replies + 1 normal reply). 856 $this->setUser($s1); 857 $forum1cm = get_coursemodule_from_instance('forum', $forum1->id); 858 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 859 $this->assertEquals(4, $result); 860 861 // S2 should see 3 unread posts in forum 1 (1 discussions post + 1 private reply + 1 normal reply). 862 $this->setUser($s2); 863 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 864 $this->assertEquals(3, $result); 865 866 // S2 should see 1 unread posts in forum 2 (visible groups, 1 discussion post from s1). 867 $forum2cm = get_coursemodule_from_instance('forum', $forum2->id); 868 $result = forum_tp_count_forum_unread_posts($forum2cm, $course, true); 869 $this->assertEquals(1, $result); 870 871 // The editing teacher should still see 7 unread posts (2 discussions posts + 2 normal replies + 3 private replies) 872 // in forum 1 since they have the capability to view all groups by default. 873 $this->setUser($t1); 874 $result = forum_tp_count_forum_unread_posts($forum1cm, $course, true); 875 $this->assertEquals(7, $result); 876 } 877 878 /** 879 * Test the logic in the test_forum_tp_get_untracked_forums() function. 880 */ 881 public function test_forum_tp_get_untracked_forums() { 882 global $CFG; 883 884 $this->resetAfterTest(); 885 886 $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1)); 887 $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0)); 888 $course = $this->getDataGenerator()->create_course(); 889 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off. 890 $forumoff = $this->getDataGenerator()->create_module('forum', $options); 891 892 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On. 893 $forumforce = $this->getDataGenerator()->create_module('forum', $options); 894 895 $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional. 896 $forumoptional = $this->getDataGenerator()->create_module('forum', $options); 897 898 // Allow force. 899 $CFG->forum_allowforcedreadtracking = 1; 900 901 // On user with force on. 902 $result = forum_tp_get_untracked_forums($useron->id, $course->id); 903 $this->assertEquals(1, count($result)); 904 $this->assertEquals(true, isset($result[$forumoff->id])); 905 906 // Off user with force on. 907 $result = forum_tp_get_untracked_forums($useroff->id, $course->id); 908 $this->assertEquals(2, count($result)); 909 $this->assertEquals(true, isset($result[$forumoff->id])); 910 $this->assertEquals(true, isset($result[$forumoptional->id])); 911 912 // Don't allow force. 913 $CFG->forum_allowforcedreadtracking = 0; 914 915 // On user with force off. 916 $result = forum_tp_get_untracked_forums($useron->id, $course->id); 917 $this->assertEquals(1, count($result)); 918 $this->assertEquals(true, isset($result[$forumoff->id])); 919 920 // Off user with force off. 921 $result = forum_tp_get_untracked_forums($useroff->id, $course->id); 922 $this->assertEquals(3, count($result)); 923 $this->assertEquals(true, isset($result[$forumoff->id])); 924 $this->assertEquals(true, isset($result[$forumoptional->id])); 925 $this->assertEquals(true, isset($result[$forumforce->id])); 926 927 // Stop tracking so we can test again. 928 forum_tp_stop_tracking($forumforce->id, $useron->id); 929 forum_tp_stop_tracking($forumoptional->id, $useron->id); 930 forum_tp_stop_tracking($forumforce->id, $useroff->id); 931 forum_tp_stop_tracking($forumoptional->id, $useroff->id); 932 933 // Allow force. 934 $CFG->forum_allowforcedreadtracking = 1; 935 936 // On user with force on. 937 $result = forum_tp_get_untracked_forums($useron->id, $course->id); 938 $this->assertEquals(2, count($result)); 939 $this->assertEquals(true, isset($result[$forumoff->id])); 940 $this->assertEquals(true, isset($result[$forumoptional->id])); 941 942 // Off user with force on. 943 $result = forum_tp_get_untracked_forums($useroff->id, $course->id); 944 $this->assertEquals(2, count($result)); 945 $this->assertEquals(true, isset($result[$forumoff->id])); 946 $this->assertEquals(true, isset($result[$forumoptional->id])); 947 948 // Don't allow force. 949 $CFG->forum_allowforcedreadtracking = 0; 950 951 // On user with force off. 952 $result = forum_tp_get_untracked_forums($useron->id, $course->id); 953 $this->assertEquals(3, count($result)); 954 $this->assertEquals(true, isset($result[$forumoff->id])); 955 $this->assertEquals(true, isset($result[$forumoptional->id])); 956 $this->assertEquals(true, isset($result[$forumforce->id])); 957 958 // Off user with force off. 959 $result = forum_tp_get_untracked_forums($useroff->id, $course->id); 960 $this->assertEquals(3, count($result)); 961 $this->assertEquals(true, isset($result[$forumoff->id])); 962 $this->assertEquals(true, isset($result[$forumoptional->id])); 963 $this->assertEquals(true, isset($result[$forumforce->id])); 964 } 965 966 /** 967 * Test subscription using automatic subscription on create. 968 */ 969 public function test_forum_auto_subscribe_on_create() { 970 global $CFG; 971 972 $this->resetAfterTest(); 973 974 $usercount = 5; 975 $course = $this->getDataGenerator()->create_course(); 976 $users = array(); 977 978 for ($i = 0; $i < $usercount; $i++) { 979 $user = $this->getDataGenerator()->create_user(); 980 $users[] = $user; 981 $this->getDataGenerator()->enrol_user($user->id, $course->id); 982 } 983 984 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); // Automatic Subscription. 985 $forum = $this->getDataGenerator()->create_module('forum', $options); 986 987 $result = \mod_forum\subscriptions::fetch_subscribed_users($forum); 988 $this->assertEquals($usercount, count($result)); 989 foreach ($users as $user) { 990 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum)); 991 } 992 } 993 994 /** 995 * Test subscription using forced subscription on create. 996 */ 997 public function test_forum_forced_subscribe_on_create() { 998 global $CFG; 999 1000 $this->resetAfterTest(); 1001 1002 $usercount = 5; 1003 $course = $this->getDataGenerator()->create_course(); 1004 $users = array(); 1005 1006 for ($i = 0; $i < $usercount; $i++) { 1007 $user = $this->getDataGenerator()->create_user(); 1008 $users[] = $user; 1009 $this->getDataGenerator()->enrol_user($user->id, $course->id); 1010 } 1011 1012 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); // Forced subscription. 1013 $forum = $this->getDataGenerator()->create_module('forum', $options); 1014 1015 $result = \mod_forum\subscriptions::fetch_subscribed_users($forum); 1016 $this->assertEquals($usercount, count($result)); 1017 foreach ($users as $user) { 1018 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum)); 1019 } 1020 } 1021 1022 /** 1023 * Test subscription using optional subscription on create. 1024 */ 1025 public function test_forum_optional_subscribe_on_create() { 1026 global $CFG; 1027 1028 $this->resetAfterTest(); 1029 1030 $usercount = 5; 1031 $course = $this->getDataGenerator()->create_course(); 1032 $users = array(); 1033 1034 for ($i = 0; $i < $usercount; $i++) { 1035 $user = $this->getDataGenerator()->create_user(); 1036 $users[] = $user; 1037 $this->getDataGenerator()->enrol_user($user->id, $course->id); 1038 } 1039 1040 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); // Subscription optional. 1041 $forum = $this->getDataGenerator()->create_module('forum', $options); 1042 1043 $result = \mod_forum\subscriptions::fetch_subscribed_users($forum); 1044 // No subscriptions by default. 1045 $this->assertEquals(0, count($result)); 1046 foreach ($users as $user) { 1047 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum)); 1048 } 1049 } 1050 1051 /** 1052 * Test subscription using disallow subscription on create. 1053 */ 1054 public function test_forum_disallow_subscribe_on_create() { 1055 global $CFG; 1056 1057 $this->resetAfterTest(); 1058 1059 $usercount = 5; 1060 $course = $this->getDataGenerator()->create_course(); 1061 $users = array(); 1062 1063 for ($i = 0; $i < $usercount; $i++) { 1064 $user = $this->getDataGenerator()->create_user(); 1065 $users[] = $user; 1066 $this->getDataGenerator()->enrol_user($user->id, $course->id); 1067 } 1068 1069 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); // Subscription prevented. 1070 $forum = $this->getDataGenerator()->create_module('forum', $options); 1071 1072 $result = \mod_forum\subscriptions::fetch_subscribed_users($forum); 1073 // No subscriptions by default. 1074 $this->assertEquals(0, count($result)); 1075 foreach ($users as $user) { 1076 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum)); 1077 } 1078 } 1079 1080 /** 1081 * Test that context fetching returns the appropriate context. 1082 */ 1083 public function test_forum_get_context() { 1084 global $DB, $PAGE; 1085 1086 $this->resetAfterTest(); 1087 1088 // Setup test data. 1089 $course = $this->getDataGenerator()->create_course(); 1090 $coursecontext = \context_course::instance($course->id); 1091 1092 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 1093 $forum = $this->getDataGenerator()->create_module('forum', $options); 1094 $forumcm = get_coursemodule_from_instance('forum', $forum->id); 1095 $forumcontext = \context_module::instance($forumcm->id); 1096 1097 // First check that specifying the context results in the correct context being returned. 1098 // Do this before we set up the page object and we should return from the coursemodule record. 1099 // There should be no DB queries here because the context type was correct. 1100 $startcount = $DB->perf_get_reads(); 1101 $result = forum_get_context($forum->id, $forumcontext); 1102 $aftercount = $DB->perf_get_reads(); 1103 $this->assertEquals($forumcontext, $result); 1104 $this->assertEquals(0, $aftercount - $startcount); 1105 1106 // And a context which is not the correct type. 1107 // This tests will result in a DB query to fetch the course_module. 1108 $startcount = $DB->perf_get_reads(); 1109 $result = forum_get_context($forum->id, $coursecontext); 1110 $aftercount = $DB->perf_get_reads(); 1111 $this->assertEquals($forumcontext, $result); 1112 $this->assertEquals(1, $aftercount - $startcount); 1113 1114 // Now do not specify a context at all. 1115 // This tests will result in a DB query to fetch the course_module. 1116 $startcount = $DB->perf_get_reads(); 1117 $result = forum_get_context($forum->id); 1118 $aftercount = $DB->perf_get_reads(); 1119 $this->assertEquals($forumcontext, $result); 1120 $this->assertEquals(1, $aftercount - $startcount); 1121 1122 // Set up the default page event to use the forum. 1123 $PAGE = new \moodle_page(); 1124 $PAGE->set_context($forumcontext); 1125 $PAGE->set_cm($forumcm, $course, $forum); 1126 1127 // Now specify a context which is not a context_module. 1128 // There should be no DB queries here because we use the PAGE. 1129 $startcount = $DB->perf_get_reads(); 1130 $result = forum_get_context($forum->id, $coursecontext); 1131 $aftercount = $DB->perf_get_reads(); 1132 $this->assertEquals($forumcontext, $result); 1133 $this->assertEquals(0, $aftercount - $startcount); 1134 1135 // Now do not specify a context at all. 1136 // There should be no DB queries here because we use the PAGE. 1137 $startcount = $DB->perf_get_reads(); 1138 $result = forum_get_context($forum->id); 1139 $aftercount = $DB->perf_get_reads(); 1140 $this->assertEquals($forumcontext, $result); 1141 $this->assertEquals(0, $aftercount - $startcount); 1142 1143 // Now specify the page context of the course instead.. 1144 $PAGE = new \moodle_page(); 1145 $PAGE->set_context($coursecontext); 1146 1147 // Now specify a context which is not a context_module. 1148 // This tests will result in a DB query to fetch the course_module. 1149 $startcount = $DB->perf_get_reads(); 1150 $result = forum_get_context($forum->id, $coursecontext); 1151 $aftercount = $DB->perf_get_reads(); 1152 $this->assertEquals($forumcontext, $result); 1153 $this->assertEquals(1, $aftercount - $startcount); 1154 1155 // Now do not specify a context at all. 1156 // This tests will result in a DB query to fetch the course_module. 1157 $startcount = $DB->perf_get_reads(); 1158 $result = forum_get_context($forum->id); 1159 $aftercount = $DB->perf_get_reads(); 1160 $this->assertEquals($forumcontext, $result); 1161 $this->assertEquals(1, $aftercount - $startcount); 1162 } 1163 1164 /** 1165 * Test getting the neighbour threads of a discussion. 1166 */ 1167 public function test_forum_get_neighbours() { 1168 global $CFG, $DB; 1169 $this->resetAfterTest(); 1170 1171 $timenow = time(); 1172 $timenext = $timenow; 1173 1174 // Setup test data. 1175 $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 1176 $course = $this->getDataGenerator()->create_course(); 1177 $user = $this->getDataGenerator()->create_user(); 1178 $user2 = $this->getDataGenerator()->create_user(); 1179 1180 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1181 $cm = get_coursemodule_from_instance('forum', $forum->id); 1182 $context = \context_module::instance($cm->id); 1183 1184 $record = new \stdClass(); 1185 $record->course = $course->id; 1186 $record->userid = $user->id; 1187 $record->forum = $forum->id; 1188 $record->timemodified = time(); 1189 $disc1 = $forumgen->create_discussion($record); 1190 $record->timemodified++; 1191 $disc2 = $forumgen->create_discussion($record); 1192 $record->timemodified++; 1193 $disc3 = $forumgen->create_discussion($record); 1194 $record->timemodified++; 1195 $disc4 = $forumgen->create_discussion($record); 1196 $record->timemodified++; 1197 $disc5 = $forumgen->create_discussion($record); 1198 1199 // Getting the neighbours. 1200 $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); 1201 $this->assertEmpty($neighbours['prev']); 1202 $this->assertEquals($disc2->id, $neighbours['next']->id); 1203 1204 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1205 $this->assertEquals($disc1->id, $neighbours['prev']->id); 1206 $this->assertEquals($disc3->id, $neighbours['next']->id); 1207 1208 $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); 1209 $this->assertEquals($disc2->id, $neighbours['prev']->id); 1210 $this->assertEquals($disc4->id, $neighbours['next']->id); 1211 1212 $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum); 1213 $this->assertEquals($disc3->id, $neighbours['prev']->id); 1214 $this->assertEquals($disc5->id, $neighbours['next']->id); 1215 1216 $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); 1217 $this->assertEquals($disc4->id, $neighbours['prev']->id); 1218 $this->assertEmpty($neighbours['next']); 1219 1220 // Post in some discussions. We manually update the discussion record because 1221 // the data generator plays with timemodified in a way that would break this test. 1222 $record->timemodified++; 1223 $disc1->timemodified = $record->timemodified; 1224 $DB->update_record('forum_discussions', $disc1); 1225 1226 $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); 1227 $this->assertEquals($disc4->id, $neighbours['prev']->id); 1228 $this->assertEquals($disc1->id, $neighbours['next']->id); 1229 1230 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1231 $this->assertEmpty($neighbours['prev']); 1232 $this->assertEquals($disc3->id, $neighbours['next']->id); 1233 1234 $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); 1235 $this->assertEquals($disc5->id, $neighbours['prev']->id); 1236 $this->assertEmpty($neighbours['next']); 1237 1238 // After some discussions were created. 1239 $record->timemodified++; 1240 $disc6 = $forumgen->create_discussion($record); 1241 $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum); 1242 $this->assertEquals($disc1->id, $neighbours['prev']->id); 1243 $this->assertEmpty($neighbours['next']); 1244 1245 $record->timemodified++; 1246 $disc7 = $forumgen->create_discussion($record); 1247 $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum); 1248 $this->assertEquals($disc6->id, $neighbours['prev']->id); 1249 $this->assertEmpty($neighbours['next']); 1250 1251 // Adding timed discussions. 1252 $CFG->forum_enabletimedposts = true; 1253 $now = $record->timemodified; 1254 $past = $now - 600; 1255 $future = $now + 600; 1256 1257 $record = new \stdClass(); 1258 $record->course = $course->id; 1259 $record->userid = $user->id; 1260 $record->forum = $forum->id; 1261 $record->timestart = $past; 1262 $record->timeend = $future; 1263 $record->timemodified = $now; 1264 $record->timemodified++; 1265 $disc8 = $forumgen->create_discussion($record); 1266 $record->timemodified++; 1267 $record->timestart = $future; 1268 $record->timeend = 0; 1269 $disc9 = $forumgen->create_discussion($record); 1270 $record->timemodified++; 1271 $record->timestart = 0; 1272 $record->timeend = 0; 1273 $disc10 = $forumgen->create_discussion($record); 1274 $record->timemodified++; 1275 $record->timestart = 0; 1276 $record->timeend = $past; 1277 $disc11 = $forumgen->create_discussion($record); 1278 $record->timemodified++; 1279 $record->timestart = $past; 1280 $record->timeend = $future; 1281 $disc12 = $forumgen->create_discussion($record); 1282 $record->timemodified++; 1283 $record->timestart = $future + 1; // Should be last post for those that can see it. 1284 $record->timeend = 0; 1285 $disc13 = $forumgen->create_discussion($record); 1286 1287 // Admin user ignores the timed settings of discussions. 1288 // Post ordering taking into account timestart: 1289 // 8 = t 1290 // 10 = t+3 1291 // 11 = t+4 1292 // 12 = t+5 1293 // 9 = t+60 1294 // 13 = t+61. 1295 $this->setAdminUser(); 1296 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1297 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1298 $this->assertEquals($disc10->id, $neighbours['next']->id); 1299 1300 $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); 1301 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1302 $this->assertEquals($disc13->id, $neighbours['next']->id); 1303 1304 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1305 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1306 $this->assertEquals($disc11->id, $neighbours['next']->id); 1307 1308 $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); 1309 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1310 $this->assertEquals($disc12->id, $neighbours['next']->id); 1311 1312 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1313 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1314 $this->assertEquals($disc9->id, $neighbours['next']->id); 1315 1316 $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum); 1317 $this->assertEquals($disc9->id, $neighbours['prev']->id); 1318 $this->assertEmpty($neighbours['next']); 1319 1320 // Normal user can see their own timed discussions. 1321 $this->setUser($user); 1322 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1323 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1324 $this->assertEquals($disc10->id, $neighbours['next']->id); 1325 1326 $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); 1327 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1328 $this->assertEquals($disc13->id, $neighbours['next']->id); 1329 1330 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1331 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1332 $this->assertEquals($disc11->id, $neighbours['next']->id); 1333 1334 $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); 1335 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1336 $this->assertEquals($disc12->id, $neighbours['next']->id); 1337 1338 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1339 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1340 $this->assertEquals($disc9->id, $neighbours['next']->id); 1341 1342 $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum); 1343 $this->assertEquals($disc9->id, $neighbours['prev']->id); 1344 $this->assertEmpty($neighbours['next']); 1345 1346 // Normal user does not ignore timed settings. 1347 $this->setUser($user2); 1348 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1349 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1350 $this->assertEquals($disc10->id, $neighbours['next']->id); 1351 1352 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1353 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1354 $this->assertEquals($disc12->id, $neighbours['next']->id); 1355 1356 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1357 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1358 $this->assertEmpty($neighbours['next']); 1359 1360 // Reset to normal mode. 1361 $CFG->forum_enabletimedposts = false; 1362 $this->setAdminUser(); 1363 1364 // Two discussions with identical timemodified will sort by id. 1365 $record->timemodified += 25; 1366 $DB->update_record('forum_discussions', (object) array('id' => $disc3->id, 'timemodified' => $record->timemodified)); 1367 $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified)); 1368 $DB->update_record('forum_discussions', (object) array('id' => $disc12->id, 'timemodified' => $record->timemodified - 5)); 1369 $disc2 = $DB->get_record('forum_discussions', array('id' => $disc2->id)); 1370 $disc3 = $DB->get_record('forum_discussions', array('id' => $disc3->id)); 1371 1372 $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); 1373 $this->assertEquals($disc2->id, $neighbours['prev']->id); 1374 $this->assertEmpty($neighbours['next']); 1375 1376 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1377 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1378 $this->assertEquals($disc3->id, $neighbours['next']->id); 1379 1380 // Set timemodified to not be identical. 1381 $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified - 1)); 1382 1383 // Test pinned posts behave correctly. 1384 $disc8->pinned = FORUM_DISCUSSION_PINNED; 1385 $DB->update_record('forum_discussions', (object) array('id' => $disc8->id, 'pinned' => $disc8->pinned)); 1386 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1387 $this->assertEquals($disc3->id, $neighbours['prev']->id); 1388 $this->assertEmpty($neighbours['next']); 1389 1390 $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); 1391 $this->assertEquals($disc2->id, $neighbours['prev']->id); 1392 $this->assertEquals($disc8->id, $neighbours['next']->id); 1393 1394 // Test 3 pinned posts. 1395 $disc6->pinned = FORUM_DISCUSSION_PINNED; 1396 $DB->update_record('forum_discussions', (object) array('id' => $disc6->id, 'pinned' => $disc6->pinned)); 1397 $disc4->pinned = FORUM_DISCUSSION_PINNED; 1398 $DB->update_record('forum_discussions', (object) array('id' => $disc4->id, 'pinned' => $disc4->pinned)); 1399 1400 $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum); 1401 $this->assertEquals($disc4->id, $neighbours['prev']->id); 1402 $this->assertEquals($disc8->id, $neighbours['next']->id); 1403 1404 $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum); 1405 $this->assertEquals($disc3->id, $neighbours['prev']->id); 1406 $this->assertEquals($disc6->id, $neighbours['next']->id); 1407 1408 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1409 $this->assertEquals($disc6->id, $neighbours['prev']->id); 1410 $this->assertEmpty($neighbours['next']); 1411 } 1412 1413 /** 1414 * Test getting the neighbour threads of a blog-like forum. 1415 */ 1416 public function test_forum_get_neighbours_blog() { 1417 global $CFG, $DB; 1418 $this->resetAfterTest(); 1419 1420 $timenow = time(); 1421 $timenext = $timenow; 1422 1423 // Setup test data. 1424 $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 1425 $course = $this->getDataGenerator()->create_course(); 1426 $user = $this->getDataGenerator()->create_user(); 1427 $user2 = $this->getDataGenerator()->create_user(); 1428 1429 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog')); 1430 $cm = get_coursemodule_from_instance('forum', $forum->id); 1431 $context = \context_module::instance($cm->id); 1432 1433 $record = new \stdClass(); 1434 $record->course = $course->id; 1435 $record->userid = $user->id; 1436 $record->forum = $forum->id; 1437 $record->timemodified = time(); 1438 $disc1 = $forumgen->create_discussion($record); 1439 $record->timemodified++; 1440 $disc2 = $forumgen->create_discussion($record); 1441 $record->timemodified++; 1442 $disc3 = $forumgen->create_discussion($record); 1443 $record->timemodified++; 1444 $disc4 = $forumgen->create_discussion($record); 1445 $record->timemodified++; 1446 $disc5 = $forumgen->create_discussion($record); 1447 1448 // Getting the neighbours. 1449 $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); 1450 $this->assertEmpty($neighbours['prev']); 1451 $this->assertEquals($disc2->id, $neighbours['next']->id); 1452 1453 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1454 $this->assertEquals($disc1->id, $neighbours['prev']->id); 1455 $this->assertEquals($disc3->id, $neighbours['next']->id); 1456 1457 $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); 1458 $this->assertEquals($disc2->id, $neighbours['prev']->id); 1459 $this->assertEquals($disc4->id, $neighbours['next']->id); 1460 1461 $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum); 1462 $this->assertEquals($disc3->id, $neighbours['prev']->id); 1463 $this->assertEquals($disc5->id, $neighbours['next']->id); 1464 1465 $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); 1466 $this->assertEquals($disc4->id, $neighbours['prev']->id); 1467 $this->assertEmpty($neighbours['next']); 1468 1469 // Make sure that the thread's timemodified does not affect the order. 1470 $record->timemodified++; 1471 $disc1->timemodified = $record->timemodified; 1472 $DB->update_record('forum_discussions', $disc1); 1473 1474 $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); 1475 $this->assertEmpty($neighbours['prev']); 1476 $this->assertEquals($disc2->id, $neighbours['next']->id); 1477 1478 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1479 $this->assertEquals($disc1->id, $neighbours['prev']->id); 1480 $this->assertEquals($disc3->id, $neighbours['next']->id); 1481 1482 // Add another blog post. 1483 $record->timemodified++; 1484 $disc6 = $forumgen->create_discussion($record); 1485 $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum); 1486 $this->assertEquals($disc5->id, $neighbours['prev']->id); 1487 $this->assertEmpty($neighbours['next']); 1488 1489 $record->timemodified++; 1490 $disc7 = $forumgen->create_discussion($record); 1491 $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum); 1492 $this->assertEquals($disc6->id, $neighbours['prev']->id); 1493 $this->assertEmpty($neighbours['next']); 1494 1495 // Adding timed discussions. 1496 $CFG->forum_enabletimedposts = true; 1497 $now = $record->timemodified; 1498 $past = $now - 600; 1499 $future = $now + 600; 1500 1501 $record = new \stdClass(); 1502 $record->course = $course->id; 1503 $record->userid = $user->id; 1504 $record->forum = $forum->id; 1505 $record->timestart = $past; 1506 $record->timeend = $future; 1507 $record->timemodified = $now; 1508 $record->timemodified++; 1509 $disc8 = $forumgen->create_discussion($record); 1510 $record->timemodified++; 1511 $record->timestart = $future; 1512 $record->timeend = 0; 1513 $disc9 = $forumgen->create_discussion($record); 1514 $record->timemodified++; 1515 $record->timestart = 0; 1516 $record->timeend = 0; 1517 $disc10 = $forumgen->create_discussion($record); 1518 $record->timemodified++; 1519 $record->timestart = 0; 1520 $record->timeend = $past; 1521 $disc11 = $forumgen->create_discussion($record); 1522 $record->timemodified++; 1523 $record->timestart = $past; 1524 $record->timeend = $future; 1525 $disc12 = $forumgen->create_discussion($record); 1526 1527 // Admin user ignores the timed settings of discussions. 1528 $this->setAdminUser(); 1529 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1530 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1531 $this->assertEquals($disc9->id, $neighbours['next']->id); 1532 1533 $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); 1534 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1535 $this->assertEquals($disc10->id, $neighbours['next']->id); 1536 1537 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1538 $this->assertEquals($disc9->id, $neighbours['prev']->id); 1539 $this->assertEquals($disc11->id, $neighbours['next']->id); 1540 1541 $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); 1542 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1543 $this->assertEquals($disc12->id, $neighbours['next']->id); 1544 1545 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1546 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1547 $this->assertEmpty($neighbours['next']); 1548 1549 // Normal user can see their own timed discussions. 1550 $this->setUser($user); 1551 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1552 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1553 $this->assertEquals($disc9->id, $neighbours['next']->id); 1554 1555 $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); 1556 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1557 $this->assertEquals($disc10->id, $neighbours['next']->id); 1558 1559 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1560 $this->assertEquals($disc9->id, $neighbours['prev']->id); 1561 $this->assertEquals($disc11->id, $neighbours['next']->id); 1562 1563 $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); 1564 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1565 $this->assertEquals($disc12->id, $neighbours['next']->id); 1566 1567 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1568 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1569 $this->assertEmpty($neighbours['next']); 1570 1571 // Normal user does not ignore timed settings. 1572 $this->setUser($user2); 1573 $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); 1574 $this->assertEquals($disc7->id, $neighbours['prev']->id); 1575 $this->assertEquals($disc10->id, $neighbours['next']->id); 1576 1577 $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); 1578 $this->assertEquals($disc8->id, $neighbours['prev']->id); 1579 $this->assertEquals($disc12->id, $neighbours['next']->id); 1580 1581 $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); 1582 $this->assertEquals($disc10->id, $neighbours['prev']->id); 1583 $this->assertEmpty($neighbours['next']); 1584 1585 // Reset to normal mode. 1586 $CFG->forum_enabletimedposts = false; 1587 $this->setAdminUser(); 1588 1589 $record->timemodified++; 1590 // Two blog posts with identical creation time will sort by id. 1591 $DB->update_record('forum_posts', (object) array('id' => $disc2->firstpost, 'created' => $record->timemodified)); 1592 $DB->update_record('forum_posts', (object) array('id' => $disc3->firstpost, 'created' => $record->timemodified)); 1593 1594 $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); 1595 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1596 $this->assertEquals($disc3->id, $neighbours['next']->id); 1597 1598 $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); 1599 $this->assertEquals($disc2->id, $neighbours['prev']->id); 1600 $this->assertEmpty($neighbours['next']); 1601 } 1602 1603 /** 1604 * Test getting the neighbour threads of a discussion. 1605 */ 1606 public function test_forum_get_neighbours_with_groups() { 1607 $this->resetAfterTest(); 1608 1609 $timenow = time(); 1610 $timenext = $timenow; 1611 1612 // Setup test data. 1613 $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 1614 $course = $this->getDataGenerator()->create_course(); 1615 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 1616 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 1617 $user1 = $this->getDataGenerator()->create_user(); 1618 $user2 = $this->getDataGenerator()->create_user(); 1619 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 1620 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 1621 $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id)); 1622 1623 $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => VISIBLEGROUPS)); 1624 $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => SEPARATEGROUPS)); 1625 $cm1 = get_coursemodule_from_instance('forum', $forum1->id); 1626 $cm2 = get_coursemodule_from_instance('forum', $forum2->id); 1627 $context1 = \context_module::instance($cm1->id); 1628 $context2 = \context_module::instance($cm2->id); 1629 1630 // Creating discussions in both forums. 1631 $record = new \stdClass(); 1632 $record->course = $course->id; 1633 $record->userid = $user1->id; 1634 $record->forum = $forum1->id; 1635 $record->groupid = $group1->id; 1636 $record->timemodified = time(); 1637 $disc11 = $forumgen->create_discussion($record); 1638 $record->forum = $forum2->id; 1639 $record->timemodified++; 1640 $disc21 = $forumgen->create_discussion($record); 1641 1642 $record->timemodified++; 1643 $record->userid = $user2->id; 1644 $record->forum = $forum1->id; 1645 $record->groupid = $group2->id; 1646 $disc12 = $forumgen->create_discussion($record); 1647 $record->forum = $forum2->id; 1648 $disc22 = $forumgen->create_discussion($record); 1649 1650 $record->timemodified++; 1651 $record->userid = $user1->id; 1652 $record->forum = $forum1->id; 1653 $record->groupid = null; 1654 $disc13 = $forumgen->create_discussion($record); 1655 $record->forum = $forum2->id; 1656 $disc23 = $forumgen->create_discussion($record); 1657 1658 $record->timemodified++; 1659 $record->userid = $user2->id; 1660 $record->forum = $forum1->id; 1661 $record->groupid = $group2->id; 1662 $disc14 = $forumgen->create_discussion($record); 1663 $record->forum = $forum2->id; 1664 $disc24 = $forumgen->create_discussion($record); 1665 1666 $record->timemodified++; 1667 $record->userid = $user1->id; 1668 $record->forum = $forum1->id; 1669 $record->groupid = $group1->id; 1670 $disc15 = $forumgen->create_discussion($record); 1671 $record->forum = $forum2->id; 1672 $disc25 = $forumgen->create_discussion($record); 1673 1674 // Admin user can see all groups. 1675 $this->setAdminUser(); 1676 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1677 $this->assertEmpty($neighbours['prev']); 1678 $this->assertEquals($disc12->id, $neighbours['next']->id); 1679 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1680 $this->assertEmpty($neighbours['prev']); 1681 $this->assertEquals($disc22->id, $neighbours['next']->id); 1682 1683 $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); 1684 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1685 $this->assertEquals($disc13->id, $neighbours['next']->id); 1686 $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); 1687 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1688 $this->assertEquals($disc23->id, $neighbours['next']->id); 1689 1690 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1691 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1692 $this->assertEquals($disc14->id, $neighbours['next']->id); 1693 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1694 $this->assertEquals($disc22->id, $neighbours['prev']->id); 1695 $this->assertEquals($disc24->id, $neighbours['next']->id); 1696 1697 $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1); 1698 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1699 $this->assertEquals($disc15->id, $neighbours['next']->id); 1700 $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); 1701 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1702 $this->assertEquals($disc25->id, $neighbours['next']->id); 1703 1704 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1705 $this->assertEquals($disc14->id, $neighbours['prev']->id); 1706 $this->assertEmpty($neighbours['next']); 1707 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1708 $this->assertEquals($disc24->id, $neighbours['prev']->id); 1709 $this->assertEmpty($neighbours['next']); 1710 1711 // Admin user is only viewing group 1. 1712 $_POST['group'] = $group1->id; 1713 $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); 1714 $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); 1715 1716 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1717 $this->assertEmpty($neighbours['prev']); 1718 $this->assertEquals($disc13->id, $neighbours['next']->id); 1719 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1720 $this->assertEmpty($neighbours['prev']); 1721 $this->assertEquals($disc23->id, $neighbours['next']->id); 1722 1723 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1724 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1725 $this->assertEquals($disc15->id, $neighbours['next']->id); 1726 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1727 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1728 $this->assertEquals($disc25->id, $neighbours['next']->id); 1729 1730 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1731 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1732 $this->assertEmpty($neighbours['next']); 1733 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1734 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1735 $this->assertEmpty($neighbours['next']); 1736 1737 // Normal user viewing non-grouped posts (this is only possible in visible groups). 1738 $this->setUser($user1); 1739 $_POST['group'] = 0; 1740 $this->assertEquals(0, groups_get_activity_group($cm1, true)); 1741 1742 // They can see anything in visible groups. 1743 $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); 1744 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1745 $this->assertEquals($disc13->id, $neighbours['next']->id); 1746 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1747 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1748 $this->assertEquals($disc14->id, $neighbours['next']->id); 1749 1750 // Normal user, orphan of groups, can only see non-grouped posts in separate groups. 1751 $this->setUser($user2); 1752 $_POST['group'] = 0; 1753 $this->assertEquals(0, groups_get_activity_group($cm2, true)); 1754 1755 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1756 $this->assertEmpty($neighbours['prev']); 1757 $this->assertEmpty($neighbours['next']); 1758 1759 $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); 1760 $this->assertEmpty($neighbours['prev']); 1761 $this->assertEquals($disc23->id, $neighbours['next']->id); 1762 1763 $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); 1764 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1765 $this->assertEmpty($neighbours['next']); 1766 1767 // Switching to viewing group 1. 1768 $this->setUser($user1); 1769 $_POST['group'] = $group1->id; 1770 $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); 1771 $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); 1772 1773 // They can see non-grouped or same group. 1774 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1775 $this->assertEmpty($neighbours['prev']); 1776 $this->assertEquals($disc13->id, $neighbours['next']->id); 1777 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1778 $this->assertEmpty($neighbours['prev']); 1779 $this->assertEquals($disc23->id, $neighbours['next']->id); 1780 1781 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1782 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1783 $this->assertEquals($disc15->id, $neighbours['next']->id); 1784 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1785 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1786 $this->assertEquals($disc25->id, $neighbours['next']->id); 1787 1788 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1789 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1790 $this->assertEmpty($neighbours['next']); 1791 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1792 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1793 $this->assertEmpty($neighbours['next']); 1794 1795 // Querying the neighbours of a discussion passing the wrong CM. 1796 $this->expectException('coding_exception'); 1797 forum_get_discussion_neighbours($cm2, $disc11, $forum2); 1798 } 1799 1800 /** 1801 * Test getting the neighbour threads of a blog-like forum with groups involved. 1802 */ 1803 public function test_forum_get_neighbours_with_groups_blog() { 1804 $this->resetAfterTest(); 1805 1806 $timenow = time(); 1807 $timenext = $timenow; 1808 1809 // Setup test data. 1810 $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 1811 $course = $this->getDataGenerator()->create_course(); 1812 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 1813 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 1814 $user1 = $this->getDataGenerator()->create_user(); 1815 $user2 = $this->getDataGenerator()->create_user(); 1816 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 1817 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 1818 $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id)); 1819 1820 $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog', 1821 'groupmode' => VISIBLEGROUPS)); 1822 $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog', 1823 'groupmode' => SEPARATEGROUPS)); 1824 $cm1 = get_coursemodule_from_instance('forum', $forum1->id); 1825 $cm2 = get_coursemodule_from_instance('forum', $forum2->id); 1826 $context1 = \context_module::instance($cm1->id); 1827 $context2 = \context_module::instance($cm2->id); 1828 1829 // Creating blog posts in both forums. 1830 $record = new \stdClass(); 1831 $record->course = $course->id; 1832 $record->userid = $user1->id; 1833 $record->forum = $forum1->id; 1834 $record->groupid = $group1->id; 1835 $record->timemodified = time(); 1836 $disc11 = $forumgen->create_discussion($record); 1837 $record->timenow = $timenext++; 1838 $record->forum = $forum2->id; 1839 $record->timemodified++; 1840 $disc21 = $forumgen->create_discussion($record); 1841 1842 $record->timemodified++; 1843 $record->userid = $user2->id; 1844 $record->forum = $forum1->id; 1845 $record->groupid = $group2->id; 1846 $disc12 = $forumgen->create_discussion($record); 1847 $record->forum = $forum2->id; 1848 $disc22 = $forumgen->create_discussion($record); 1849 1850 $record->timemodified++; 1851 $record->userid = $user1->id; 1852 $record->forum = $forum1->id; 1853 $record->groupid = null; 1854 $disc13 = $forumgen->create_discussion($record); 1855 $record->forum = $forum2->id; 1856 $disc23 = $forumgen->create_discussion($record); 1857 1858 $record->timemodified++; 1859 $record->userid = $user2->id; 1860 $record->forum = $forum1->id; 1861 $record->groupid = $group2->id; 1862 $disc14 = $forumgen->create_discussion($record); 1863 $record->forum = $forum2->id; 1864 $disc24 = $forumgen->create_discussion($record); 1865 1866 $record->timemodified++; 1867 $record->userid = $user1->id; 1868 $record->forum = $forum1->id; 1869 $record->groupid = $group1->id; 1870 $disc15 = $forumgen->create_discussion($record); 1871 $record->forum = $forum2->id; 1872 $disc25 = $forumgen->create_discussion($record); 1873 1874 // Admin user can see all groups. 1875 $this->setAdminUser(); 1876 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1877 $this->assertEmpty($neighbours['prev']); 1878 $this->assertEquals($disc12->id, $neighbours['next']->id); 1879 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1880 $this->assertEmpty($neighbours['prev']); 1881 $this->assertEquals($disc22->id, $neighbours['next']->id); 1882 1883 $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); 1884 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1885 $this->assertEquals($disc13->id, $neighbours['next']->id); 1886 $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); 1887 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1888 $this->assertEquals($disc23->id, $neighbours['next']->id); 1889 1890 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1891 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1892 $this->assertEquals($disc14->id, $neighbours['next']->id); 1893 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1894 $this->assertEquals($disc22->id, $neighbours['prev']->id); 1895 $this->assertEquals($disc24->id, $neighbours['next']->id); 1896 1897 $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1); 1898 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1899 $this->assertEquals($disc15->id, $neighbours['next']->id); 1900 $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); 1901 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1902 $this->assertEquals($disc25->id, $neighbours['next']->id); 1903 1904 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1905 $this->assertEquals($disc14->id, $neighbours['prev']->id); 1906 $this->assertEmpty($neighbours['next']); 1907 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1908 $this->assertEquals($disc24->id, $neighbours['prev']->id); 1909 $this->assertEmpty($neighbours['next']); 1910 1911 // Admin user is only viewing group 1. 1912 $_POST['group'] = $group1->id; 1913 $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); 1914 $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); 1915 1916 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1917 $this->assertEmpty($neighbours['prev']); 1918 $this->assertEquals($disc13->id, $neighbours['next']->id); 1919 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1920 $this->assertEmpty($neighbours['prev']); 1921 $this->assertEquals($disc23->id, $neighbours['next']->id); 1922 1923 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1924 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1925 $this->assertEquals($disc15->id, $neighbours['next']->id); 1926 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1927 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1928 $this->assertEquals($disc25->id, $neighbours['next']->id); 1929 1930 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1931 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1932 $this->assertEmpty($neighbours['next']); 1933 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1934 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1935 $this->assertEmpty($neighbours['next']); 1936 1937 // Normal user viewing non-grouped posts (this is only possible in visible groups). 1938 $this->setUser($user1); 1939 $_POST['group'] = 0; 1940 $this->assertEquals(0, groups_get_activity_group($cm1, true)); 1941 1942 // They can see anything in visible groups. 1943 $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); 1944 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1945 $this->assertEquals($disc13->id, $neighbours['next']->id); 1946 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1947 $this->assertEquals($disc12->id, $neighbours['prev']->id); 1948 $this->assertEquals($disc14->id, $neighbours['next']->id); 1949 1950 // Normal user, orphan of groups, can only see non-grouped posts in separate groups. 1951 $this->setUser($user2); 1952 $_POST['group'] = 0; 1953 $this->assertEquals(0, groups_get_activity_group($cm2, true)); 1954 1955 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1956 $this->assertEmpty($neighbours['prev']); 1957 $this->assertEmpty($neighbours['next']); 1958 1959 $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); 1960 $this->assertEmpty($neighbours['prev']); 1961 $this->assertEquals($disc23->id, $neighbours['next']->id); 1962 1963 $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); 1964 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1965 $this->assertEmpty($neighbours['next']); 1966 1967 // Switching to viewing group 1. 1968 $this->setUser($user1); 1969 $_POST['group'] = $group1->id; 1970 $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); 1971 $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); 1972 1973 // They can see non-grouped or same group. 1974 $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); 1975 $this->assertEmpty($neighbours['prev']); 1976 $this->assertEquals($disc13->id, $neighbours['next']->id); 1977 $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); 1978 $this->assertEmpty($neighbours['prev']); 1979 $this->assertEquals($disc23->id, $neighbours['next']->id); 1980 1981 $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); 1982 $this->assertEquals($disc11->id, $neighbours['prev']->id); 1983 $this->assertEquals($disc15->id, $neighbours['next']->id); 1984 $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); 1985 $this->assertEquals($disc21->id, $neighbours['prev']->id); 1986 $this->assertEquals($disc25->id, $neighbours['next']->id); 1987 1988 $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); 1989 $this->assertEquals($disc13->id, $neighbours['prev']->id); 1990 $this->assertEmpty($neighbours['next']); 1991 $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); 1992 $this->assertEquals($disc23->id, $neighbours['prev']->id); 1993 $this->assertEmpty($neighbours['next']); 1994 1995 // Querying the neighbours of a discussion passing the wrong CM. 1996 $this->expectException('coding_exception'); 1997 forum_get_discussion_neighbours($cm2, $disc11, $forum2); 1998 } 1999 2000 public function test_count_discussion_replies_basic() { 2001 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2002 2003 // Count the discussion replies in the forum. 2004 $result = forum_count_discussion_replies($forum->id); 2005 $this->assertCount(10, $result); 2006 } 2007 2008 public function test_count_discussion_replies_limited() { 2009 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2010 // Adding limits shouldn't make a difference. 2011 $result = forum_count_discussion_replies($forum->id, "", 20); 2012 $this->assertCount(10, $result); 2013 } 2014 2015 public function test_count_discussion_replies_paginated() { 2016 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2017 // Adding paging shouldn't make any difference. 2018 $result = forum_count_discussion_replies($forum->id, "", -1, 0, 100); 2019 $this->assertCount(10, $result); 2020 } 2021 2022 public function test_count_discussion_replies_paginated_sorted() { 2023 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2024 // Specifying the forumsort should also give a good result. This follows a different path. 2025 $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 100); 2026 $this->assertCount(10, $result); 2027 foreach ($result as $row) { 2028 // Grab the first discussionid. 2029 $discussionid = array_shift($discussionids); 2030 $this->assertEquals($discussionid, $row->discussion); 2031 } 2032 } 2033 2034 public function test_count_discussion_replies_limited_sorted() { 2035 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2036 // Adding limits, and a forumsort shouldn't make a difference. 2037 $result = forum_count_discussion_replies($forum->id, "d.id asc", 20); 2038 $this->assertCount(10, $result); 2039 foreach ($result as $row) { 2040 // Grab the first discussionid. 2041 $discussionid = array_shift($discussionids); 2042 $this->assertEquals($discussionid, $row->discussion); 2043 } 2044 } 2045 2046 public function test_count_discussion_replies_paginated_sorted_small() { 2047 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2048 // Grabbing a smaller subset and they should be ordered as expected. 2049 $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 5); 2050 $this->assertCount(5, $result); 2051 foreach ($result as $row) { 2052 // Grab the first discussionid. 2053 $discussionid = array_shift($discussionids); 2054 $this->assertEquals($discussionid, $row->discussion); 2055 } 2056 } 2057 2058 public function test_count_discussion_replies_paginated_sorted_small_reverse() { 2059 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2060 // Grabbing a smaller subset and they should be ordered as expected. 2061 $result = forum_count_discussion_replies($forum->id, "d.id desc", -1, 0, 5); 2062 $this->assertCount(5, $result); 2063 foreach ($result as $row) { 2064 // Grab the last discussionid. 2065 $discussionid = array_pop($discussionids); 2066 $this->assertEquals($discussionid, $row->discussion); 2067 } 2068 } 2069 2070 public function test_count_discussion_replies_limited_sorted_small_reverse() { 2071 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2072 // Adding limits, and a forumsort shouldn't make a difference. 2073 $result = forum_count_discussion_replies($forum->id, "d.id desc", 5); 2074 $this->assertCount(5, $result); 2075 foreach ($result as $row) { 2076 // Grab the last discussionid. 2077 $discussionid = array_pop($discussionids); 2078 $this->assertEquals($discussionid, $row->discussion); 2079 } 2080 } 2081 2082 /** 2083 * Test the reply count when used with private replies. 2084 */ 2085 public function test_forum_count_discussion_replies_private() { 2086 global $DB; 2087 $this->resetAfterTest(); 2088 2089 $course = $this->getDataGenerator()->create_course(); 2090 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 2091 $context = \context_module::instance($forum->cmid); 2092 $cm = get_coursemodule_from_instance('forum', $forum->id); 2093 2094 $student = $this->getDataGenerator()->create_user(); 2095 $this->getDataGenerator()->enrol_user($student->id, $course->id); 2096 2097 $teacher = $this->getDataGenerator()->create_user(); 2098 $this->getDataGenerator()->enrol_user($teacher->id, $course->id); 2099 2100 $privilegeduser = $this->getDataGenerator()->create_user(); 2101 $this->getDataGenerator()->enrol_user($privilegeduser->id, $course->id, 'editingteacher'); 2102 2103 $otheruser = $this->getDataGenerator()->create_user(); 2104 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id); 2105 2106 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 2107 2108 // Create a discussion with some replies. 2109 $record = new \stdClass(); 2110 $record->course = $forum->course; 2111 $record->forum = $forum->id; 2112 $record->userid = $student->id; 2113 $discussion = $generator->create_discussion($record); 2114 $replycount = 5; 2115 $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2116 2117 // Create a couple of standard replies. 2118 $post = new \stdClass(); 2119 $post->userid = $student->id; 2120 $post->discussion = $discussion->id; 2121 $post->parent = $replyto->id; 2122 2123 for ($i = 0; $i < $replycount; $i++) { 2124 $post = $generator->create_post($post); 2125 } 2126 2127 // Create a private reply post from the teacher back to the student. 2128 $reply = new \stdClass(); 2129 $reply->userid = $teacher->id; 2130 $reply->discussion = $discussion->id; 2131 $reply->parent = $replyto->id; 2132 $reply->privatereplyto = $replyto->userid; 2133 $generator->create_post($reply); 2134 2135 // The user is the author of the private reply. 2136 $this->setUser($teacher->id); 2137 $counts = forum_count_discussion_replies($forum->id); 2138 $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies); 2139 2140 // The user is the intended recipient. 2141 $this->setUser($student->id); 2142 $counts = forum_count_discussion_replies($forum->id); 2143 $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies); 2144 2145 // The user is not the author or recipient, but does have the readprivatereplies capability. 2146 $this->setUser($privilegeduser->id); 2147 $counts = forum_count_discussion_replies($forum->id, "", -1, -1, 0, true); 2148 $this->assertEquals($replycount + 1, $counts[$discussion->id]->replies); 2149 2150 // The user is not allowed to view this post. 2151 $this->setUser($otheruser->id); 2152 $counts = forum_count_discussion_replies($forum->id); 2153 $this->assertEquals($replycount, $counts[$discussion->id]->replies); 2154 } 2155 2156 public function test_discussion_pinned_sort() { 2157 list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5); 2158 $cm = get_coursemodule_from_instance('forum', $forum->id); 2159 $discussions = forum_get_discussions($cm); 2160 // First discussion should be pinned. 2161 $first = reset($discussions); 2162 $this->assertEquals(1, $first->pinned, "First discussion should be pinned discussion"); 2163 } 2164 public function test_forum_view() { 2165 global $CFG; 2166 2167 $CFG->enablecompletion = 1; 2168 $this->resetAfterTest(); 2169 2170 // Setup test data. 2171 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 2172 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 2173 array('completion' => 2, 'completionview' => 1)); 2174 $context = \context_module::instance($forum->cmid); 2175 $cm = get_coursemodule_from_instance('forum', $forum->id); 2176 2177 // Trigger and capture the event. 2178 $sink = $this->redirectEvents(); 2179 2180 $this->setAdminUser(); 2181 forum_view($forum, $course, $cm, $context); 2182 2183 $events = $sink->get_events(); 2184 // 2 additional events thanks to completion. 2185 $this->assertCount(3, $events); 2186 $event = array_pop($events); 2187 2188 // Checking that the event contains the expected values. 2189 $this->assertInstanceOf('\mod_forum\event\course_module_viewed', $event); 2190 $this->assertEquals($context, $event->get_context()); 2191 $url = new \moodle_url('/mod/forum/view.php', array('f' => $forum->id)); 2192 $this->assertEquals($url, $event->get_url()); 2193 $this->assertEventContextNotUsed($event); 2194 $this->assertNotEmpty($event->get_name()); 2195 2196 // Check completion status. 2197 $completion = new \completion_info($course); 2198 $completiondata = $completion->get_data($cm); 2199 $this->assertEquals(1, $completiondata->completionstate); 2200 2201 } 2202 2203 /** 2204 * Test forum_discussion_view. 2205 */ 2206 public function test_forum_discussion_view() { 2207 global $CFG, $USER; 2208 2209 $this->resetAfterTest(); 2210 2211 // Setup test data. 2212 $course = $this->getDataGenerator()->create_course(); 2213 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 2214 $discussion = $this->create_single_discussion_with_replies($forum, $USER, 2); 2215 2216 $context = \context_module::instance($forum->cmid); 2217 $cm = get_coursemodule_from_instance('forum', $forum->id); 2218 2219 // Trigger and capture the event. 2220 $sink = $this->redirectEvents(); 2221 2222 $this->setAdminUser(); 2223 forum_discussion_view($context, $forum, $discussion); 2224 2225 $events = $sink->get_events(); 2226 $this->assertCount(1, $events); 2227 $event = array_pop($events); 2228 2229 // Checking that the event contains the expected values. 2230 $this->assertInstanceOf('\mod_forum\event\discussion_viewed', $event); 2231 $this->assertEquals($context, $event->get_context()); 2232 $this->assertEventContextNotUsed($event); 2233 2234 $this->assertNotEmpty($event->get_name()); 2235 2236 } 2237 2238 /** 2239 * Create a new course, forum, and user with a number of discussions and replies. 2240 * 2241 * @param int $discussioncount The number of discussions to create 2242 * @param int $replycount The number of replies to create in each discussion 2243 * @return array Containing the created forum object, and the ids of the created discussions. 2244 */ 2245 protected function create_multiple_discussions_with_replies($discussioncount, $replycount) { 2246 $this->resetAfterTest(); 2247 2248 // Setup the content. 2249 $user = $this->getDataGenerator()->create_user(); 2250 $course = $this->getDataGenerator()->create_course(); 2251 $record = new \stdClass(); 2252 $record->course = $course->id; 2253 $forum = $this->getDataGenerator()->create_module('forum', $record); 2254 2255 // Create 10 discussions with replies. 2256 $discussionids = array(); 2257 for ($i = 0; $i < $discussioncount; $i++) { 2258 // Pin 3rd discussion. 2259 if ($i == 3) { 2260 $discussion = $this->create_single_discussion_pinned_with_replies($forum, $user, $replycount); 2261 } else { 2262 $discussion = $this->create_single_discussion_with_replies($forum, $user, $replycount); 2263 } 2264 2265 $discussionids[] = $discussion->id; 2266 } 2267 return array($forum, $discussionids); 2268 } 2269 2270 /** 2271 * Create a discussion with a number of replies. 2272 * 2273 * @param object $forum The forum which has been created 2274 * @param object $user The user making the discussion and replies 2275 * @param int $replycount The number of replies 2276 * @return object $discussion 2277 */ 2278 protected function create_single_discussion_with_replies($forum, $user, $replycount) { 2279 global $DB; 2280 2281 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2282 2283 $record = new \stdClass(); 2284 $record->course = $forum->course; 2285 $record->forum = $forum->id; 2286 $record->userid = $user->id; 2287 $discussion = $generator->create_discussion($record); 2288 2289 // Retrieve the first post. 2290 $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2291 2292 // Create the replies. 2293 $post = new \stdClass(); 2294 $post->userid = $user->id; 2295 $post->discussion = $discussion->id; 2296 $post->parent = $replyto->id; 2297 2298 for ($i = 0; $i < $replycount; $i++) { 2299 $generator->create_post($post); 2300 } 2301 2302 return $discussion; 2303 } 2304 /** 2305 * Create a discussion with a number of replies. 2306 * 2307 * @param object $forum The forum which has been created 2308 * @param object $user The user making the discussion and replies 2309 * @param int $replycount The number of replies 2310 * @return object $discussion 2311 */ 2312 protected function create_single_discussion_pinned_with_replies($forum, $user, $replycount) { 2313 global $DB; 2314 2315 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2316 2317 $record = new \stdClass(); 2318 $record->course = $forum->course; 2319 $record->forum = $forum->id; 2320 $record->userid = $user->id; 2321 $record->pinned = FORUM_DISCUSSION_PINNED; 2322 $discussion = $generator->create_discussion($record); 2323 2324 // Retrieve the first post. 2325 $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2326 2327 // Create the replies. 2328 $post = new \stdClass(); 2329 $post->userid = $user->id; 2330 $post->discussion = $discussion->id; 2331 $post->parent = $replyto->id; 2332 2333 for ($i = 0; $i < $replycount; $i++) { 2334 $generator->create_post($post); 2335 } 2336 2337 return $discussion; 2338 } 2339 2340 /** 2341 * Tests for mod_forum_rating_can_see_item_ratings(). 2342 * 2343 * @throws coding_exception 2344 * @throws rating_exception 2345 */ 2346 public function test_mod_forum_rating_can_see_item_ratings() { 2347 global $DB; 2348 2349 $this->resetAfterTest(); 2350 2351 // Setup test data. 2352 $course = new \stdClass(); 2353 $course->groupmode = SEPARATEGROUPS; 2354 $course->groupmodeforce = true; 2355 $course = $this->getDataGenerator()->create_course($course); 2356 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 2357 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 2358 $cm = get_coursemodule_from_instance('forum', $forum->id); 2359 $context = \context_module::instance($cm->id); 2360 2361 // Create users. 2362 $user1 = $this->getDataGenerator()->create_user(); 2363 $user2 = $this->getDataGenerator()->create_user(); 2364 $user3 = $this->getDataGenerator()->create_user(); 2365 $user4 = $this->getDataGenerator()->create_user(); 2366 2367 // Groups and stuff. 2368 $role = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST); 2369 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $role->id); 2370 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 2371 $this->getDataGenerator()->enrol_user($user3->id, $course->id, $role->id); 2372 $this->getDataGenerator()->enrol_user($user4->id, $course->id, $role->id); 2373 2374 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2375 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2376 groups_add_member($group1, $user1); 2377 groups_add_member($group1, $user2); 2378 groups_add_member($group2, $user3); 2379 groups_add_member($group2, $user4); 2380 2381 $record = new \stdClass(); 2382 $record->course = $forum->course; 2383 $record->forum = $forum->id; 2384 $record->userid = $user1->id; 2385 $record->groupid = $group1->id; 2386 $discussion = $generator->create_discussion($record); 2387 2388 // Retrieve the first post. 2389 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 2390 2391 $ratingoptions = new \stdClass; 2392 $ratingoptions->context = $context; 2393 $ratingoptions->ratingarea = 'post'; 2394 $ratingoptions->component = 'mod_forum'; 2395 $ratingoptions->itemid = $post->id; 2396 $ratingoptions->scaleid = 2; 2397 $ratingoptions->userid = $user2->id; 2398 $rating = new \rating($ratingoptions); 2399 $rating->update_rating(2); 2400 2401 // Now try to access it as various users. 2402 unassign_capability('moodle/site:accessallgroups', $role->id); 2403 $params = array('contextid' => 2, 2404 'component' => 'mod_forum', 2405 'ratingarea' => 'post', 2406 'itemid' => $post->id, 2407 'scaleid' => 2); 2408 $this->setUser($user1); 2409 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2410 $this->setUser($user2); 2411 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2412 $this->setUser($user3); 2413 $this->assertFalse(mod_forum_rating_can_see_item_ratings($params)); 2414 $this->setUser($user4); 2415 $this->assertFalse(mod_forum_rating_can_see_item_ratings($params)); 2416 2417 // Now try with accessallgroups cap and make sure everything is visible. 2418 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $role->id, $context->id); 2419 $this->setUser($user1); 2420 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2421 $this->setUser($user2); 2422 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2423 $this->setUser($user3); 2424 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2425 $this->setUser($user4); 2426 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2427 2428 // Change group mode and verify visibility. 2429 $course->groupmode = VISIBLEGROUPS; 2430 $DB->update_record('course', $course); 2431 unassign_capability('moodle/site:accessallgroups', $role->id); 2432 $this->setUser($user1); 2433 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2434 $this->setUser($user2); 2435 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2436 $this->setUser($user3); 2437 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2438 $this->setUser($user4); 2439 $this->assertTrue(mod_forum_rating_can_see_item_ratings($params)); 2440 2441 } 2442 2443 /** 2444 * Test forum_get_discussions 2445 */ 2446 public function test_forum_get_discussions_with_groups() { 2447 global $DB; 2448 2449 $this->resetAfterTest(true); 2450 2451 // Create course to add the module. 2452 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0)); 2453 $user1 = self::getDataGenerator()->create_user(); 2454 $user2 = self::getDataGenerator()->create_user(); 2455 $user3 = self::getDataGenerator()->create_user(); 2456 2457 $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2458 self::getDataGenerator()->enrol_user($user1->id, $course->id, $role->id); 2459 self::getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 2460 self::getDataGenerator()->enrol_user($user3->id, $course->id, $role->id); 2461 2462 // Forum forcing separate gropus. 2463 $record = new \stdClass(); 2464 $record->course = $course->id; 2465 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); 2466 $cm = get_coursemodule_from_instance('forum', $forum->id); 2467 2468 // Create groups. 2469 $group1 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group1')); 2470 $group2 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group2')); 2471 $group3 = self::getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'group3')); 2472 2473 // Add the user1 to g1 and g2 groups. 2474 groups_add_member($group1->id, $user1->id); 2475 groups_add_member($group2->id, $user1->id); 2476 2477 // Add the user 2 and 3 to only one group. 2478 groups_add_member($group1->id, $user2->id); 2479 groups_add_member($group3->id, $user3->id); 2480 2481 // Add a few discussions. 2482 $record = array(); 2483 $record['course'] = $course->id; 2484 $record['forum'] = $forum->id; 2485 $record['userid'] = $user1->id; 2486 $record['groupid'] = $group1->id; 2487 $discussiong1u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2488 2489 $record['groupid'] = $group2->id; 2490 $discussiong2u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2491 2492 $record['userid'] = $user2->id; 2493 $record['groupid'] = $group1->id; 2494 $discussiong1u2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2495 2496 $record['userid'] = $user3->id; 2497 $record['groupid'] = $group3->id; 2498 $discussiong3u3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2499 2500 self::setUser($user1); 2501 2502 // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions. 2503 $discussions = forum_get_discussions($cm); 2504 self::assertCount(2, $discussions); 2505 foreach ($discussions as $discussion) { 2506 self::assertEquals($group1->id, $discussion->groupid); 2507 } 2508 2509 // Get all my discussions. 2510 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0); 2511 self::assertCount(3, $discussions); 2512 2513 // Get all my g1 discussions. 2514 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group1->id); 2515 self::assertCount(2, $discussions); 2516 foreach ($discussions as $discussion) { 2517 self::assertEquals($group1->id, $discussion->groupid); 2518 } 2519 2520 // Get all my g2 discussions. 2521 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id); 2522 self::assertCount(1, $discussions); 2523 $discussion = array_shift($discussions); 2524 self::assertEquals($group2->id, $discussion->groupid); 2525 self::assertEquals($user1->id, $discussion->userid); 2526 self::assertEquals($discussiong2u1->id, $discussion->discussion); 2527 2528 // Get all my g3 discussions (I'm not enrolled in that group). 2529 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id); 2530 self::assertCount(0, $discussions); 2531 2532 // This group does not exist. 2533 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id + 1000); 2534 self::assertCount(0, $discussions); 2535 2536 self::setUser($user2); 2537 2538 // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions. 2539 $discussions = forum_get_discussions($cm); 2540 self::assertCount(2, $discussions); 2541 foreach ($discussions as $discussion) { 2542 self::assertEquals($group1->id, $discussion->groupid); 2543 } 2544 2545 // Get all my viewable discussions. 2546 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0); 2547 self::assertCount(2, $discussions); 2548 foreach ($discussions as $discussion) { 2549 self::assertEquals($group1->id, $discussion->groupid); 2550 } 2551 2552 // Get all my g2 discussions (I'm not enrolled in that group). 2553 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id); 2554 self::assertCount(0, $discussions); 2555 2556 // Get all my g3 discussions (I'm not enrolled in that group). 2557 $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id); 2558 self::assertCount(0, $discussions); 2559 2560 } 2561 2562 /** 2563 * Test forum_user_can_post_discussion 2564 */ 2565 public function test_forum_user_can_post_discussion() { 2566 global $DB; 2567 2568 $this->resetAfterTest(true); 2569 2570 // Create course to add the module. 2571 $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1)); 2572 $user = self::getDataGenerator()->create_user(); 2573 $this->getDataGenerator()->enrol_user($user->id, $course->id); 2574 2575 // Forum forcing separate gropus. 2576 $record = new \stdClass(); 2577 $record->course = $course->id; 2578 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); 2579 $cm = get_coursemodule_from_instance('forum', $forum->id); 2580 $context = \context_module::instance($cm->id); 2581 2582 self::setUser($user); 2583 2584 // The user is not enroled in any group, try to post in a forum with separate groups. 2585 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2586 $this->assertFalse($can); 2587 2588 // Create a group. 2589 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2590 2591 // Try to post in a group the user is not enrolled. 2592 $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context); 2593 $this->assertFalse($can); 2594 2595 // Add the user to a group. 2596 groups_add_member($group->id, $user->id); 2597 2598 // Try to post in a group the user is not enrolled. 2599 $can = forum_user_can_post_discussion($forum, $group->id + 1, -1, $cm, $context); 2600 $this->assertFalse($can); 2601 2602 // Now try to post in the user group. (null means it will guess the group). 2603 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2604 $this->assertTrue($can); 2605 2606 $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context); 2607 $this->assertTrue($can); 2608 2609 // Test all groups. 2610 $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context); 2611 $this->assertFalse($can); 2612 2613 $this->setAdminUser(); 2614 $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context); 2615 $this->assertTrue($can); 2616 2617 // Change forum type. 2618 $forum->type = 'news'; 2619 $DB->update_record('forum', $forum); 2620 2621 // Admin can post news. 2622 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2623 $this->assertTrue($can); 2624 2625 // Normal users don't. 2626 self::setUser($user); 2627 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2628 $this->assertFalse($can); 2629 2630 // Change forum type. 2631 $forum->type = 'eachuser'; 2632 $DB->update_record('forum', $forum); 2633 2634 // I didn't post yet, so I should be able to post. 2635 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2636 $this->assertTrue($can); 2637 2638 // Post now. 2639 $record = new \stdClass(); 2640 $record->course = $course->id; 2641 $record->userid = $user->id; 2642 $record->forum = $forum->id; 2643 $record->groupid = $group->id; 2644 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2645 2646 // I already posted, I shouldn't be able to post. 2647 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2648 $this->assertFalse($can); 2649 2650 // Last check with no groups, normal forum and course. 2651 $course->groupmode = NOGROUPS; 2652 $course->groupmodeforce = 0; 2653 $DB->update_record('course', $course); 2654 2655 $forum->type = 'general'; 2656 $forum->groupmode = NOGROUPS; 2657 $DB->update_record('forum', $forum); 2658 2659 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2660 $this->assertTrue($can); 2661 } 2662 2663 /** 2664 * Test forum_user_can_post_discussion_after_cutoff 2665 */ 2666 public function test_forum_user_can_post_discussion_after_cutoff() { 2667 $this->resetAfterTest(true); 2668 2669 // Create course to add the module. 2670 $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1)); 2671 $student = self::getDataGenerator()->create_user(); 2672 $teacher = self::getDataGenerator()->create_user(); 2673 $this->getDataGenerator()->enrol_user($student->id, $course->id); 2674 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher'); 2675 2676 // Forum forcing separate gropus. 2677 $record = new \stdClass(); 2678 $record->course = $course->id; 2679 $record->cutoffdate = time() - 1; 2680 $forum = self::getDataGenerator()->create_module('forum', $record); 2681 $cm = get_coursemodule_from_instance('forum', $forum->id); 2682 $context = \context_module::instance($cm->id); 2683 2684 self::setUser($student); 2685 2686 // Students usually don't have the mod/forum:canoverridecutoff capability. 2687 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2688 $this->assertFalse($can); 2689 2690 self::setUser($teacher); 2691 2692 // Teachers usually have the mod/forum:canoverridecutoff capability. 2693 $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context); 2694 $this->assertTrue($can); 2695 } 2696 2697 /** 2698 * Test forum_user_has_posted_discussion with no groups. 2699 */ 2700 public function test_forum_user_has_posted_discussion_no_groups() { 2701 global $CFG; 2702 2703 $this->resetAfterTest(true); 2704 2705 $course = self::getDataGenerator()->create_course(); 2706 $author = self::getDataGenerator()->create_user(); 2707 $other = self::getDataGenerator()->create_user(); 2708 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2709 $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2710 2711 self::setUser($author); 2712 2713 // Neither user has posted. 2714 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id)); 2715 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id)); 2716 2717 // Post in the forum. 2718 $record = new \stdClass(); 2719 $record->course = $course->id; 2720 $record->userid = $author->id; 2721 $record->forum = $forum->id; 2722 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2723 2724 // The author has now posted, but the other user has not. 2725 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2726 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id)); 2727 } 2728 2729 /** 2730 * Test forum_user_has_posted_discussion with multiple forums 2731 */ 2732 public function test_forum_user_has_posted_discussion_multiple_forums() { 2733 global $CFG; 2734 2735 $this->resetAfterTest(true); 2736 2737 $course = self::getDataGenerator()->create_course(); 2738 $author = self::getDataGenerator()->create_user(); 2739 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2740 $forum1 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2741 $forum2 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]); 2742 2743 self::setUser($author); 2744 2745 // No post in either forum. 2746 $this->assertFalse(forum_user_has_posted_discussion($forum1->id, $author->id)); 2747 $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id)); 2748 2749 // Post in the forum. 2750 $record = new \stdClass(); 2751 $record->course = $course->id; 2752 $record->userid = $author->id; 2753 $record->forum = $forum1->id; 2754 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2755 2756 // The author has now posted in forum1, but not forum2. 2757 $this->assertTrue(forum_user_has_posted_discussion($forum1->id, $author->id)); 2758 $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id)); 2759 } 2760 2761 /** 2762 * Test forum_user_has_posted_discussion with multiple groups. 2763 */ 2764 public function test_forum_user_has_posted_discussion_multiple_groups() { 2765 global $CFG; 2766 2767 $this->resetAfterTest(true); 2768 2769 $course = self::getDataGenerator()->create_course(); 2770 $author = self::getDataGenerator()->create_user(); 2771 $this->getDataGenerator()->enrol_user($author->id, $course->id); 2772 2773 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2774 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2775 groups_add_member($group1->id, $author->id); 2776 groups_add_member($group2->id, $author->id); 2777 2778 $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ], [ 2779 'groupmode' => SEPARATEGROUPS, 2780 ]); 2781 2782 self::setUser($author); 2783 2784 // The user has not posted in either group. 2785 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id)); 2786 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2787 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2788 2789 // Post in one group. 2790 $record = new \stdClass(); 2791 $record->course = $course->id; 2792 $record->userid = $author->id; 2793 $record->forum = $forum->id; 2794 $record->groupid = $group1->id; 2795 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2796 2797 // The author has now posted in one group, but the other user has not. 2798 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2799 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2800 $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2801 2802 // Post in the other group. 2803 $record = new \stdClass(); 2804 $record->course = $course->id; 2805 $record->userid = $author->id; 2806 $record->forum = $forum->id; 2807 $record->groupid = $group2->id; 2808 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 2809 2810 // The author has now posted in one group, but the other user has not. 2811 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id)); 2812 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id)); 2813 $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); 2814 } 2815 2816 /** 2817 * Test the logic for forum_get_user_posted_mailnow where the user can select if qanda forum post should be sent without delay 2818 * 2819 * @covers ::forum_get_user_posted_mailnow 2820 */ 2821 public function test_forum_get_user_posted_mailnow() { 2822 $this->resetAfterTest(); 2823 2824 // Create a forum. 2825 $course = $this->getDataGenerator()->create_course(); 2826 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 2827 $author = $this->getDataGenerator()->create_user(); 2828 $authorid = $author->id; 2829 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 2830 2831 // Create a discussion. 2832 $record = new \stdClass(); 2833 $record->course = $forum->course; 2834 $record->forum = $forum->id; 2835 $record->userid = $authorid; 2836 $discussion = $generator->create_discussion($record); 2837 $did = $discussion->id; 2838 2839 // Return False if no post exists with 'mailnow' selected. 2840 $generator->create_post(['userid' => $authorid, 'discussion' => $did, 'forum' => $forum->id, 'mailnow' => 0]); 2841 $result = forum_get_user_posted_mailnow($did, $authorid); 2842 $this->assertFalse($result); 2843 2844 // Return True only if any post has 'mailnow' selected. 2845 $generator->create_post(['userid' => $authorid, 'discussion' => $did, 'forum' => $forum->id, 'mailnow' => 1]); 2846 $result = forum_get_user_posted_mailnow($did, $authorid); 2847 $this->assertTrue($result); 2848 } 2849 2850 /** 2851 * Tests the mod_forum_myprofile_navigation() function. 2852 */ 2853 public function test_mod_forum_myprofile_navigation() { 2854 $this->resetAfterTest(true); 2855 2856 // Set up the test. 2857 $tree = new \core_user\output\myprofile\tree(); 2858 $user = $this->getDataGenerator()->create_user(); 2859 $course = $this->getDataGenerator()->create_course(); 2860 $iscurrentuser = true; 2861 2862 // Set as the current user. 2863 $this->setUser($user); 2864 2865 // Check the node tree is correct. 2866 mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course); 2867 $reflector = new \ReflectionObject($tree); 2868 $nodes = $reflector->getProperty('nodes'); 2869 $nodes->setAccessible(true); 2870 $this->assertArrayHasKey('forumposts', $nodes->getValue($tree)); 2871 $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree)); 2872 } 2873 2874 /** 2875 * Tests the mod_forum_myprofile_navigation() function as a guest. 2876 */ 2877 public function test_mod_forum_myprofile_navigation_as_guest() { 2878 global $USER; 2879 2880 $this->resetAfterTest(true); 2881 2882 // Set up the test. 2883 $tree = new \core_user\output\myprofile\tree(); 2884 $course = $this->getDataGenerator()->create_course(); 2885 $iscurrentuser = true; 2886 2887 // Set user as guest. 2888 $this->setGuestUser(); 2889 2890 // Check the node tree is correct. 2891 mod_forum_myprofile_navigation($tree, $USER, $iscurrentuser, $course); 2892 $reflector = new \ReflectionObject($tree); 2893 $nodes = $reflector->getProperty('nodes'); 2894 $nodes->setAccessible(true); 2895 $this->assertArrayNotHasKey('forumposts', $nodes->getValue($tree)); 2896 $this->assertArrayNotHasKey('forumdiscussions', $nodes->getValue($tree)); 2897 } 2898 2899 /** 2900 * Tests the mod_forum_myprofile_navigation() function as a user viewing another user's profile. 2901 */ 2902 public function test_mod_forum_myprofile_navigation_different_user() { 2903 $this->resetAfterTest(true); 2904 2905 // Set up the test. 2906 $tree = new \core_user\output\myprofile\tree(); 2907 $user = $this->getDataGenerator()->create_user(); 2908 $user2 = $this->getDataGenerator()->create_user(); 2909 $course = $this->getDataGenerator()->create_course(); 2910 $iscurrentuser = true; 2911 2912 // Set to different user's profile. 2913 $this->setUser($user2); 2914 2915 // Check the node tree is correct. 2916 mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course); 2917 $reflector = new \ReflectionObject($tree); 2918 $nodes = $reflector->getProperty('nodes'); 2919 $nodes->setAccessible(true); 2920 $this->assertArrayHasKey('forumposts', $nodes->getValue($tree)); 2921 $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree)); 2922 } 2923 2924 /** 2925 * Test test_pinned_discussion_with_group. 2926 */ 2927 public function test_pinned_discussion_with_group() { 2928 global $SESSION; 2929 2930 $this->resetAfterTest(); 2931 $course1 = $this->getDataGenerator()->create_course(); 2932 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); 2933 2934 // Create an author user. 2935 $author = $this->getDataGenerator()->create_user(); 2936 $this->getDataGenerator()->enrol_user($author->id, $course1->id); 2937 2938 // Create two viewer users - one in a group, one not. 2939 $viewer1 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1)); 2940 $this->getDataGenerator()->enrol_user($viewer1->id, $course1->id); 2941 2942 $viewer2 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1)); 2943 $this->getDataGenerator()->enrol_user($viewer2->id, $course1->id); 2944 $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group1->id)); 2945 2946 $forum1 = $this->getDataGenerator()->create_module('forum', (object) array( 2947 'course' => $course1->id, 2948 'groupmode' => SEPARATEGROUPS, 2949 )); 2950 2951 $coursemodule = get_coursemodule_from_instance('forum', $forum1->id); 2952 2953 $alldiscussions = array(); 2954 $group1discussions = array(); 2955 2956 // Create 4 discussions in all participants group and group1, where the first 2957 // discussion is pinned in each group. 2958 $allrecord = new \stdClass(); 2959 $allrecord->course = $course1->id; 2960 $allrecord->userid = $author->id; 2961 $allrecord->forum = $forum1->id; 2962 $allrecord->pinned = FORUM_DISCUSSION_PINNED; 2963 2964 $group1record = new \stdClass(); 2965 $group1record->course = $course1->id; 2966 $group1record->userid = $author->id; 2967 $group1record->forum = $forum1->id; 2968 $group1record->groupid = $group1->id; 2969 $group1record->pinned = FORUM_DISCUSSION_PINNED; 2970 2971 $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord); 2972 $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record); 2973 2974 // Create unpinned discussions. 2975 $allrecord->pinned = FORUM_DISCUSSION_UNPINNED; 2976 $group1record->pinned = FORUM_DISCUSSION_UNPINNED; 2977 for ($i = 0; $i < 3; $i++) { 2978 $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord); 2979 $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record); 2980 } 2981 2982 // As viewer1 (no group). This user shouldn't see any of group1's discussions 2983 // so their expected discussion order is (where rightmost is highest priority): 2984 // Ad1, ad2, ad3, ad0. 2985 $this->setUser($viewer1->id); 2986 2987 // CHECK 1. 2988 // Take the neighbours of ad3, which should be prev: ad2 and next: ad0. 2989 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1); 2990 // Ad2 check. 2991 $this->assertEquals($alldiscussions[2]->id, $neighbours['prev']->id); 2992 // Ad0 check. 2993 $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id); 2994 2995 // CHECK 2. 2996 // Take the neighbours of ad0, which should be prev: ad3 and next: null. 2997 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[0], $forum1); 2998 // Ad3 check. 2999 $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id); 3000 // Null check. 3001 $this->assertEmpty($neighbours['next']); 3002 3003 // CHECK 3. 3004 // Take the neighbours of ad1, which should be prev: null and next: ad2. 3005 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1); 3006 // Null check. 3007 $this->assertEmpty($neighbours['prev']); 3008 // Ad2 check. 3009 $this->assertEquals($alldiscussions[2]->id, $neighbours['next']->id); 3010 3011 // Temporary hack to workaround for MDL-52656. 3012 $SESSION->currentgroup = null; 3013 3014 // As viewer2 (group1). This user should see all of group1's posts and the all participants group. 3015 // The expected discussion order is (rightmost is highest priority): 3016 // Ad1, gd1, ad2, gd2, ad3, gd3, ad0, gd0. 3017 $this->setUser($viewer2->id); 3018 3019 // CHECK 1. 3020 // Take the neighbours of ad1, which should be prev: null and next: gd1. 3021 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1); 3022 // Null check. 3023 $this->assertEmpty($neighbours['prev']); 3024 // Gd1 check. 3025 $this->assertEquals($group1discussions[1]->id, $neighbours['next']->id); 3026 3027 // CHECK 2. 3028 // Take the neighbours of ad3, which should be prev: gd2 and next: gd3. 3029 $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1); 3030 // Gd2 check. 3031 $this->assertEquals($group1discussions[2]->id, $neighbours['prev']->id); 3032 // Gd3 check. 3033 $this->assertEquals($group1discussions[3]->id, $neighbours['next']->id); 3034 3035 // CHECK 3. 3036 // Take the neighbours of gd3, which should be prev: ad3 and next: ad0. 3037 $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[3], $forum1); 3038 // Ad3 check. 3039 $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id); 3040 // Ad0 check. 3041 $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id); 3042 3043 // CHECK 4. 3044 // Take the neighbours of gd0, which should be prev: ad0 and next: null. 3045 $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[0], $forum1); 3046 // Ad0 check. 3047 $this->assertEquals($alldiscussions[0]->id, $neighbours['prev']->id); 3048 // Null check. 3049 $this->assertEmpty($neighbours['next']); 3050 } 3051 3052 /** 3053 * Test test_pinned_with_timed_discussions. 3054 */ 3055 public function test_pinned_with_timed_discussions() { 3056 global $CFG; 3057 3058 $CFG->forum_enabletimedposts = true; 3059 3060 $this->resetAfterTest(); 3061 $course = $this->getDataGenerator()->create_course(); 3062 3063 // Create an user. 3064 $user = $this->getDataGenerator()->create_user(); 3065 $this->getDataGenerator()->enrol_user($user->id, $course->id); 3066 3067 // Create a forum. 3068 $record = new \stdClass(); 3069 $record->course = $course->id; 3070 $forum = $this->getDataGenerator()->create_module('forum', (object) array( 3071 'course' => $course->id, 3072 'groupmode' => SEPARATEGROUPS, 3073 )); 3074 3075 $coursemodule = get_coursemodule_from_instance('forum', $forum->id); 3076 $now = time(); 3077 $discussions = array(); 3078 $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3079 3080 $record = new \stdClass(); 3081 $record->course = $course->id; 3082 $record->userid = $user->id; 3083 $record->forum = $forum->id; 3084 $record->pinned = FORUM_DISCUSSION_PINNED; 3085 $record->timemodified = $now; 3086 3087 $discussions[] = $discussiongenerator->create_discussion($record); 3088 3089 $record->pinned = FORUM_DISCUSSION_UNPINNED; 3090 $record->timestart = $now + 10; 3091 3092 $discussions[] = $discussiongenerator->create_discussion($record); 3093 3094 $record->timestart = $now; 3095 3096 $discussions[] = $discussiongenerator->create_discussion($record); 3097 3098 // Expected order of discussions: 3099 // D2, d1, d0. 3100 $this->setUser($user->id); 3101 3102 // CHECK 1. 3103 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum); 3104 // Null check. 3105 $this->assertEmpty($neighbours['prev']); 3106 // D1 check. 3107 $this->assertEquals($discussions[1]->id, $neighbours['next']->id); 3108 3109 // CHECK 2. 3110 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum); 3111 // D2 check. 3112 $this->assertEquals($discussions[2]->id, $neighbours['prev']->id); 3113 // D0 check. 3114 $this->assertEquals($discussions[0]->id, $neighbours['next']->id); 3115 3116 // CHECK 3. 3117 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum); 3118 // D2 check. 3119 $this->assertEquals($discussions[1]->id, $neighbours['prev']->id); 3120 // Null check. 3121 $this->assertEmpty($neighbours['next']); 3122 } 3123 3124 /** 3125 * Test test_pinned_timed_discussions_with_timed_discussions. 3126 */ 3127 public function test_pinned_timed_discussions_with_timed_discussions() { 3128 global $CFG; 3129 3130 $CFG->forum_enabletimedposts = true; 3131 3132 $this->resetAfterTest(); 3133 $course = $this->getDataGenerator()->create_course(); 3134 3135 // Create an user. 3136 $user = $this->getDataGenerator()->create_user(); 3137 $this->getDataGenerator()->enrol_user($user->id, $course->id); 3138 3139 // Create a forum. 3140 $record = new \stdClass(); 3141 $record->course = $course->id; 3142 $forum = $this->getDataGenerator()->create_module('forum', (object) array( 3143 'course' => $course->id, 3144 'groupmode' => SEPARATEGROUPS, 3145 )); 3146 3147 $coursemodule = get_coursemodule_from_instance('forum', $forum->id); 3148 $now = time(); 3149 $discussions = array(); 3150 $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3151 3152 $record = new \stdClass(); 3153 $record->course = $course->id; 3154 $record->userid = $user->id; 3155 $record->forum = $forum->id; 3156 $record->pinned = FORUM_DISCUSSION_PINNED; 3157 $record->timemodified = $now; 3158 $record->timestart = $now + 10; 3159 3160 $discussions[] = $discussiongenerator->create_discussion($record); 3161 3162 $record->pinned = FORUM_DISCUSSION_UNPINNED; 3163 3164 $discussions[] = $discussiongenerator->create_discussion($record); 3165 3166 $record->timestart = $now; 3167 3168 $discussions[] = $discussiongenerator->create_discussion($record); 3169 3170 $record->pinned = FORUM_DISCUSSION_PINNED; 3171 3172 $discussions[] = $discussiongenerator->create_discussion($record); 3173 3174 // Expected order of discussions: 3175 // D2, d1, d3, d0. 3176 $this->setUser($user->id); 3177 3178 // CHECK 1. 3179 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum); 3180 // Null check. 3181 $this->assertEmpty($neighbours['prev']); 3182 // D1 check. 3183 $this->assertEquals($discussions[1]->id, $neighbours['next']->id); 3184 3185 // CHECK 2. 3186 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum); 3187 // D2 check. 3188 $this->assertEquals($discussions[2]->id, $neighbours['prev']->id); 3189 // D3 check. 3190 $this->assertEquals($discussions[3]->id, $neighbours['next']->id); 3191 3192 // CHECK 3. 3193 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[3], $forum); 3194 // D1 check. 3195 $this->assertEquals($discussions[1]->id, $neighbours['prev']->id); 3196 // D0 check. 3197 $this->assertEquals($discussions[0]->id, $neighbours['next']->id); 3198 3199 // CHECK 4. 3200 $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum); 3201 // D3 check. 3202 $this->assertEquals($discussions[3]->id, $neighbours['prev']->id); 3203 // Null check. 3204 $this->assertEmpty($neighbours['next']); 3205 } 3206 3207 /** 3208 * Test for forum_is_author_hidden. 3209 */ 3210 public function test_forum_is_author_hidden() { 3211 // First post, different forum type. 3212 $post = (object) ['parent' => 0]; 3213 $forum = (object) ['type' => 'standard']; 3214 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3215 3216 // Child post, different forum type. 3217 $post->parent = 1; 3218 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3219 3220 // First post, single simple discussion forum type. 3221 $post->parent = 0; 3222 $forum->type = 'single'; 3223 $this->assertTrue(forum_is_author_hidden($post, $forum)); 3224 3225 // Child post, single simple discussion forum type. 3226 $post->parent = 1; 3227 $this->assertFalse(forum_is_author_hidden($post, $forum)); 3228 3229 // Incorrect parameters: $post. 3230 $this->expectException('coding_exception'); 3231 $this->expectExceptionMessage('$post->parent must be set.'); 3232 unset($post->parent); 3233 forum_is_author_hidden($post, $forum); 3234 3235 // Incorrect parameters: $forum. 3236 $this->expectException('coding_exception'); 3237 $this->expectExceptionMessage('$forum->type must be set.'); 3238 unset($forum->type); 3239 forum_is_author_hidden($post, $forum); 3240 } 3241 3242 /** 3243 * Test the forum_discussion_is_locked function. 3244 * 3245 * @dataProvider forum_discussion_is_locked_provider 3246 * @param \stdClass $forum 3247 * @param \stdClass $discussion 3248 * @param bool $expect 3249 */ 3250 public function test_forum_discussion_is_locked($forum, $discussion, $expect) { 3251 $this->resetAfterTest(); 3252 3253 $datagenerator = $this->getDataGenerator(); 3254 $plugingenerator = $datagenerator->get_plugin_generator('mod_forum'); 3255 3256 $course = $datagenerator->create_course(); 3257 $user = $datagenerator->create_user(); 3258 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3259 'course' => $course->id 3260 ], $forum)); 3261 $discussion = $plugingenerator->create_discussion((object) array_merge([ 3262 'course' => $course->id, 3263 'userid' => $user->id, 3264 'forum' => $forum->id, 3265 ], $discussion)); 3266 3267 $this->assertEquals($expect, forum_discussion_is_locked($forum, $discussion)); 3268 } 3269 3270 /** 3271 * Dataprovider for forum_discussion_is_locked tests. 3272 * 3273 * @return array 3274 */ 3275 public function forum_discussion_is_locked_provider() { 3276 return [ 3277 'Unlocked: lockdiscussionafter is false' => [ 3278 ['lockdiscussionafter' => false], 3279 [], 3280 false 3281 ], 3282 'Unlocked: lockdiscussionafter is set; forum is of type single; post is recent' => [ 3283 ['lockdiscussionafter' => DAYSECS, 'type' => 'single'], 3284 ['timemodified' => time()], 3285 false 3286 ], 3287 'Unlocked: lockdiscussionafter is set; forum is of type single; post is old' => [ 3288 ['lockdiscussionafter' => MINSECS, 'type' => 'single'], 3289 ['timemodified' => time() - DAYSECS], 3290 false 3291 ], 3292 'Unlocked: lockdiscussionafter is set; forum is of type eachuser; post is recent' => [ 3293 ['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'], 3294 ['timemodified' => time()], 3295 false 3296 ], 3297 'Locked: lockdiscussionafter is set; forum is of type eachuser; post is old' => [ 3298 ['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'], 3299 ['timemodified' => time() - DAYSECS], 3300 true 3301 ], 3302 ]; 3303 } 3304 3305 /** 3306 * Test the forum_is_cutoff_date_reached function. 3307 * 3308 * @dataProvider forum_is_cutoff_date_reached_provider 3309 * @param array $forum 3310 * @param bool $expect 3311 */ 3312 public function test_forum_is_cutoff_date_reached($forum, $expect) { 3313 $this->resetAfterTest(); 3314 3315 $datagenerator = $this->getDataGenerator(); 3316 $course = $datagenerator->create_course(); 3317 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3318 'course' => $course->id 3319 ], $forum)); 3320 3321 $this->assertEquals($expect, forum_is_cutoff_date_reached($forum)); 3322 } 3323 3324 /** 3325 * Dataprovider for forum_is_cutoff_date_reached tests. 3326 * 3327 * @return array 3328 */ 3329 public function forum_is_cutoff_date_reached_provider() { 3330 $now = time(); 3331 return [ 3332 'cutoffdate is unset' => [ 3333 [], 3334 false 3335 ], 3336 'cutoffdate is 0' => [ 3337 ['cutoffdate' => 0], 3338 false 3339 ], 3340 'cutoffdate is set and is in future' => [ 3341 ['cutoffdate' => $now + 86400], 3342 false 3343 ], 3344 'cutoffdate is set and is in past' => [ 3345 ['cutoffdate' => $now - 86400], 3346 true 3347 ], 3348 ]; 3349 } 3350 3351 /** 3352 * Test the forum_is_due_date_reached function. 3353 * 3354 * @dataProvider forum_is_due_date_reached_provider 3355 * @param \stdClass $forum 3356 * @param bool $expect 3357 */ 3358 public function test_forum_is_due_date_reached($forum, $expect) { 3359 $this->resetAfterTest(); 3360 3361 $this->setAdminUser(); 3362 3363 $datagenerator = $this->getDataGenerator(); 3364 $course = $datagenerator->create_course(); 3365 $forum = $datagenerator->create_module('forum', (object) array_merge([ 3366 'course' => $course->id 3367 ], $forum)); 3368 3369 $this->assertEquals($expect, forum_is_due_date_reached($forum)); 3370 } 3371 3372 /** 3373 * Dataprovider for forum_is_due_date_reached tests. 3374 * 3375 * @return array 3376 */ 3377 public function forum_is_due_date_reached_provider() { 3378 $now = time(); 3379 return [ 3380 'duedate is unset' => [ 3381 [], 3382 false 3383 ], 3384 'duedate is 0' => [ 3385 ['duedate' => 0], 3386 false 3387 ], 3388 'duedate is set and is in future' => [ 3389 ['duedate' => $now + 86400], 3390 false 3391 ], 3392 'duedate is set and is in past' => [ 3393 ['duedate' => $now - 86400], 3394 true 3395 ], 3396 ]; 3397 } 3398 3399 /** 3400 * Test that {@link forum_update_post()} keeps correct forum_discussions usermodified. 3401 */ 3402 public function test_forum_update_post_keeps_discussions_usermodified() { 3403 global $DB; 3404 3405 $this->resetAfterTest(); 3406 3407 // Let there be light. 3408 $teacher = self::getDataGenerator()->create_user(); 3409 $student = self::getDataGenerator()->create_user(); 3410 $course = self::getDataGenerator()->create_course(); 3411 3412 $forum = self::getDataGenerator()->create_module('forum', (object)[ 3413 'course' => $course->id, 3414 ]); 3415 3416 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum'); 3417 3418 // Let the teacher start a discussion. 3419 $discussion = $generator->create_discussion((object)[ 3420 'course' => $course->id, 3421 'userid' => $teacher->id, 3422 'forum' => $forum->id, 3423 ]); 3424 3425 // On this freshly created discussion, the teacher is the author of the last post. 3426 $this->assertEquals($teacher->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3427 3428 // Fetch modified timestamp of the discussion. 3429 $discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]); 3430 $pasttime = $discussionmodified - 3600; 3431 3432 // Adjust the discussion modified timestamp back an hour, so it's in the past. 3433 $adjustment = (object)[ 3434 'id' => $discussion->id, 3435 'timemodified' => $pasttime, 3436 ]; 3437 $DB->update_record('forum_discussions', $adjustment); 3438 3439 // Let the student reply to the teacher's post. 3440 $reply = $generator->create_post((object)[ 3441 'course' => $course->id, 3442 'userid' => $student->id, 3443 'forum' => $forum->id, 3444 'discussion' => $discussion->id, 3445 'parent' => $discussion->firstpost, 3446 ]); 3447 3448 // The student should now be the last post's author. 3449 $this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3450 3451 // Fetch modified timestamp of the discussion and student's post. 3452 $discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]); 3453 $postmodified = $DB->get_field('forum_posts', 'modified', ['id' => $reply->id]); 3454 3455 // Discussion modified time should be updated to be equal to the newly created post's time. 3456 $this->assertEquals($discussionmodified, $postmodified); 3457 3458 // Adjust the discussion and post timestamps, so they are in the past. 3459 $adjustment = (object)[ 3460 'id' => $discussion->id, 3461 'timemodified' => $pasttime, 3462 ]; 3463 $DB->update_record('forum_discussions', $adjustment); 3464 3465 $adjustment = (object)[ 3466 'id' => $reply->id, 3467 'modified' => $pasttime, 3468 ]; 3469 $DB->update_record('forum_posts', $adjustment); 3470 3471 // The discussion and student's post time should now be an hour in the past. 3472 $this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id])); 3473 $this->assertEquals($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id])); 3474 3475 // Let the teacher edit the student's reply. 3476 $this->setUser($teacher->id); 3477 $newpost = (object)[ 3478 'id' => $reply->id, 3479 'itemid' => 0, 3480 'subject' => 'Amended subject', 3481 ]; 3482 forum_update_post($newpost, null); 3483 3484 // The student should still be the last post's author. 3485 $this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id])); 3486 3487 // The discussion modified time should not have changed. 3488 $this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id])); 3489 3490 // The post time should be updated. 3491 $this->assertGreaterThan($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id])); 3492 } 3493 3494 public function test_forum_core_calendar_provide_event_action() { 3495 $this->resetAfterTest(); 3496 $this->setAdminUser(); 3497 3498 // Create the activity. 3499 $course = $this->getDataGenerator()->create_course(); 3500 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3501 'completionreplies' => 5, 'completiondiscussions' => 2)); 3502 3503 // Create a calendar event. 3504 $event = $this->create_action_event($course->id, $forum->id, 3505 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3506 3507 // Create an action factory. 3508 $factory = new \core_calendar\action_factory(); 3509 3510 // Decorate action event. 3511 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3512 3513 // Confirm the event was decorated. 3514 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 3515 $this->assertEquals(get_string('view'), $actionevent->get_name()); 3516 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 3517 $this->assertEquals(7, $actionevent->get_item_count()); 3518 $this->assertTrue($actionevent->is_actionable()); 3519 } 3520 3521 public function test_forum_core_calendar_provide_event_action_in_hidden_section() { 3522 global $CFG; 3523 3524 $this->resetAfterTest(); 3525 $this->setAdminUser(); 3526 3527 // Create a course. 3528 $course = $this->getDataGenerator()->create_course(); 3529 3530 // Create a student. 3531 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3532 3533 // Create the activity. 3534 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3535 'completionreplies' => 5, 'completiondiscussions' => 2)); 3536 3537 // Create a calendar event. 3538 $event = $this->create_action_event($course->id, $forum->id, 3539 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3540 3541 // Set sections 0 as hidden. 3542 set_section_visible($course->id, 0, 0); 3543 3544 // Now, log out. 3545 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 3546 $this->setUser(); 3547 3548 // Create an action factory. 3549 $factory = new \core_calendar\action_factory(); 3550 3551 // Decorate action event for the student. 3552 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3553 3554 // Confirm the event is not shown at all. 3555 $this->assertNull($actionevent); 3556 } 3557 3558 public function test_forum_core_calendar_provide_event_action_for_user() { 3559 global $CFG; 3560 3561 $this->resetAfterTest(); 3562 $this->setAdminUser(); 3563 3564 // Create a course. 3565 $course = $this->getDataGenerator()->create_course(); 3566 3567 // Create a student. 3568 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3569 3570 // Create the activity. 3571 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 3572 'completionreplies' => 5, 'completiondiscussions' => 2)); 3573 3574 // Create a calendar event. 3575 $event = $this->create_action_event($course->id, $forum->id, 3576 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3577 3578 // Now log out. 3579 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 3580 $this->setUser(); 3581 3582 // Create an action factory. 3583 $factory = new \core_calendar\action_factory(); 3584 3585 // Decorate action event for the student. 3586 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3587 3588 // Confirm the event was decorated. 3589 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 3590 $this->assertEquals(get_string('view'), $actionevent->get_name()); 3591 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 3592 $this->assertEquals(7, $actionevent->get_item_count()); 3593 $this->assertTrue($actionevent->is_actionable()); 3594 } 3595 3596 public function test_forum_core_calendar_provide_event_action_as_non_user() { 3597 global $CFG; 3598 3599 $this->resetAfterTest(); 3600 $this->setAdminUser(); 3601 3602 // Create the activity. 3603 $course = $this->getDataGenerator()->create_course(); 3604 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 3605 3606 // Create a calendar event. 3607 $event = $this->create_action_event($course->id, $forum->id, 3608 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3609 3610 // Log out the user and set force login to true. 3611 \core\session\manager::init_empty_session(); 3612 $CFG->forcelogin = true; 3613 3614 // Create an action factory. 3615 $factory = new \core_calendar\action_factory(); 3616 3617 // Decorate action event. 3618 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3619 3620 // Ensure result was null. 3621 $this->assertNull($actionevent); 3622 } 3623 3624 public function test_forum_core_calendar_provide_event_action_already_completed() { 3625 global $CFG; 3626 3627 $this->resetAfterTest(); 3628 $this->setAdminUser(); 3629 3630 $CFG->enablecompletion = 1; 3631 3632 // Create the activity. 3633 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 3634 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 3635 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 3636 3637 // Get some additional data. 3638 $cm = get_coursemodule_from_instance('forum', $forum->id); 3639 3640 // Create a calendar event. 3641 $event = $this->create_action_event($course->id, $forum->id, 3642 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3643 3644 // Mark the activity as completed. 3645 $completion = new \completion_info($course); 3646 $completion->set_module_viewed($cm); 3647 3648 // Create an action factory. 3649 $factory = new \core_calendar\action_factory(); 3650 3651 // Decorate action event. 3652 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory); 3653 3654 // Ensure result was null. 3655 $this->assertNull($actionevent); 3656 } 3657 3658 public function test_forum_core_calendar_provide_event_action_already_completed_for_user() { 3659 global $CFG; 3660 3661 $this->resetAfterTest(); 3662 $this->setAdminUser(); 3663 3664 $CFG->enablecompletion = 1; 3665 3666 // Create a course. 3667 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 3668 3669 // Create a student. 3670 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 3671 3672 // Create the activity. 3673 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 3674 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 3675 3676 // Get some additional data. 3677 $cm = get_coursemodule_from_instance('forum', $forum->id); 3678 3679 // Create a calendar event. 3680 $event = $this->create_action_event($course->id, $forum->id, 3681 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 3682 3683 // Mark the activity as completed for the student. 3684 $completion = new \completion_info($course); 3685 $completion->set_module_viewed($cm, $student->id); 3686 3687 // Create an action factory. 3688 $factory = new \core_calendar\action_factory(); 3689 3690 // Decorate action event. 3691 $actionevent = mod_forum_core_calendar_provide_event_action($event, $factory, $student->id); 3692 3693 // Ensure result was null. 3694 $this->assertNull($actionevent); 3695 } 3696 3697 public function test_mod_forum_get_tagged_posts() { 3698 global $DB; 3699 3700 $this->resetAfterTest(); 3701 $this->setAdminUser(); 3702 3703 // Setup test data. 3704 $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 3705 $course3 = $this->getDataGenerator()->create_course(); 3706 $course2 = $this->getDataGenerator()->create_course(); 3707 $course1 = $this->getDataGenerator()->create_course(); 3708 $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id)); 3709 $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id)); 3710 $forum3 = $this->getDataGenerator()->create_module('forum', array('course' => $course3->id)); 3711 $post11 = $forumgenerator->create_content($forum1, array('tags' => array('Cats', 'Dogs'))); 3712 $post12 = $forumgenerator->create_content($forum1, array('tags' => array('Cats', 'mice'))); 3713 $post13 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'))); 3714 $post14 = $forumgenerator->create_content($forum1); 3715 $post15 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'))); 3716 $post16 = $forumgenerator->create_content($forum1, array('tags' => array('Cats'), 'hidden' => true)); 3717 $post21 = $forumgenerator->create_content($forum2, array('tags' => array('Cats'))); 3718 $post22 = $forumgenerator->create_content($forum2, array('tags' => array('Cats', 'Dogs'))); 3719 $post23 = $forumgenerator->create_content($forum2, array('tags' => array('mice', 'Cats'))); 3720 $post31 = $forumgenerator->create_content($forum3, array('tags' => array('mice', 'Cats'))); 3721 3722 $tag = \core_tag_tag::get_by_name(0, 'Cats'); 3723 3724 // Admin can see everything. 3725 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3726 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */0); 3727 $this->assertMatchesRegularExpression('/'.$post11->subject.'</', $res->content); 3728 $this->assertMatchesRegularExpression('/'.$post12->subject.'</', $res->content); 3729 $this->assertMatchesRegularExpression('/'.$post13->subject.'</', $res->content); 3730 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'</', $res->content); 3731 $this->assertMatchesRegularExpression('/'.$post15->subject.'</', $res->content); 3732 $this->assertMatchesRegularExpression('/'.$post16->subject.'</', $res->content); 3733 $this->assertDoesNotMatchRegularExpression('/'.$post21->subject.'</', $res->content); 3734 $this->assertDoesNotMatchRegularExpression('/'.$post22->subject.'</', $res->content); 3735 $this->assertDoesNotMatchRegularExpression('/'.$post23->subject.'</', $res->content); 3736 $this->assertDoesNotMatchRegularExpression('/'.$post31->subject.'</', $res->content); 3737 $this->assertEmpty($res->prevpageurl); 3738 $this->assertNotEmpty($res->nextpageurl); 3739 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3740 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */1); 3741 $this->assertDoesNotMatchRegularExpression('/'.$post11->subject.'</', $res->content); 3742 $this->assertDoesNotMatchRegularExpression('/'.$post12->subject.'</', $res->content); 3743 $this->assertDoesNotMatchRegularExpression('/'.$post13->subject.'</', $res->content); 3744 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'</', $res->content); 3745 $this->assertDoesNotMatchRegularExpression('/'.$post15->subject.'</', $res->content); 3746 $this->assertDoesNotMatchRegularExpression('/'.$post16->subject.'</', $res->content); 3747 $this->assertMatchesRegularExpression('/'.$post21->subject.'</', $res->content); 3748 $this->assertMatchesRegularExpression('/'.$post22->subject.'</', $res->content); 3749 $this->assertMatchesRegularExpression('/'.$post23->subject.'</', $res->content); 3750 $this->assertMatchesRegularExpression('/'.$post31->subject.'</', $res->content); 3751 $this->assertNotEmpty($res->prevpageurl); 3752 $this->assertEmpty($res->nextpageurl); 3753 3754 // Create and enrol a user. 3755 $student = self::getDataGenerator()->create_user(); 3756 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 3757 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); 3758 $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual'); 3759 $this->setUser($student); 3760 \core_tag_index_builder::reset_caches(); 3761 3762 // User can not see posts in course 3 because he is not enrolled. 3763 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3764 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$post = */1); 3765 $this->assertMatchesRegularExpression('/'.$post22->subject.'/', $res->content); 3766 $this->assertMatchesRegularExpression('/'.$post23->subject.'/', $res->content); 3767 $this->assertDoesNotMatchRegularExpression('/'.$post31->subject.'/', $res->content); 3768 3769 // User can search forum posts inside a course. 3770 $coursecontext = \context_course::instance($course1->id); 3771 $res = mod_forum_get_tagged_posts($tag, /*$exclusivemode = */false, 3772 /*$fromctx = */0, /*$ctx = */$coursecontext->id, /*$rec = */1, /*$post = */0); 3773 $this->assertMatchesRegularExpression('/'.$post11->subject.'/', $res->content); 3774 $this->assertMatchesRegularExpression('/'.$post12->subject.'/', $res->content); 3775 $this->assertMatchesRegularExpression('/'.$post13->subject.'/', $res->content); 3776 $this->assertDoesNotMatchRegularExpression('/'.$post14->subject.'/', $res->content); 3777 $this->assertMatchesRegularExpression('/'.$post15->subject.'/', $res->content); 3778 $this->assertMatchesRegularExpression('/'.$post16->subject.'/', $res->content); 3779 $this->assertDoesNotMatchRegularExpression('/'.$post21->subject.'/', $res->content); 3780 $this->assertDoesNotMatchRegularExpression('/'.$post22->subject.'/', $res->content); 3781 $this->assertDoesNotMatchRegularExpression('/'.$post23->subject.'/', $res->content); 3782 $this->assertEmpty($res->nextpageurl); 3783 } 3784 3785 /** 3786 * Creates an action event. 3787 * 3788 * @param int $courseid The course id. 3789 * @param int $instanceid The instance id. 3790 * @param string $eventtype The event type. 3791 * @return bool|calendar_event 3792 */ 3793 private function create_action_event($courseid, $instanceid, $eventtype) { 3794 $event = new \stdClass(); 3795 $event->name = 'Calendar event'; 3796 $event->modulename = 'forum'; 3797 $event->courseid = $courseid; 3798 $event->instance = $instanceid; 3799 $event->type = CALENDAR_EVENT_TYPE_ACTION; 3800 $event->eventtype = $eventtype; 3801 $event->timestart = time(); 3802 3803 return \calendar_event::create($event); 3804 } 3805 3806 /** 3807 * Test the callback responsible for returning the completion rule descriptions. 3808 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 3809 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 3810 */ 3811 public function test_mod_forum_completion_get_active_rule_descriptions() { 3812 $this->resetAfterTest(); 3813 $this->setAdminUser(); 3814 3815 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 3816 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 3817 $forum1 = $this->getDataGenerator()->create_module('forum', [ 3818 'course' => $course->id, 3819 'completion' => 2, 3820 'completiondiscussions' => 3, 3821 'completionreplies' => 3, 3822 'completionposts' => 3 3823 ]); 3824 $forum2 = $this->getDataGenerator()->create_module('forum', [ 3825 'course' => $course->id, 3826 'completion' => 2, 3827 'completiondiscussions' => 0, 3828 'completionreplies' => 0, 3829 'completionposts' => 0 3830 ]); 3831 $cm1 = \cm_info::create(get_coursemodule_from_instance('forum', $forum1->id)); 3832 $cm2 = \cm_info::create(get_coursemodule_from_instance('forum', $forum2->id)); 3833 3834 // Data for the stdClass input type. 3835 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 3836 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 3837 $moddefaults = new \stdClass(); 3838 $moddefaults->customdata = ['customcompletionrules' => [ 3839 'completiondiscussions' => 3, 3840 'completionreplies' => 3, 3841 'completionposts' => 3 3842 ]]; 3843 $moddefaults->completion = 2; 3844 3845 $activeruledescriptions = [ 3846 get_string('completiondiscussionsdesc', 'forum', 3), 3847 get_string('completionrepliesdesc', 'forum', 3), 3848 get_string('completionpostsdesc', 'forum', 3) 3849 ]; 3850 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 3851 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($cm2), []); 3852 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 3853 $this->assertEquals(mod_forum_get_completion_active_rule_descriptions(new \stdClass()), []); 3854 } 3855 3856 /** 3857 * Test the forum_post_is_visible_privately function used in private replies. 3858 */ 3859 public function test_forum_post_is_visible_privately() { 3860 $this->resetAfterTest(); 3861 3862 $course = $this->getDataGenerator()->create_course(); 3863 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 3864 $context = \context_module::instance($forum->cmid); 3865 $cm = get_coursemodule_from_instance('forum', $forum->id); 3866 3867 $author = $this->getDataGenerator()->create_user(); 3868 $this->getDataGenerator()->enrol_user($author->id, $course->id); 3869 3870 $recipient = $this->getDataGenerator()->create_user(); 3871 $this->getDataGenerator()->enrol_user($recipient->id, $course->id); 3872 3873 $privilegeduser = $this->getDataGenerator()->create_user(); 3874 $this->getDataGenerator()->enrol_user($privilegeduser->id, $course->id, 'editingteacher'); 3875 3876 $otheruser = $this->getDataGenerator()->create_user(); 3877 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id); 3878 3879 // Fake a post - this does not need to be persisted to the DB. 3880 $post = new \stdClass(); 3881 $post->userid = $author->id; 3882 $post->privatereplyto = $recipient->id; 3883 3884 // The user is the author. 3885 $this->setUser($author->id); 3886 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3887 3888 // The user is the intended recipient. 3889 $this->setUser($recipient->id); 3890 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3891 3892 // The user is not the author or recipient, but does have the readprivatereplies capability. 3893 $this->setUser($privilegeduser->id); 3894 $this->assertTrue(forum_post_is_visible_privately($post, $cm)); 3895 3896 // The user is not allowed to view this post. 3897 $this->setUser($otheruser->id); 3898 $this->assertFalse(forum_post_is_visible_privately($post, $cm)); 3899 } 3900 3901 /** 3902 * An unkown event type should not have any limits 3903 */ 3904 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_unknown_event() { 3905 global $CFG; 3906 require_once($CFG->dirroot . "/calendar/lib.php"); 3907 3908 $this->resetAfterTest(true); 3909 $this->setAdminUser(); 3910 $generator = $this->getDataGenerator(); 3911 $course = $generator->create_course(); 3912 $duedate = time() + DAYSECS; 3913 $forum = new \stdClass(); 3914 $forum->duedate = $duedate; 3915 3916 // Create a valid event. 3917 $event = new \calendar_event([ 3918 'name' => 'Test event', 3919 'description' => '', 3920 'format' => 1, 3921 'courseid' => $course->id, 3922 'groupid' => 0, 3923 'userid' => 2, 3924 'modulename' => 'forum', 3925 'instance' => 1, 3926 'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE", 3927 'timestart' => 1, 3928 'timeduration' => 86400, 3929 'visible' => 1 3930 ]); 3931 3932 list ($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 3933 $this->assertNull($min); 3934 $this->assertNull($max); 3935 } 3936 3937 /** 3938 * Forums configured without a cutoff date should not have any limits applied. 3939 */ 3940 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_no_limit() { 3941 global $CFG; 3942 require_once($CFG->dirroot . '/calendar/lib.php'); 3943 3944 $this->resetAfterTest(true); 3945 $this->setAdminUser(); 3946 $generator = $this->getDataGenerator(); 3947 $course = $generator->create_course(); 3948 $duedate = time() + DAYSECS; 3949 $forum = new \stdClass(); 3950 $forum->duedate = $duedate; 3951 3952 // Create a valid event. 3953 $event = new \calendar_event([ 3954 'name' => 'Test event', 3955 'description' => '', 3956 'format' => 1, 3957 'courseid' => $course->id, 3958 'groupid' => 0, 3959 'userid' => 2, 3960 'modulename' => 'forum', 3961 'instance' => 1, 3962 'eventtype' => FORUM_EVENT_TYPE_DUE, 3963 'timestart' => 1, 3964 'timeduration' => 86400, 3965 'visible' => 1 3966 ]); 3967 3968 list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 3969 $this->assertNull($min); 3970 $this->assertNull($max); 3971 } 3972 3973 /** 3974 * Forums should be top bound by the cutoff date. 3975 */ 3976 public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_with_limits() { 3977 global $CFG; 3978 require_once($CFG->dirroot . '/calendar/lib.php'); 3979 3980 $this->resetAfterTest(true); 3981 $this->setAdminUser(); 3982 $generator = $this->getDataGenerator(); 3983 $course = $generator->create_course(); 3984 $duedate = time() + DAYSECS; 3985 $cutoffdate = $duedate + DAYSECS; 3986 $forum = new \stdClass(); 3987 $forum->duedate = $duedate; 3988 $forum->cutoffdate = $cutoffdate; 3989 3990 // Create a valid event. 3991 $event = new \calendar_event([ 3992 'name' => 'Test event', 3993 'description' => '', 3994 'format' => 1, 3995 'courseid' => $course->id, 3996 'groupid' => 0, 3997 'userid' => 2, 3998 'modulename' => 'forum', 3999 'instance' => 1, 4000 'eventtype' => FORUM_EVENT_TYPE_DUE, 4001 'timestart' => 1, 4002 'timeduration' => 86400, 4003 'visible' => 1 4004 ]); 4005 4006 list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum); 4007 $this->assertNull($min); 4008 $this->assertEquals($cutoffdate, $max[0]); 4009 $this->assertNotEmpty($max[1]); 4010 } 4011 4012 /** 4013 * An unknown event type should not change the forum instance. 4014 */ 4015 public function test_mod_forum_core_calendar_event_timestart_updated_unknown_event() { 4016 global $CFG, $DB; 4017 require_once($CFG->dirroot . "/calendar/lib.php"); 4018 4019 $this->resetAfterTest(true); 4020 $this->setAdminUser(); 4021 $generator = $this->getDataGenerator(); 4022 $course = $generator->create_course(); 4023 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4024 $duedate = time() + DAYSECS; 4025 $cutoffdate = $duedate + DAYSECS; 4026 $forum = $forumgenerator->create_instance(['course' => $course->id]); 4027 $forum->duedate = $duedate; 4028 $forum->cutoffdate = $cutoffdate; 4029 $DB->update_record('forum', $forum); 4030 4031 // Create a valid event. 4032 $event = new \calendar_event([ 4033 'name' => 'Test event', 4034 'description' => '', 4035 'format' => 1, 4036 'courseid' => $course->id, 4037 'groupid' => 0, 4038 'userid' => 2, 4039 'modulename' => 'forum', 4040 'instance' => $forum->id, 4041 'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE", 4042 'timestart' => 1, 4043 'timeduration' => 86400, 4044 'visible' => 1 4045 ]); 4046 4047 mod_forum_core_calendar_event_timestart_updated($event, $forum); 4048 4049 $forum = $DB->get_record('forum', ['id' => $forum->id]); 4050 $this->assertEquals($duedate, $forum->duedate); 4051 $this->assertEquals($cutoffdate, $forum->cutoffdate); 4052 } 4053 4054 /** 4055 * Due date events should update the forum due date. 4056 */ 4057 public function test_mod_forum_core_calendar_event_timestart_updated_due_event() { 4058 global $CFG, $DB; 4059 require_once($CFG->dirroot . "/calendar/lib.php"); 4060 4061 $this->resetAfterTest(true); 4062 $this->setAdminUser(); 4063 $generator = $this->getDataGenerator(); 4064 $course = $generator->create_course(); 4065 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4066 $duedate = time() + DAYSECS; 4067 $cutoffdate = $duedate + DAYSECS; 4068 $newduedate = $duedate + 1; 4069 $forum = $forumgenerator->create_instance(['course' => $course->id]); 4070 $forum->duedate = $duedate; 4071 $forum->cutoffdate = $cutoffdate; 4072 $DB->update_record('forum', $forum); 4073 4074 // Create a valid event. 4075 $event = new \calendar_event([ 4076 'name' => 'Test event', 4077 'description' => '', 4078 'format' => 1, 4079 'courseid' => $course->id, 4080 'groupid' => 0, 4081 'userid' => 2, 4082 'modulename' => 'forum', 4083 'instance' => $forum->id, 4084 'eventtype' => FORUM_EVENT_TYPE_DUE, 4085 'timestart' => $newduedate, 4086 'timeduration' => 86400, 4087 'visible' => 1 4088 ]); 4089 4090 mod_forum_core_calendar_event_timestart_updated($event, $forum); 4091 4092 $forum = $DB->get_record('forum', ['id' => $forum->id]); 4093 $this->assertEquals($newduedate, $forum->duedate); 4094 $this->assertEquals($cutoffdate, $forum->cutoffdate); 4095 } 4096 4097 /** 4098 * Test forum_get_layout_modes function. 4099 */ 4100 public function test_forum_get_layout_modes() { 4101 $expectednormal = [ 4102 FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), 4103 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), 4104 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), 4105 FORUM_MODE_NESTED => get_string('modenested', 'forum') 4106 ]; 4107 $expectedexperimental = [ 4108 FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), 4109 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), 4110 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), 4111 FORUM_MODE_NESTED_V2 => get_string('modenestedv2', 'forum') 4112 ]; 4113 4114 $this->assertEquals($expectednormal, forum_get_layout_modes()); 4115 $this->assertEquals($expectednormal, forum_get_layout_modes(false)); 4116 $this->assertEquals($expectedexperimental, forum_get_layout_modes(true)); 4117 } 4118 4119 /** 4120 * Provides data for tests that cause forum_check_throttling to return early. 4121 * 4122 * @return array 4123 */ 4124 public function forum_check_throttling_early_returns_provider() { 4125 return [ 4126 'Empty blockafter' => [(object)['id' => 1, 'course' => SITEID, 'blockafter' => 0]], 4127 'Empty blockperiod' => [(object)['id' => 1, 'course' => SITEID, 'blockafter' => DAYSECS, 'blockperiod' => 0]], 4128 ]; 4129 } 4130 4131 /** 4132 * Tests the early return scenarios of forum_check_throttling. 4133 * 4134 * @dataProvider forum_check_throttling_early_returns_provider 4135 * @covers ::forum_check_throttling 4136 * @param \stdClass $forum The forum data. 4137 */ 4138 public function test_forum_check_throttling_early_returns(\stdClass $forum) { 4139 $this->assertFalse(forum_check_throttling($forum)); 4140 } 4141 4142 /** 4143 * Provides data for tests that cause forum_check_throttling to throw exceptions early. 4144 * 4145 * @return array 4146 */ 4147 public function forum_check_throttling_early_exceptions_provider() { 4148 return [ 4149 'Non-object forum' => ['a'], 4150 'Forum ID not set' => [(object)['id' => false]], 4151 'Course ID not set' => [(object)['id' => 1]], 4152 ]; 4153 } 4154 4155 /** 4156 * Tests the early exception scenarios of forum_check_throttling. 4157 * 4158 * @dataProvider forum_check_throttling_early_exceptions_provider 4159 * @covers ::forum_check_throttling 4160 * @param mixed $forum The forum data. 4161 */ 4162 public function test_forum_check_throttling_early_exceptions($forum) { 4163 $this->expectException(\coding_exception::class); 4164 $this->assertFalse(forum_check_throttling($forum)); 4165 } 4166 4167 /** 4168 * Tests forum_check_throttling when a non-existent numeric ID is passed for its forum parameter. 4169 * 4170 * @covers ::forum_check_throttling 4171 */ 4172 public function test_forum_check_throttling_nonexistent_numeric_id() { 4173 $this->resetAfterTest(); 4174 4175 $this->expectException(\moodle_exception::class); 4176 forum_check_throttling(1); 4177 } 4178 4179 /** 4180 * Tests forum_check_throttling when a non-existent forum record is passed for its forum parameter. 4181 * 4182 * @covers ::forum_check_throttling 4183 */ 4184 public function test_forum_check_throttling_nonexistent_forum_cm() { 4185 $this->resetAfterTest(); 4186 4187 $dummyforum = (object)[ 4188 'id' => 1, 4189 'course' => SITEID, 4190 'blockafter' => 2, 4191 'blockperiod' => DAYSECS, 4192 ]; 4193 $this->expectException(\moodle_exception::class); 4194 forum_check_throttling($dummyforum); 4195 } 4196 4197 /** 4198 * Tests forum_check_throttling when a user with the 'mod/forum:postwithoutthrottling' capability. 4199 * 4200 * @covers ::forum_check_throttling 4201 */ 4202 public function test_forum_check_throttling_teacher() { 4203 $this->resetAfterTest(); 4204 4205 $generator = $this->getDataGenerator(); 4206 $course = $generator->create_course(); 4207 $teacher = $generator->create_and_enrol($course, 'teacher'); 4208 4209 /** @var mod_forum_generator $forumgenerator */ 4210 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4211 // Forum that limits students from creating more than two posts per day. 4212 $forum = $forumgenerator->create_instance( 4213 [ 4214 'course' => $course->id, 4215 'blockafter' => 2, 4216 'blockperiod' => DAYSECS, 4217 ] 4218 ); 4219 4220 $this->setUser($teacher); 4221 $discussionrecord = [ 4222 'course' => $course->id, 4223 'forum' => $forum->id, 4224 'userid' => $teacher->id, 4225 ]; 4226 $discussion = $forumgenerator->create_discussion($discussionrecord); 4227 // Create a forum post as the teacher. 4228 $postrecord = [ 4229 'userid' => $teacher->id, 4230 'discussion' => $discussion->id, 4231 ]; 4232 $forumgenerator->create_post($postrecord); 4233 // Create another forum post. 4234 $forumgenerator->create_post($postrecord); 4235 4236 $this->assertFalse(forum_check_throttling($forum)); 4237 } 4238 4239 /** 4240 * Tests forum_check_throttling for students. 4241 * 4242 * @covers ::forum_check_throttling 4243 */ 4244 public function test_forum_check_throttling_student() { 4245 $this->resetAfterTest(); 4246 4247 $generator = $this->getDataGenerator(); 4248 $course = $generator->create_course(); 4249 $student = $generator->create_and_enrol($course, 'student'); 4250 4251 /** @var mod_forum_generator $forumgenerator */ 4252 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 4253 // Forum that limits students from creating more than two posts per day. 4254 $forum = $forumgenerator->create_instance( 4255 [ 4256 'course' => $course->id, 4257 'blockafter' => 2, 4258 'blockperiod' => DAYSECS, 4259 'warnafter' => 1, 4260 ] 4261 ); 4262 4263 $this->setUser($student); 4264 4265 // Student hasn't posted yet so no warning will be shown. 4266 $throttling = forum_check_throttling($forum); 4267 $this->assertFalse($throttling); 4268 4269 // Create a discussion. 4270 $discussionrecord = [ 4271 'course' => $course->id, 4272 'forum' => $forum->id, 4273 'userid' => $student->id, 4274 ]; 4275 $discussion = $forumgenerator->create_discussion($discussionrecord); 4276 4277 // A warning will be shown to the student, but they should still be able to post. 4278 $throttling = forum_check_throttling($forum); 4279 $this->assertIsObject($throttling); 4280 $this->assertTrue($throttling->canpost); 4281 4282 // Create another forum post as the student. 4283 $postrecord = [ 4284 'userid' => $student->id, 4285 'discussion' => $discussion->id, 4286 ]; 4287 $forumgenerator->create_post($postrecord); 4288 4289 // Student should now be unable to post after their second post. 4290 $throttling = forum_check_throttling($forum); 4291 $this->assertIsObject($throttling); 4292 $this->assertFalse($throttling->canpost); 4293 } 4294 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body