[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * The forum module mail generation tests. 19 * 20 * @package mod_forum 21 * @category external 22 * @copyright 2013 Andrew Nicols 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 30 class mod_forum_mail_testcase extends advanced_testcase { 31 32 protected $helper; 33 34 public function setUp() { 35 // We must clear the subscription caches. This has to be done both before each test, and after in case of other 36 // tests using these functions. 37 \mod_forum\subscriptions::reset_forum_cache(); 38 \mod_forum\subscriptions::reset_discussion_cache(); 39 40 global $CFG; 41 require_once($CFG->dirroot . '/mod/forum/lib.php'); 42 43 $helper = new stdClass(); 44 45 // Messaging is not compatible with transactions... 46 $this->preventResetByRollback(); 47 48 // Catch all messages. 49 $helper->messagesink = $this->redirectMessages(); 50 $helper->mailsink = $this->redirectEmails(); 51 52 // Confirm that we have an empty message sink so far. 53 $messages = $helper->messagesink->get_messages(); 54 $this->assertEquals(0, count($messages)); 55 56 $messages = $helper->mailsink->get_messages(); 57 $this->assertEquals(0, count($messages)); 58 59 // Forcibly reduce the maxeditingtime to a second in the past to 60 // ensure that messages are sent out. 61 $CFG->maxeditingtime = -1; 62 63 $this->helper = $helper; 64 } 65 66 public function tearDown() { 67 // We must clear the subscription caches. This has to be done both before each test, and after in case of other 68 // tests using these functions. 69 \mod_forum\subscriptions::reset_forum_cache(); 70 71 $this->helper->messagesink->clear(); 72 $this->helper->messagesink->close(); 73 74 $this->helper->mailsink->clear(); 75 $this->helper->mailsink->close(); 76 } 77 78 /** 79 * Perform message inbound setup for the mod_forum reply handler. 80 */ 81 protected function helper_spoof_message_inbound_setup() { 82 global $CFG, $DB; 83 // Setup the default Inbound Message mailbox settings. 84 $CFG->messageinbound_domain = 'example.com'; 85 $CFG->messageinbound_enabled = true; 86 87 // Must be no longer than 15 characters. 88 $CFG->messageinbound_mailbox = 'moodlemoodle123'; 89 90 $record = $DB->get_record('messageinbound_handlers', array('classname' => '\mod_forum\message\inbound\reply_handler')); 91 $record->enabled = true; 92 $record->id = $DB->update_record('messageinbound_handlers', $record); 93 } 94 95 /** 96 * Helper to create the required number of users in the specified 97 * course. 98 * Users are enrolled as students. 99 * 100 * @param stdClass $course The course object 101 * @param integer $count The number of users to create 102 * @return array The users created 103 */ 104 protected function helper_create_users($course, $count) { 105 $users = array(); 106 107 for ($i = 0; $i < $count; $i++) { 108 $user = $this->getDataGenerator()->create_user(); 109 $this->getDataGenerator()->enrol_user($user->id, $course->id); 110 $users[] = $user; 111 } 112 113 return $users; 114 } 115 116 /** 117 * Create a new discussion and post within the specified forum, as the 118 * specified author. 119 * 120 * @param stdClass $forum The forum to post in 121 * @param stdClass $author The author to post as 122 * @param array $fields any other fields in discussion (name, message, messageformat, ...) 123 * @param array An array containing the discussion object, and the post object 124 */ 125 protected function helper_post_to_forum($forum, $author, $fields = array()) { 126 global $DB; 127 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 128 129 // Create a discussion in the forum, and then add a post to that discussion. 130 $record = (object)$fields; 131 $record->course = $forum->course; 132 $record->userid = $author->id; 133 $record->forum = $forum->id; 134 $discussion = $generator->create_discussion($record); 135 136 // Retrieve the post which was created by create_discussion. 137 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id)); 138 139 return array($discussion, $post); 140 } 141 142 /** 143 * Update the post time for the specified post by $factor. 144 * 145 * @param stdClass $post The post to update 146 * @param int $factor The amount to update by 147 */ 148 protected function helper_update_post_time($post, $factor) { 149 global $DB; 150 151 // Update the post to have a created in the past. 152 $DB->set_field('forum_posts', 'created', $post->created + $factor, array('id' => $post->id)); 153 } 154 155 /** 156 * Update the subscription time for the specified user/discussion by $factor. 157 * 158 * @param stdClass $user The user to update 159 * @param stdClass $discussion The discussion to update for this user 160 * @param int $factor The amount to update by 161 */ 162 protected function helper_update_subscription_time($user, $discussion, $factor) { 163 global $DB; 164 165 $sub = $DB->get_record('forum_discussion_subs', array('userid' => $user->id, 'discussion' => $discussion->id)); 166 167 // Update the subscription to have a preference in the past. 168 $DB->set_field('forum_discussion_subs', 'preference', $sub->preference + $factor, array('id' => $sub->id)); 169 } 170 171 /** 172 * Create a new post within an existing discussion, as the specified author. 173 * 174 * @param stdClass $forum The forum to post in 175 * @param stdClass $discussion The discussion to post in 176 * @param stdClass $author The author to post as 177 * @return stdClass The forum post 178 */ 179 protected function helper_post_to_discussion($forum, $discussion, $author) { 180 global $DB; 181 182 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); 183 184 // Add a post to the discussion. 185 $record = new stdClass(); 186 $record->course = $forum->course; 187 $strre = get_string('re', 'forum'); 188 $record->subject = $strre . ' ' . $discussion->subject; 189 $record->userid = $author->id; 190 $record->forum = $forum->id; 191 $record->discussion = $discussion->id; 192 $record->mailnow = 1; 193 194 $post = $generator->create_post($record); 195 196 return $post; 197 } 198 199 /** 200 * Run the forum cron, and check that the specified post was sent the 201 * specified number of times. 202 * 203 * @param stdClass $post The forum post object 204 * @param integer $expected The number of times that the post should have been sent 205 * @return array An array of the messages caught by the message sink 206 */ 207 protected function helper_run_cron_check_count($post, $expected) { 208 209 // Clear the sinks before running cron. 210 $this->helper->messagesink->clear(); 211 $this->helper->mailsink->clear(); 212 213 // Cron daily uses mtrace, turn on buffering to silence output. 214 $this->expectOutputRegex("/{$expected} users were sent post {$post->id}, '{$post->subject}'/"); 215 forum_cron(); 216 217 // Now check the results in the message sink. 218 $messages = $this->helper->messagesink->get_messages(); 219 220 // There should be the expected number of messages. 221 $this->assertEquals($expected, count($messages)); 222 223 return $messages; 224 } 225 226 /** 227 * Run the forum cron, and check that the specified posts were sent the 228 * specified number of times. 229 * 230 * @param stdClass $post The forum post object 231 * @param integer $expected The number of times that the post should have been sent 232 * @return array An array of the messages caught by the message sink 233 */ 234 protected function helper_run_cron_check_counts($posts, $expected) { 235 236 // Clear the sinks before running cron. 237 $this->helper->messagesink->clear(); 238 $this->helper->mailsink->clear(); 239 240 // Cron daily uses mtrace, turn on buffering to silence output. 241 foreach ($posts as $post) { 242 $this->expectOutputRegex("/{$post['count']} users were sent post {$post['id']}, '{$post['subject']}'/"); 243 } 244 forum_cron(); 245 246 // Now check the results in the message sink. 247 $messages = $this->helper->messagesink->get_messages(); 248 249 // There should be the expected number of messages. 250 $this->assertEquals($expected, count($messages)); 251 252 return $messages; 253 } 254 255 public function test_forced_subscription() { 256 $this->resetAfterTest(true); 257 258 // Create a course, with a forum. 259 $course = $this->getDataGenerator()->create_course(); 260 261 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); 262 $forum = $this->getDataGenerator()->create_module('forum', $options); 263 264 // Create two users enrolled in the course as students. 265 list($author, $recipient) = $this->helper_create_users($course, 2); 266 267 // Post a discussion to the forum. 268 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 269 270 // We expect both users to receive this post. 271 $expected = 2; 272 273 // Run cron and check that the expected number of users received the notification. 274 $messages = $this->helper_run_cron_check_count($post, $expected); 275 276 $seenauthor = false; 277 $seenrecipient = false; 278 foreach ($messages as $message) { 279 // They should both be from our user. 280 $this->assertEquals($author->id, $message->useridfrom); 281 282 if ($message->useridto == $author->id) { 283 $seenauthor = true; 284 } else if ($message->useridto = $recipient->id) { 285 $seenrecipient = true; 286 } 287 } 288 289 // Check we saw messages for both users. 290 $this->assertTrue($seenauthor); 291 $this->assertTrue($seenrecipient); 292 } 293 294 public function test_subscription_disabled() { 295 global $DB; 296 297 $this->resetAfterTest(true); 298 299 // Create a course, with a forum. 300 $course = $this->getDataGenerator()->create_course(); 301 302 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); 303 $forum = $this->getDataGenerator()->create_module('forum', $options); 304 305 // Create two users enrolled in the course as students. 306 list($author, $recipient) = $this->helper_create_users($course, 2); 307 308 // Post a discussion to the forum. 309 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 310 311 // We expect both users to receive this post. 312 $expected = 0; 313 314 // Run cron and check that the expected number of users received the notification. 315 $messages = $this->helper_run_cron_check_count($post, $expected); 316 317 // A user with the manageactivities capability within the course can subscribe. 318 $expected = 1; 319 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); 320 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id)); 321 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); 322 323 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array( 324 'userid' => $recipient->id, 325 'forum' => $forum->id, 326 ))); 327 328 // Run cron and check that the expected number of users received the notification. 329 list($discussion, $post) = $this->helper_post_to_forum($forum, $recipient); 330 $messages = $this->helper_run_cron_check_count($post, $expected); 331 332 // Unsubscribe the user again. 333 \mod_forum\subscriptions::unsubscribe_user($recipient->id, $forum); 334 335 $expected = 0; 336 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array( 337 'userid' => $recipient->id, 338 'forum' => $forum->id, 339 ))); 340 341 // Run cron and check that the expected number of users received the notification. 342 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 343 $messages = $this->helper_run_cron_check_count($post, $expected); 344 345 // Subscribe the user to the discussion. 346 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion); 347 $this->helper_update_subscription_time($recipient, $discussion, -60); 348 349 $reply = $this->helper_post_to_discussion($forum, $discussion, $author); 350 $this->helper_update_post_time($reply, -30); 351 352 $messages = $this->helper_run_cron_check_count($reply, $expected); 353 } 354 355 public function test_automatic() { 356 $this->resetAfterTest(true); 357 358 // Create a course, with a forum. 359 $course = $this->getDataGenerator()->create_course(); 360 361 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); 362 $forum = $this->getDataGenerator()->create_module('forum', $options); 363 364 // Create two users enrolled in the course as students. 365 list($author, $recipient) = $this->helper_create_users($course, 2); 366 367 // Post a discussion to the forum. 368 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 369 370 // We expect both users to receive this post. 371 $expected = 2; 372 373 // Run cron and check that the expected number of users received the notification. 374 $messages = $this->helper_run_cron_check_count($post, $expected); 375 376 $seenauthor = false; 377 $seenrecipient = false; 378 foreach ($messages as $message) { 379 // They should both be from our user. 380 $this->assertEquals($author->id, $message->useridfrom); 381 382 if ($message->useridto == $author->id) { 383 $seenauthor = true; 384 } else if ($message->useridto = $recipient->id) { 385 $seenrecipient = true; 386 } 387 } 388 389 // Check we saw messages for both users. 390 $this->assertTrue($seenauthor); 391 $this->assertTrue($seenrecipient); 392 } 393 394 public function test_optional() { 395 $this->resetAfterTest(true); 396 397 // Create a course, with a forum. 398 $course = $this->getDataGenerator()->create_course(); 399 400 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 401 $forum = $this->getDataGenerator()->create_module('forum', $options); 402 403 // Create two users enrolled in the course as students. 404 list($author, $recipient) = $this->helper_create_users($course, 2); 405 406 // Post a discussion to the forum. 407 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 408 409 // We expect both users to receive this post. 410 $expected = 0; 411 412 // Run cron and check that the expected number of users received the notification. 413 $messages = $this->helper_run_cron_check_count($post, $expected); 414 } 415 416 public function test_automatic_with_unsubscribed_user() { 417 $this->resetAfterTest(true); 418 419 // Create a course, with a forum. 420 $course = $this->getDataGenerator()->create_course(); 421 422 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); 423 $forum = $this->getDataGenerator()->create_module('forum', $options); 424 425 // Create two users enrolled in the course as students. 426 list($author, $recipient) = $this->helper_create_users($course, 2); 427 428 // Unsubscribe the 'author' user from the forum. 429 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum); 430 431 // Post a discussion to the forum. 432 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 433 434 // We expect only one user to receive this post. 435 $expected = 1; 436 437 // Run cron and check that the expected number of users received the notification. 438 $messages = $this->helper_run_cron_check_count($post, $expected); 439 440 $seenauthor = false; 441 $seenrecipient = false; 442 foreach ($messages as $message) { 443 // They should both be from our user. 444 $this->assertEquals($author->id, $message->useridfrom); 445 446 if ($message->useridto == $author->id) { 447 $seenauthor = true; 448 } else if ($message->useridto = $recipient->id) { 449 $seenrecipient = true; 450 } 451 } 452 453 // Check we only saw one user. 454 $this->assertFalse($seenauthor); 455 $this->assertTrue($seenrecipient); 456 } 457 458 public function test_optional_with_subscribed_user() { 459 $this->resetAfterTest(true); 460 461 // Create a course, with a forum. 462 $course = $this->getDataGenerator()->create_course(); 463 464 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 465 $forum = $this->getDataGenerator()->create_module('forum', $options); 466 467 // Create two users enrolled in the course as students. 468 list($author, $recipient) = $this->helper_create_users($course, 2); 469 470 // Subscribe the 'recipient' user from the forum. 471 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); 472 473 // Post a discussion to the forum. 474 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 475 476 // We expect only one user to receive this post. 477 $expected = 1; 478 479 // Run cron and check that the expected number of users received the notification. 480 $messages = $this->helper_run_cron_check_count($post, $expected); 481 482 $seenauthor = false; 483 $seenrecipient = false; 484 foreach ($messages as $message) { 485 // They should both be from our user. 486 $this->assertEquals($author->id, $message->useridfrom); 487 488 if ($message->useridto == $author->id) { 489 $seenauthor = true; 490 } else if ($message->useridto = $recipient->id) { 491 $seenrecipient = true; 492 } 493 } 494 495 // Check we only saw one user. 496 $this->assertFalse($seenauthor); 497 $this->assertTrue($seenrecipient); 498 } 499 500 public function test_automatic_with_unsubscribed_discussion() { 501 $this->resetAfterTest(true); 502 503 // Create a course, with a forum. 504 $course = $this->getDataGenerator()->create_course(); 505 506 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); 507 $forum = $this->getDataGenerator()->create_module('forum', $options); 508 509 // Create two users enrolled in the course as students. 510 list($author, $recipient) = $this->helper_create_users($course, 2); 511 512 // Post a discussion to the forum. 513 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 514 515 // Unsubscribe the 'author' user from the discussion. 516 \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion); 517 518 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id)); 519 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($recipient->id, $forum, $discussion->id)); 520 521 // We expect only one user to receive this post. 522 $expected = 1; 523 524 // Run cron and check that the expected number of users received the notification. 525 $messages = $this->helper_run_cron_check_count($post, $expected); 526 527 $seenauthor = false; 528 $seenrecipient = false; 529 foreach ($messages as $message) { 530 // They should both be from our user. 531 $this->assertEquals($author->id, $message->useridfrom); 532 533 if ($message->useridto == $author->id) { 534 $seenauthor = true; 535 } else if ($message->useridto = $recipient->id) { 536 $seenrecipient = true; 537 } 538 } 539 540 // Check we only saw one user. 541 $this->assertFalse($seenauthor); 542 $this->assertTrue($seenrecipient); 543 } 544 545 public function test_optional_with_subscribed_discussion() { 546 $this->resetAfterTest(true); 547 548 // Create a course, with a forum. 549 $course = $this->getDataGenerator()->create_course(); 550 551 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 552 $forum = $this->getDataGenerator()->create_module('forum', $options); 553 554 // Create two users enrolled in the course as students. 555 list($author, $recipient) = $this->helper_create_users($course, 2); 556 557 // Post a discussion to the forum. 558 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 559 $this->helper_update_post_time($post, -90); 560 561 // Subscribe the 'recipient' user to the discussion. 562 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion); 563 $this->helper_update_subscription_time($recipient, $discussion, -60); 564 565 // Initially we don't expect any user to receive this post as you cannot subscribe to a discussion until after 566 // you have read it. 567 $expected = 0; 568 569 // Run cron and check that the expected number of users received the notification. 570 $messages = $this->helper_run_cron_check_count($post, $expected); 571 572 // Have a user reply to the discussion. 573 $reply = $this->helper_post_to_discussion($forum, $discussion, $author); 574 $this->helper_update_post_time($reply, -30); 575 576 // We expect only one user to receive this post. 577 $expected = 1; 578 579 // Run cron and check that the expected number of users received the notification. 580 $messages = $this->helper_run_cron_check_count($reply, $expected); 581 582 $seenauthor = false; 583 $seenrecipient = false; 584 foreach ($messages as $message) { 585 // They should both be from our user. 586 $this->assertEquals($author->id, $message->useridfrom); 587 588 if ($message->useridto == $author->id) { 589 $seenauthor = true; 590 } else if ($message->useridto = $recipient->id) { 591 $seenrecipient = true; 592 } 593 } 594 595 // Check we only saw one user. 596 $this->assertFalse($seenauthor); 597 $this->assertTrue($seenrecipient); 598 } 599 600 public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() { 601 $this->resetAfterTest(true); 602 603 // Create a course, with a forum. 604 $course = $this->getDataGenerator()->create_course(); 605 606 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); 607 $forum = $this->getDataGenerator()->create_module('forum', $options); 608 609 // Create two users enrolled in the course as students. 610 list($author, $recipient) = $this->helper_create_users($course, 2); 611 612 // Post a discussion to the forum. 613 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 614 $this->helper_update_post_time($post, -90); 615 616 // Unsubscribe the 'author' user from the forum. 617 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum); 618 619 // Then re-subscribe them to the discussion. 620 \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion); 621 $this->helper_update_subscription_time($author, $discussion, -60); 622 623 // We expect just the user subscribed to the forum to receive this post at the moment as the discussion 624 // subscription time is after the post time. 625 $expected = 1; 626 627 // Run cron and check that the expected number of users received the notification. 628 $messages = $this->helper_run_cron_check_count($post, $expected); 629 630 $seenauthor = false; 631 $seenrecipient = false; 632 foreach ($messages as $message) { 633 // They should both be from our user. 634 $this->assertEquals($author->id, $message->useridfrom); 635 636 if ($message->useridto == $author->id) { 637 $seenauthor = true; 638 } else if ($message->useridto = $recipient->id) { 639 $seenrecipient = true; 640 } 641 } 642 643 // Check we only saw one user. 644 $this->assertFalse($seenauthor); 645 $this->assertTrue($seenrecipient); 646 647 // Now post a reply to the original post. 648 $reply = $this->helper_post_to_discussion($forum, $discussion, $author); 649 $this->helper_update_post_time($reply, -30); 650 651 // We expect two users to receive this post. 652 $expected = 2; 653 654 // Run cron and check that the expected number of users received the notification. 655 $messages = $this->helper_run_cron_check_count($reply, $expected); 656 657 $seenauthor = false; 658 $seenrecipient = false; 659 foreach ($messages as $message) { 660 // They should both be from our user. 661 $this->assertEquals($author->id, $message->useridfrom); 662 663 if ($message->useridto == $author->id) { 664 $seenauthor = true; 665 } else if ($message->useridto = $recipient->id) { 666 $seenrecipient = true; 667 } 668 } 669 670 // Check we saw both users. 671 $this->assertTrue($seenauthor); 672 $this->assertTrue($seenrecipient); 673 } 674 675 public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() { 676 $this->resetAfterTest(true); 677 678 // Create a course, with a forum. 679 $course = $this->getDataGenerator()->create_course(); 680 681 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 682 $forum = $this->getDataGenerator()->create_module('forum', $options); 683 684 // Create two users enrolled in the course as students. 685 list($author, $recipient) = $this->helper_create_users($course, 2); 686 687 // Post a discussion to the forum. 688 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 689 690 // Unsubscribe the 'recipient' user from the discussion. 691 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum); 692 693 // Then unsubscribe them from the discussion. 694 \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion); 695 696 // We don't expect any users to receive this post. 697 $expected = 0; 698 699 // Run cron and check that the expected number of users received the notification. 700 $messages = $this->helper_run_cron_check_count($post, $expected); 701 } 702 703 /** 704 * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after 705 * they subscribed to the discussion. 706 */ 707 public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() { 708 $this->resetAfterTest(true); 709 710 // Create a course, with a forum. 711 $course = $this->getDataGenerator()->create_course(); 712 713 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); 714 $forum = $this->getDataGenerator()->create_module('forum', $options); 715 716 $expectedmessages = array(); 717 718 // Create a user enrolled in the course as a student. 719 list($author) = $this->helper_create_users($course, 1); 720 721 // Post a discussion to the forum. 722 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 723 $this->helper_update_post_time($post, -90); 724 725 $expectedmessages[] = array( 726 'id' => $post->id, 727 'subject' => $post->subject, 728 'count' => 0, 729 ); 730 731 // Then subscribe the user to the discussion. 732 $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion)); 733 $this->helper_update_subscription_time($author, $discussion, -60); 734 735 // Then post a reply to the first discussion. 736 $reply = $this->helper_post_to_discussion($forum, $discussion, $author); 737 $this->helper_update_post_time($reply, -30); 738 739 $expectedmessages[] = array( 740 'id' => $reply->id, 741 'subject' => $reply->subject, 742 'count' => 1, 743 ); 744 745 $expectedcount = 1; 746 747 // Run cron and check that the expected number of users received the notification. 748 $messages = $this->helper_run_cron_check_counts($expectedmessages, $expectedcount); 749 } 750 751 public function test_forum_message_inbound_multiple_posts() { 752 $this->resetAfterTest(true); 753 754 // Create a course, with a forum. 755 $course = $this->getDataGenerator()->create_course(); 756 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); 757 $forum = $this->getDataGenerator()->create_module('forum', $options); 758 759 // Create a user enrolled in the course as a student. 760 list($author) = $this->helper_create_users($course, 1); 761 762 $expectedmessages = array(); 763 764 // Post a discussion to the forum. 765 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 766 $this->helper_update_post_time($post, -90); 767 768 $expectedmessages[] = array( 769 'id' => $post->id, 770 'subject' => $post->subject, 771 'count' => 0, 772 ); 773 774 // Then post a reply to the first discussion. 775 $reply = $this->helper_post_to_discussion($forum, $discussion, $author); 776 $this->helper_update_post_time($reply, -60); 777 778 $expectedmessages[] = array( 779 'id' => $reply->id, 780 'subject' => $reply->subject, 781 'count' => 1, 782 ); 783 784 $expectedcount = 2; 785 786 // Ensure that messageinbound is enabled and configured for the forum handler. 787 $this->helper_spoof_message_inbound_setup(); 788 789 $author->emailstop = '0'; 790 set_user_preference('message_provider_mod_forum_posts_loggedoff', 'email', $author); 791 set_user_preference('message_provider_mod_forum_posts_loggedin', 'email', $author); 792 793 // Run cron and check that the expected number of users received the notification. 794 // Clear the mailsink, and close the messagesink. 795 $this->helper->mailsink->clear(); 796 $this->helper->messagesink->close(); 797 798 // Cron daily uses mtrace, turn on buffering to silence output. 799 foreach ($expectedmessages as $post) { 800 $this->expectOutputRegex("/{$post['count']} users were sent post {$post['id']}, '{$post['subject']}'/"); 801 } 802 803 forum_cron(); 804 $messages = $this->helper->mailsink->get_messages(); 805 806 // There should be the expected number of messages. 807 $this->assertEquals($expectedcount, count($messages)); 808 809 foreach ($messages as $message) { 810 $this->assertRegExp('/Reply-To: moodlemoodle123\+[^@]*@example.com/', $message->header); 811 } 812 } 813 814 public function test_long_subject() { 815 $this->resetAfterTest(true); 816 817 // Create a course, with a forum. 818 $course = $this->getDataGenerator()->create_course(); 819 820 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); 821 $forum = $this->getDataGenerator()->create_module('forum', $options); 822 823 // Create a user enrolled in the course as student. 824 list($author) = $this->helper_create_users($course, 1); 825 826 // Post a discussion to the forum. 827 $subject = 'This is the very long forum post subject that somebody was very kind of leaving, it is intended to check if long subject comes in mail correctly. Thank you.'; 828 $a = (object)array('courseshortname' => $course->shortname, 'forumname' => $forum->name, 'subject' => $subject); 829 $expectedsubject = get_string('postmailsubject', 'forum', $a); 830 list($discussion, $post) = $this->helper_post_to_forum($forum, $author, array('name' => $subject)); 831 832 // Run cron and check that the expected number of users received the notification. 833 $messages = $this->helper_run_cron_check_count($post, 1); 834 $message = reset($messages); 835 $this->assertEquals($author->id, $message->useridfrom); 836 $this->assertEquals($expectedsubject, $message->subject); 837 } 838 839 /** 840 * Test inital email and reply email subjects 841 */ 842 public function test_subjects() { 843 $this->resetAfterTest(true); 844 845 $course = $this->getDataGenerator()->create_course(); 846 847 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); 848 $forum = $this->getDataGenerator()->create_module('forum', $options); 849 850 list($author) = $this->helper_create_users($course, 1); 851 list($commenter) = $this->helper_create_users($course, 1); 852 853 $strre = get_string('re', 'forum'); 854 855 // New posts should not have Re: in the subject. 856 list($discussion, $post) = $this->helper_post_to_forum($forum, $author); 857 $messages = $this->helper_run_cron_check_count($post, 2); 858 $this->assertNotContains($strre, $messages[0]->subject); 859 860 // Replies should have Re: in the subject. 861 $reply = $this->helper_post_to_discussion($forum, $discussion, $commenter); 862 $messages = $this->helper_run_cron_check_count($reply, 2); 863 $this->assertContains($strre, $messages[0]->subject); 864 } 865 866 /** 867 * dataProvider for test_forum_post_email_templates(). 868 */ 869 public function forum_post_email_templates_provider() { 870 // Base information, we'll build variations based on it. 871 $base = array( 872 'user' => array('firstname' => 'Love', 'lastname' => 'Moodle', 'mailformat' => 0, 'maildigest' => 0), 873 'course' => array('shortname' => '101', 'fullname' => 'Moodle 101'), 874 'forums' => array( 875 array( 876 'name' => 'Moodle Forum', 877 'forumposts' => array( 878 array( 879 'name' => 'Hello Moodle', 880 'message' => 'Welcome to Moodle', 881 'messageformat' => FORMAT_MOODLE, 882 'attachments' => array( 883 array( 884 'filename' => 'example.txt', 885 'filecontents' => 'Basic information about the course' 886 ), 887 ), 888 ), 889 ), 890 ), 891 ), 892 'expectations' => array( 893 array( 894 'subject' => '.*101.*Hello', 895 'contents' => array( 896 '~{$a', 897 '~&(amp|lt|gt|quot|\#039);(?!course)', 898 'Attachment example.txt:\n' . 899 'http://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt\n', 900 'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1' 901 ), 902 ), 903 ), 904 ); 905 906 // Build the text cases. 907 $textcases = array('Text mail without ampersands, quotes or lt/gt' => array('data' => $base)); 908 909 // Single and double quotes everywhere. 910 $newcase = $base; 911 $newcase['user']['lastname'] = 'Moodle\'"'; 912 $newcase['course']['shortname'] = '101\'"'; 913 $newcase['forums'][0]['name'] = 'Moodle Forum\'"'; 914 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'"'; 915 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'"'; 916 $newcase['expectations'][0]['contents'] = array( 917 'Attachment example.txt:', '~{\$a', '~&(quot|\#039);', 'Love Moodle\'', '101\'', 'Moodle Forum\'"', 918 'Hello Moodle\'"', 'Welcome to Moodle\'"'); 919 $textcases['Text mail with quotes everywhere'] = array('data' => $newcase); 920 921 // Lt and gt everywhere. This case is completely borked because format_string() 922 // strips tags with $CFG->formatstringstriptags and also escapes < and > (correct 923 // for web presentation but not for text email). See MDL-19829. 924 $newcase = $base; 925 $newcase['user']['lastname'] = 'Moodle>'; 926 $newcase['course']['shortname'] = '101>'; 927 $newcase['forums'][0]['name'] = 'Moodle Forum>'; 928 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle>'; 929 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle>'; 930 $newcase['expectations'][0]['contents'] = array( 931 'Attachment example.txt:', '~{\$a', '~&gt;', 'Love Moodle>', '101>', 'Moodle Forum>', 932 'Hello Moodle>', 'Welcome to Moodle>'); 933 $textcases['Text mail with gt and lt everywhere'] = array('data' => $newcase); 934 935 // Ampersands everywhere. This case is completely borked because format_string() 936 // escapes ampersands (correct for web presentation but not for text email). See MDL-19829. 937 $newcase = $base; 938 $newcase['user']['lastname'] = 'Moodle&'; 939 $newcase['course']['shortname'] = '101&'; 940 $newcase['forums'][0]['name'] = 'Moodle Forum&'; 941 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle&'; 942 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle&'; 943 $newcase['expectations'][0]['contents'] = array( 944 'Attachment example.txt:', '~{\$a', '~&amp;', 'Love Moodle&', '101&', 'Moodle Forum&', 945 'Hello Moodle&', 'Welcome to Moodle&'); 946 $textcases['Text mail with ampersands everywhere'] = array('data' => $newcase); 947 948 // Text+image message i.e. @@PLUGINFILE@@ token handling. 949 $newcase = $base; 950 $newcase['forums'][0]['forumposts'][0]['name'] = 'Text and image'; 951 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle, ' 952 .'@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png !'; 953 $newcase['expectations'][0]['subject'] = '.*101.*Text and image'; 954 $newcase['expectations'][0]['contents'] = array( 955 '~{$a', 956 '~&(amp|lt|gt|quot|\#039);(?!course)', 957 'Attachment example.txt:\n' . 958 'http://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt\n', 959 'Text and image', 'Moodle Forum', 960 'Welcome to Moodle, *\n.*' 961 .'http://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/' 962 .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png *\n.*!', 963 'Love Moodle', '1\d1'); 964 $textcases['Text mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase); 965 966 // Now the html cases. 967 $htmlcases = array(); 968 969 // New base for html cases, no quotes, lts, gts or ampersands. 970 $htmlbase = $base; 971 $htmlbase['user']['mailformat'] = 1; 972 $htmlbase['expectations'][0]['contents'] = array( 973 '~{\$a', 974 '~&(amp|lt|gt|quot|\#039);(?!course)', 975 '<div class="attachments">( *\n *)?<a href', 976 '<div class="subject">\n.*Hello Moodle', '>Moodle Forum', '>Welcome.*Moodle', '>Love Moodle', '>1\d1'); 977 $htmlcases['HTML mail without ampersands, quotes or lt/gt'] = array('data' => $htmlbase); 978 979 // Single and double quotes, lt and gt, ampersands everywhere. 980 $newcase = $htmlbase; 981 $newcase['user']['lastname'] = 'Moodle\'">&'; 982 $newcase['course']['shortname'] = '101\'">&'; 983 $newcase['forums'][0]['name'] = 'Moodle Forum\'">&'; 984 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'">&'; 985 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'">&'; 986 $newcase['expectations'][0]['contents'] = array( 987 '~{\$a', 988 '~&(amp|lt|gt|quot|\#039);', 989 '<div class="attachments">( *\n *)?<a href', 990 '<div class="subject">\n.*Hello Moodle\'">&', '>Moodle Forum\'">&', 991 '>Welcome.*Moodle\'">&', '>Love Moodle&\#039;">&', '>101\'">&'); 992 $htmlcases['HTML mail with quotes, gt, lt and ampersand everywhere'] = array('data' => $newcase); 993 994 // Text+image message i.e. @@PLUGINFILE@@ token handling. 995 $newcase = $htmlbase; 996 $newcase['forums'][0]['forumposts'][0]['name'] = 'HTML text and image'; 997 $newcase['forums'][0]['forumposts'][0]['message'] = '<p>Welcome to Moodle, ' 998 .'<img src="@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png"' 999 .' alt="" width="200" height="393" class="img-responsive" />!</p>'; 1000 $newcase['expectations'][0]['subject'] = '.*101.*HTML text and image'; 1001 $newcase['expectations'][0]['contents'] = array( 1002 '~{\$a', 1003 '~&(amp|lt|gt|quot|\#039);(?!course)', 1004 '<div class="attachments">( *\n *)?<a href', 1005 '<div class="subject">\n.*HTML text and image', '>Moodle Forum', 1006 '<p>Welcome to Moodle, ' 1007 .'<img src="http://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/' 1008 .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png"' 1009 .' alt="" width="200" height="393" class="img-responsive" />!</p>', 1010 '>Love Moodle', '>1\d1'); 1011 $htmlcases['HTML mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase); 1012 1013 return $textcases + $htmlcases; 1014 } 1015 1016 /** 1017 * Verify forum emails body using templates to generate the expected results. 1018 * 1019 * @dataProvider forum_post_email_templates_provider 1020 * @param array $data provider samples. 1021 */ 1022 public function test_forum_post_email_templates($data) { 1023 global $DB; 1024 1025 $this->resetAfterTest(); 1026 1027 // Create the course, with the specified options. 1028 $options = array(); 1029 foreach ($data['course'] as $option => $value) { 1030 $options[$option] = $value; 1031 } 1032 $course = $this->getDataGenerator()->create_course($options); 1033 1034 // Create the user, with the specified options and enrol in the course. 1035 $options = array(); 1036 foreach ($data['user'] as $option => $value) { 1037 $options[$option] = $value; 1038 } 1039 $user = $this->getDataGenerator()->create_user($options); 1040 $this->getDataGenerator()->enrol_user($user->id, $course->id); 1041 1042 // Create forums, always force susbscribed (for easy), with the specified options. 1043 $posts = array(); 1044 foreach ($data['forums'] as $dataforum) { 1045 $forumposts = isset($dataforum['forumposts']) ? $dataforum['forumposts'] : array(); 1046 unset($dataforum['forumposts']); 1047 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); 1048 foreach ($dataforum as $option => $value) { 1049 $options[$option] = $value; 1050 } 1051 $forum = $this->getDataGenerator()->create_module('forum', $options); 1052 1053 // Create posts, always for immediate delivery (for easy), with the specified options. 1054 foreach ($forumposts as $forumpost) { 1055 $attachments = isset($forumpost['attachments']) ? $forumpost['attachments'] : array(); 1056 unset($forumpost['attachments']); 1057 $postoptions = array('course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id, 1058 'mailnow' => 1, 'attachment' => !empty($attachments)); 1059 foreach ($forumpost as $option => $value) { 1060 $postoptions[$option] = $value; 1061 } 1062 list($discussion, $post) = $this->helper_post_to_forum($forum, $user, $postoptions); 1063 $posts[$post->subject] = $post; // Need this to verify cron output. 1064 1065 // Add the attachments to the post. 1066 if ($attachments) { 1067 $fs = get_file_storage(); 1068 foreach ($attachments as $attachment) { 1069 $filerecord = array( 1070 'contextid' => context_module::instance($forum->cmid)->id, 1071 'component' => 'mod_forum', 1072 'filearea' => 'attachment', 1073 'itemid' => $post->id, 1074 'filepath' => '/', 1075 'filename' => $attachment['filename'] 1076 ); 1077 $fs->create_file_from_string($filerecord, $attachment['filecontents']); 1078 } 1079 $DB->set_field('forum_posts', 'attachment', '1', array('id' => $post->id)); 1080 } 1081 } 1082 } 1083 1084 // Clear the mailsink and close the messagesink. 1085 // (surely setup should provide us this cleared but...) 1086 $this->helper->mailsink->clear(); 1087 $this->helper->messagesink->close(); 1088 1089 // Capture and silence cron output, verifying contents. 1090 foreach ($posts as $post) { 1091 $this->expectOutputRegex("/1 users were sent post {$post->id}, '{$post->subject}'/"); 1092 } 1093 forum_cron(); // It's really annoying that we have to run cron to test this. 1094 1095 // Get the mails. 1096 $mails = $this->helper->mailsink->get_messages(); 1097 1098 // Start testing the expectations. 1099 $expectations = $data['expectations']; 1100 1101 // Assert the number is the expected. 1102 $this->assertSame(count($expectations), count($mails)); 1103 1104 // Start processing mails, first localizing its expectations, then checking them. 1105 foreach ($mails as $mail) { 1106 // Find the corresponding expectation. 1107 $foundexpectation = null; 1108 foreach ($expectations as $key => $expectation) { 1109 // All expectations must have a subject for matching. 1110 if (!isset($expectation['subject'])) { 1111 $this->fail('Provider expectation missing mandatory subject'); 1112 } 1113 if (preg_match('!' . $expectation['subject'] . '!', $mail->subject)) { 1114 // If we already had found the expectation, there are non-unique subjects. Fail. 1115 if (isset($foundexpectation)) { 1116 $this->fail('Multiple expectations found (by subject matching). Please make them unique.'); 1117 } 1118 $foundexpectation = $expectation; 1119 unset($expectations[$key]); 1120 } 1121 } 1122 // Arrived here, we should have found the expectations. 1123 $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail'); 1124 1125 // If we have found the expectation and have contents to match, let's do it. 1126 if (isset($foundexpectation) and isset($foundexpectation['contents'])) { 1127 $mail->body = quoted_printable_decode($mail->body); 1128 if (!is_array($foundexpectation['contents'])) { // Accept both string and array. 1129 $foundexpectation['contents'] = array($foundexpectation['contents']); 1130 } 1131 foreach ($foundexpectation['contents'] as $content) { 1132 if (strpos($content, '~') !== 0) { 1133 $this->assertRegexp('#' . $content . '#m', $mail->body); 1134 } else { 1135 preg_match('#' . substr($content, 1) . '#m', $mail->body, $matches); 1136 $this->assertNotRegexp('#' . substr($content, 1) . '#m', $mail->body); 1137 } 1138 } 1139 } 1140 } 1141 // Finished, there should not be remaining expectations. 1142 $this->assertCount(0, $expectations); 1143 } 1144 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |