MDL-65888 forum: Do not send notifications to unconfirmed users
[moodle.git] / mod / forum / tests / mail_test.php
blob7688acbf1dfcad7a14010e5c52fcb35bd6abe3da
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.
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/>.
17 /**
18 * The forum module mail generation tests.
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
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/mod/forum/lib.php');
30 require_once(__DIR__ . '/cron_trait.php');
31 require_once(__DIR__ . '/generator_trait.php');
33 class mod_forum_mail_testcase extends advanced_testcase {
34 // Make use of the cron tester trait.
35 use mod_forum_tests_cron_trait;
37 // Make use of the test generator trait.
38 use mod_forum_tests_generator_trait;
40 /**
41 * @var \phpunit_message_sink
43 protected $messagesink;
45 /**
46 * @var \phpunit_mailer_sink
48 protected $mailsink;
50 public function setUp() {
51 global $CFG;
53 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
54 // tests using these functions.
55 \mod_forum\subscriptions::reset_forum_cache();
56 \mod_forum\subscriptions::reset_discussion_cache();
58 // Messaging is not compatible with transactions...
59 $this->preventResetByRollback();
61 // Catch all messages.
62 $this->messagesink = $this->redirectMessages();
63 $this->mailsink = $this->redirectEmails();
65 // Forcibly reduce the maxeditingtime to a second in the past to
66 // ensure that messages are sent out.
67 $CFG->maxeditingtime = -1;
70 public function tearDown() {
71 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
72 // tests using these functions.
73 \mod_forum\subscriptions::reset_forum_cache();
75 $this->messagesink->clear();
76 $this->messagesink->close();
77 unset($this->messagesink);
79 $this->mailsink->clear();
80 $this->mailsink->close();
81 unset($this->mailsink);
84 /**
85 * Perform message inbound setup for the mod_forum reply handler.
87 protected function helper_spoof_message_inbound_setup() {
88 global $CFG, $DB;
89 // Setup the default Inbound Message mailbox settings.
90 $CFG->messageinbound_domain = 'example.com';
91 $CFG->messageinbound_enabled = true;
93 // Must be no longer than 15 characters.
94 $CFG->messageinbound_mailbox = 'moodlemoodle123';
96 $record = $DB->get_record('messageinbound_handlers', array('classname' => '\mod_forum\message\inbound\reply_handler'));
97 $record->enabled = true;
98 $record->id = $DB->update_record('messageinbound_handlers', $record);
101 public function test_cron_message_includes_courseid() {
102 $this->resetAfterTest(true);
104 // Create a course, with a forum.
105 $course = $this->getDataGenerator()->create_course();
107 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
108 $forum = $this->getDataGenerator()->create_module('forum', $options);
110 // Create two users enrolled in the course as students.
111 list($author, $recipient) = $this->helper_create_users($course, 2);
113 // Post a discussion to the forum.
114 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
116 $expect = [
117 'author' => (object) [
118 'userid' => $author->id,
119 'messages' => 1,
121 'recipient' => (object) [
122 'userid' => $recipient->id,
123 'messages' => 1,
126 $this->queue_tasks_and_assert($expect);
128 $this->messagesink->close();
129 $this->eventsink = $this->redirectEvents();
130 $this->send_notifications_and_assert($author, [$post]);
131 $events = $this->eventsink->get_events();
132 $event = reset($events);
134 $this->assertEquals($course->id, $event->other['courseid']);
136 $this->send_notifications_and_assert($recipient, [$post]);
139 public function test_forced_subscription() {
140 global $DB;
141 $this->resetAfterTest(true);
143 // Create a course, with a forum.
144 $course = $this->getDataGenerator()->create_course();
146 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
147 $forum = $this->getDataGenerator()->create_module('forum', $options);
149 // Create users enrolled in the course as students.
150 list($author, $recipient, $unconfirmed, $deleted) = $this->helper_create_users($course, 4);
152 // Make the third user unconfirmed (thence inactive) to make sure it does not break the notifications.
153 $DB->set_field('user', 'confirmed', 0, ['id' => $unconfirmed->id]);
155 // Mark the fourth user as deleted to make sure it does not break the notifications.
156 $DB->set_field('user', 'deleted', 1, ['id' => $deleted->id]);
158 // Post a discussion to the forum.
159 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
161 $expect = [
162 (object) [
163 'userid' => $author->id,
164 'messages' => 1,
166 (object) [
167 'userid' => $recipient->id,
168 'messages' => 1,
170 (object) [
171 'userid' => $unconfirmed->id,
172 'messages' => 0,
174 (object) [
175 'userid' => $deleted->id,
176 'messages' => 0,
179 $this->queue_tasks_and_assert($expect);
181 $this->send_notifications_and_assert($author, [$post]);
182 $this->send_notifications_and_assert($recipient, [$post]);
183 $this->send_notifications_and_assert($unconfirmed, []);
184 $this->send_notifications_and_assert($deleted, []);
188 * Ensure that for a forum with subscription disabled that standard users will not receive posts.
190 public function test_subscription_disabled_standard_users() {
191 global $DB;
193 $this->resetAfterTest(true);
195 // Create a course, with a forum.
196 $course = $this->getDataGenerator()->create_course();
198 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
199 $forum = $this->getDataGenerator()->create_module('forum', $options);
201 // Create two users enrolled in the course as students.
202 list($author, $recipient) = $this->helper_create_users($course, 2);
204 // Post a discussion to the forum.
205 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
207 // Run cron and check that the expected number of users received the notification.
208 $expect = [
209 (object) [
210 'userid' => $author->id,
211 'messages' => 0,
213 (object) [
214 'userid' => $recipient->id,
215 'messages' => 0,
218 $this->queue_tasks_and_assert($expect);
220 $this->send_notifications_and_assert($author, []);
221 $this->send_notifications_and_assert($recipient, []);
225 * Ensure that for a forum with subscription disabled that a user subscribed to the forum will receive the post.
227 public function test_subscription_disabled_user_subscribed_forum() {
228 global $DB;
230 $this->resetAfterTest(true);
232 // Create a course, with a forum.
233 $course = $this->getDataGenerator()->create_course();
235 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
236 $forum = $this->getDataGenerator()->create_module('forum', $options);
238 // Create two users enrolled in the course as students.
239 list($author, $recipient) = $this->helper_create_users($course, 2);
241 // A user with the manageactivities capability within the course can subscribe.
242 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
243 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
245 // Suscribe the recipient only.
246 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
248 $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
249 'userid' => $recipient->id,
250 'forum' => $forum->id,
251 )));
253 // Post a discussion to the forum.
254 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
256 // Run cron and check that the expected number of users received the notification.
257 $expect = [
258 'author' => (object) [
259 'userid' => $author->id,
261 'recipient' => (object) [
262 'userid' => $recipient->id,
263 'messages' => 1,
266 $this->queue_tasks_and_assert($expect);
268 $this->send_notifications_and_assert($author, []);
269 $this->send_notifications_and_assert($recipient, [$post]);
273 * Ensure that for a forum with subscription disabled that a user subscribed to the discussion will receive the
274 * post.
276 public function test_subscription_disabled_user_subscribed_discussion() {
277 global $DB;
279 $this->resetAfterTest(true);
281 // Create a course, with a forum.
282 $course = $this->getDataGenerator()->create_course();
284 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
285 $forum = $this->getDataGenerator()->create_module('forum', $options);
287 // Create two users enrolled in the course as students.
288 list($author, $recipient) = $this->helper_create_users($course, 2);
290 // A user with the manageactivities capability within the course can subscribe.
291 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
292 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
294 // Run cron and check that the expected number of users received the notification.
295 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
297 // Subscribe the user to the discussion.
298 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
299 $this->helper_update_subscription_time($recipient, $discussion, -60);
301 // Run cron and check that the expected number of users received the notification.
302 $expect = [
303 'author' => (object) [
304 'userid' => $author->id,
306 'recipient' => (object) [
307 'userid' => $recipient->id,
308 'messages' => 1,
311 $this->queue_tasks_and_assert($expect);
313 $this->send_notifications_and_assert($author, []);
314 $this->send_notifications_and_assert($recipient, [$post]);
318 * Ensure that for a forum with automatic subscription that users receive posts.
320 public function test_automatic() {
321 $this->resetAfterTest(true);
323 // Create a course, with a forum.
324 $course = $this->getDataGenerator()->create_course();
326 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
327 $forum = $this->getDataGenerator()->create_module('forum', $options);
329 // Create two users enrolled in the course as students.
330 list($author, $recipient) = $this->helper_create_users($course, 2);
332 // Post a discussion to the forum.
333 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
335 $expect = [
336 (object) [
337 'userid' => $author->id,
338 'messages' => 1,
340 (object) [
341 'userid' => $recipient->id,
342 'messages' => 1,
345 $this->queue_tasks_and_assert($expect);
347 $this->send_notifications_and_assert($author, [$post]);
348 $this->send_notifications_and_assert($recipient, [$post]);
352 * Ensure that private replies are not sent to users with an automatic subscription unless they are an expected
353 * recipient.
355 public function test_automatic_with_private_reply() {
356 $this->resetAfterTest(true);
358 // Create a course, with a forum.
359 $course = $this->getDataGenerator()->create_course();
360 $forum = $this->getDataGenerator()->create_module('forum', [
361 'course' => $course->id,
362 'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
365 [$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
366 [$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher');
368 [$discussion, $post] = $this->helper_post_to_forum($forum, $student);
369 $reply = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
370 'privatereplyto' => $student->id,
373 // The private reply is queued to all messages as reply visibility may change between queueing, and sending.
374 $expect = [
375 (object) [
376 'userid' => $student->id,
377 'messages' => 2,
379 (object) [
380 'userid' => $otherstudent->id,
381 'messages' => 2,
383 (object) [
384 'userid' => $teacher->id,
385 'messages' => 2,
387 (object) [
388 'userid' => $otherteacher->id,
389 'messages' => 2,
392 $this->queue_tasks_and_assert($expect);
394 // The actual messages sent will respect private replies.
395 $this->send_notifications_and_assert($student, [$post, $reply]);
396 $this->send_notifications_and_assert($teacher, [$post, $reply]);
397 $this->send_notifications_and_assert($otherteacher, [$post, $reply]);
398 $this->send_notifications_and_assert($otherstudent, [$post]);
401 public function test_optional() {
402 $this->resetAfterTest(true);
404 // Create a course, with a forum.
405 $course = $this->getDataGenerator()->create_course();
407 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
408 $forum = $this->getDataGenerator()->create_module('forum', $options);
410 // Create two users enrolled in the course as students.
411 list($author, $recipient) = $this->helper_create_users($course, 2);
413 // Post a discussion to the forum.
414 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
416 $expect = [
417 (object) [
418 'userid' => $author->id,
419 'messages' => 0,
421 (object) [
422 'userid' => $recipient->id,
423 'messages' => 0,
426 $this->queue_tasks_and_assert($expect);
428 $this->send_notifications_and_assert($author, []);
429 $this->send_notifications_and_assert($recipient, []);
432 public function test_automatic_with_unsubscribed_user() {
433 $this->resetAfterTest(true);
435 // Create a course, with a forum.
436 $course = $this->getDataGenerator()->create_course();
438 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
439 $forum = $this->getDataGenerator()->create_module('forum', $options);
441 // Create two users enrolled in the course as students.
442 list($author, $recipient) = $this->helper_create_users($course, 2);
444 // Unsubscribe the 'author' user from the forum.
445 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
447 // Post a discussion to the forum.
448 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
450 $expect = [
451 (object) [
452 'userid' => $author->id,
453 'messages' => 0,
455 (object) [
456 'userid' => $recipient->id,
457 'messages' => 1,
460 $this->queue_tasks_and_assert($expect);
462 $this->send_notifications_and_assert($author, []);
463 $this->send_notifications_and_assert($recipient, [$post]);
466 public function test_optional_with_subscribed_user() {
467 $this->resetAfterTest(true);
469 // Create a course, with a forum.
470 $course = $this->getDataGenerator()->create_course();
472 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
473 $forum = $this->getDataGenerator()->create_module('forum', $options);
475 // Create two users enrolled in the course as students.
476 list($author, $recipient) = $this->helper_create_users($course, 2);
478 // Subscribe the 'recipient' user from the forum.
479 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
481 // Post a discussion to the forum.
482 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
484 $expect = [
485 (object) [
486 'userid' => $author->id,
487 'messages' => 0,
489 (object) [
490 'userid' => $recipient->id,
491 'messages' => 1,
494 $this->queue_tasks_and_assert($expect);
496 $this->send_notifications_and_assert($author, []);
497 $this->send_notifications_and_assert($recipient, [$post]);
500 public function test_automatic_with_unsubscribed_discussion() {
501 $this->resetAfterTest(true);
503 // Create a course, with a forum.
504 $course = $this->getDataGenerator()->create_course();
506 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
507 $forum = $this->getDataGenerator()->create_module('forum', $options);
509 // Create two users enrolled in the course as students.
510 list($author, $recipient) = $this->helper_create_users($course, 2);
512 // Post a discussion to the forum.
513 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
515 // Unsubscribe the 'author' user from the discussion.
516 \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
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));
521 $expect = [
522 (object) [
523 'userid' => $author->id,
524 'messages' => 0,
526 (object) [
527 'userid' => $recipient->id,
528 'messages' => 1,
531 $this->queue_tasks_and_assert($expect);
533 $this->send_notifications_and_assert($author, []);
534 $this->send_notifications_and_assert($recipient, [$post]);
537 public function test_optional_with_subscribed_discussion() {
538 $this->resetAfterTest(true);
540 // Create a course, with a forum.
541 $course = $this->getDataGenerator()->create_course();
543 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
544 $forum = $this->getDataGenerator()->create_module('forum', $options);
546 // Create two users enrolled in the course as students.
547 list($author, $recipient) = $this->helper_create_users($course, 2);
549 // Post a discussion to the forum.
550 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
551 $this->helper_update_post_time($post, -90);
553 // Subscribe the 'recipient' user to the discussion.
554 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
555 $this->helper_update_subscription_time($recipient, $discussion, -60);
557 // Initially we don't expect any user to receive this post as you cannot subscribe to a discussion until after
558 // you have read it.
559 $expect = [
560 (object) [
561 'userid' => $author->id,
562 'messages' => 0,
564 (object) [
565 'userid' => $recipient->id,
566 'messages' => 0,
569 $this->queue_tasks_and_assert($expect);
571 $this->send_notifications_and_assert($author, []);
572 $this->send_notifications_and_assert($recipient, []);
574 // Have a user reply to the discussion.
575 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
576 $this->helper_update_post_time($reply, -30);
578 // We expect only one user to receive this post.
579 $expect = [
580 (object) [
581 'userid' => $author->id,
582 'messages' => 0,
584 (object) [
585 'userid' => $recipient->id,
586 'messages' => 1,
589 $this->queue_tasks_and_assert($expect);
591 $this->send_notifications_and_assert($author, []);
592 $this->send_notifications_and_assert($recipient, [$reply]);
595 public function test_optional_with_subscribed_discussion_and_post() {
596 $this->resetAfterTest(true);
598 // Create a course, with a forum.
599 $course = $this->getDataGenerator()->create_course();
601 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
602 $forum = $this->getDataGenerator()->create_module('forum', $options);
604 // Create two users enrolled in the course as students.
605 list($author, $recipient) = $this->helper_create_users($course, 2);
607 // Post a discussion to the forum.
608 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
609 $this->helper_update_post_time($post, -90);
611 // Have a user reply to the discussion before we subscribed.
612 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
613 $this->helper_update_post_time($reply, -75);
615 // Subscribe the 'recipient' user to the discussion.
616 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
617 $this->helper_update_subscription_time($recipient, $discussion, -60);
619 // Have a user reply to the discussion.
620 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
621 $this->helper_update_post_time($reply, -30);
623 // We expect only one user to receive this post.
624 // The original post won't be received as it was written before the user subscribed.
625 $expect = [
626 (object) [
627 'userid' => $author->id,
628 'messages' => 0,
630 (object) [
631 'userid' => $recipient->id,
632 'messages' => 1,
635 $this->queue_tasks_and_assert($expect);
637 $this->send_notifications_and_assert($author, []);
638 $this->send_notifications_and_assert($recipient, [$reply]);
641 public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() {
642 $this->resetAfterTest(true);
644 // Create a course, with a forum.
645 $course = $this->getDataGenerator()->create_course();
647 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
648 $forum = $this->getDataGenerator()->create_module('forum', $options);
650 // Create two users enrolled in the course as students.
651 list($author, $recipient) = $this->helper_create_users($course, 2);
653 // Post a discussion to the forum.
654 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
655 $this->helper_update_post_time($post, -90);
657 // Unsubscribe the 'author' user from the forum.
658 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
660 // Then re-subscribe them to the discussion.
661 \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
662 $this->helper_update_subscription_time($author, $discussion, -60);
664 $expect = [
665 (object) [
666 'userid' => $author->id,
667 'messages' => 0,
669 (object) [
670 'userid' => $recipient->id,
671 'messages' => 1,
674 $this->queue_tasks_and_assert($expect);
676 $this->send_notifications_and_assert($author, []);
677 $this->send_notifications_and_assert($recipient, [$post]);
679 // Now post a reply to the original post.
680 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
681 $this->helper_update_post_time($reply, -30);
683 $expect = [
684 (object) [
685 'userid' => $author->id,
686 'messages' => 1,
688 (object) [
689 'userid' => $recipient->id,
690 'messages' => 1,
693 $this->queue_tasks_and_assert($expect);
695 $this->send_notifications_and_assert($author, [$reply]);
696 $this->send_notifications_and_assert($recipient, [$reply]);
699 public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() {
700 $this->resetAfterTest(true);
702 // Create a course, with a forum.
703 $course = $this->getDataGenerator()->create_course();
705 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
706 $forum = $this->getDataGenerator()->create_module('forum', $options);
708 // Create two users enrolled in the course as students.
709 list($author, $recipient) = $this->helper_create_users($course, 2);
711 // Post a discussion to the forum.
712 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
714 // Unsubscribe the 'recipient' user from the discussion.
715 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
717 // Then unsubscribe them from the discussion.
718 \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion);
720 // We don't expect any users to receive this post.
721 $expect = [
722 (object) [
723 'userid' => $author->id,
724 'messages' => 0,
726 (object) [
727 'userid' => $recipient->id,
728 'messages' => 0,
731 $this->queue_tasks_and_assert($expect);
733 $this->send_notifications_and_assert($author, []);
734 $this->send_notifications_and_assert($recipient, []);
738 * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after
739 * they subscribed to the discussion.
741 public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() {
742 $this->resetAfterTest(true);
744 // Create a course, with a forum.
745 $course = $this->getDataGenerator()->create_course();
747 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
748 $forum = $this->getDataGenerator()->create_module('forum', $options);
750 $expectedmessages = array();
752 // Create a user enrolled in the course as a student.
753 list($author) = $this->helper_create_users($course, 1);
755 // Post a discussion to the forum.
756 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
757 $this->helper_update_post_time($post, -90);
759 $expectedmessages[] = array(
760 'id' => $post->id,
761 'subject' => $post->subject,
762 'count' => 0,
765 // Then subscribe the user to the discussion.
766 $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
767 $this->helper_update_subscription_time($author, $discussion, -60);
769 // Then post a reply to the first discussion.
770 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
771 $this->helper_update_post_time($reply, -30);
773 $expect = [
774 (object) [
775 'userid' => $author->id,
776 'messages' => 1,
779 $this->queue_tasks_and_assert($expect);
781 $this->send_notifications_and_assert($author, [$reply]);
784 public function test_subscription_by_inactive_users() {
785 global $DB;
786 $this->resetAfterTest(true);
788 $course = $this->getDataGenerator()->create_course();
790 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
791 $forum = $this->getDataGenerator()->create_module('forum', $options);
793 // Create two users enrolled in the course as students.
794 list($author, $u1, $u2, $u3) = $this->helper_create_users($course, 4);
796 // Subscribe the three users to the forum.
797 \mod_forum\subscriptions::subscribe_user($u1->id, $forum);
798 \mod_forum\subscriptions::subscribe_user($u2->id, $forum);
799 \mod_forum\subscriptions::subscribe_user($u3->id, $forum);
801 // Make the first user inactive - suspended.
802 $DB->set_field('user', 'suspended', 1, ['id' => $u1->id]);
804 // Make the second user inactive - unable to log in.
805 $DB->set_field('user', 'auth', 'nologin', ['id' => $u2->id]);
807 // Post a discussion to the forum.
808 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
810 $expect = [
811 (object) [
812 'userid' => $u1->id,
813 'messages' => 0,
815 (object) [
816 'userid' => $u2->id,
817 'messages' => 0,
819 (object) [
820 'userid' => $u3->id,
821 'messages' => 1,
825 $this->queue_tasks_and_assert($expect);
826 $this->send_notifications_and_assert($u1, []);
827 $this->send_notifications_and_assert($u2, []);
828 $this->send_notifications_and_assert($u3, [$post]);
831 public function test_forum_message_inbound_multiple_posts() {
832 $this->resetAfterTest(true);
834 // Create a course, with a forum.
835 $course = $this->getDataGenerator()->create_course();
836 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
837 $forum = $this->getDataGenerator()->create_module('forum', $options);
839 // Create a user enrolled in the course as a student.
840 list($author) = $this->helper_create_users($course, 1);
842 $expectedmessages = array();
844 // Post a discussion to the forum.
845 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
846 $this->helper_update_post_time($post, -90);
848 $expectedmessages[] = (object) [
849 'id' => $post->id,
850 'subject' => $post->subject,
851 'count' => 0,
854 // Then post a reply to the first discussion.
855 $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
856 $this->helper_update_post_time($reply, -60);
858 $expectedmessages[] = (object) [
859 'id' => $reply->id,
860 'subject' => $reply->subject,
861 'count' => 1,
864 // Ensure that messageinbound is enabled and configured for the forum handler.
865 $this->helper_spoof_message_inbound_setup();
867 $author->emailstop = '0';
868 set_user_preference('message_provider_mod_forum_posts_loggedoff', 'email', $author);
869 set_user_preference('message_provider_mod_forum_posts_loggedin', 'email', $author);
871 // Run cron and check that the expected number of users received the notification.
872 // Clear the mailsink, and close the messagesink.
873 $this->mailsink->clear();
874 $this->messagesink->close();
876 $expect = [
877 'author' => (object) [
878 'userid' => $author->id,
879 'messages' => count($expectedmessages),
882 $this->queue_tasks_and_assert($expect);
884 $this->send_notifications_and_assert($author, $expectedmessages);
885 $messages = $this->mailsink->get_messages();
887 // There should be the expected number of messages.
888 $this->assertEquals(2, count($messages));
890 foreach ($messages as $message) {
891 $this->assertRegExp('/Reply-To: moodlemoodle123\+[^@]*@example.com/', $message->header);
895 public function test_long_subject() {
896 $this->resetAfterTest(true);
898 // Create a course, with a forum.
899 $course = $this->getDataGenerator()->create_course();
901 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
902 $forum = $this->getDataGenerator()->create_module('forum', $options);
904 // Create a user enrolled in the course as student.
905 list($author) = $this->helper_create_users($course, 1);
907 // Post a discussion to the forum.
908 $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.';
909 $a = (object)array('courseshortname' => $course->shortname, 'forumname' => $forum->name, 'subject' => $subject);
910 $expectedsubject = get_string('postmailsubject', 'forum', $a);
911 list($discussion, $post) = $this->helper_post_to_forum($forum, $author, array('name' => $subject));
913 // Run cron and check that the expected number of users received the notification.
914 $expect = [
915 'author' => (object) [
916 'userid' => $author->id,
917 'messages' => 1,
920 $this->queue_tasks_and_assert($expect);
922 $this->send_notifications_and_assert($author, [$post]);
923 $messages = $this->messagesink->get_messages();
924 $message = reset($messages);
925 $this->assertEquals($author->id, $message->useridfrom);
926 $this->assertEquals($expectedsubject, $message->subject);
930 * Test inital email and reply email subjects
932 public function test_subjects() {
933 $this->resetAfterTest(true);
935 $course = $this->getDataGenerator()->create_course();
937 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
938 $forum = $this->getDataGenerator()->create_module('forum', $options);
940 list($author) = $this->helper_create_users($course, 1);
941 list($commenter) = $this->helper_create_users($course, 1);
943 $strre = get_string('re', 'forum');
945 // New posts should not have Re: in the subject.
946 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
947 $expect = [
948 'author' => (object) [
949 'userid' => $author->id,
950 'messages' => 1,
952 'commenter' => (object) [
953 'userid' => $commenter->id,
954 'messages' => 1,
957 $this->queue_tasks_and_assert($expect);
959 $this->send_notifications_and_assert($author, [$post]);
960 $this->send_notifications_and_assert($commenter, [$post]);
961 $messages = $this->messagesink->get_messages();
962 $this->assertNotContains($strre, $messages[0]->subject);
963 $this->messagesink->clear();
965 // Replies should have Re: in the subject.
966 $reply = $this->helper_post_to_discussion($forum, $discussion, $commenter);
968 $expect = [
969 'author' => (object) [
970 'userid' => $author->id,
971 'messages' => 1,
973 'commenter' => (object) [
974 'userid' => $commenter->id,
975 'messages' => 1,
978 $this->queue_tasks_and_assert($expect);
980 $this->send_notifications_and_assert($commenter, [$reply]);
981 $this->send_notifications_and_assert($author, [$reply]);
982 $messages = $this->messagesink->get_messages();
983 $this->assertContains($strre, $messages[0]->subject);
984 $this->assertContains($strre, $messages[1]->subject);
988 * dataProvider for test_forum_post_email_templates().
990 public function forum_post_email_templates_provider() {
991 // Base information, we'll build variations based on it.
992 $base = array(
993 'user' => array('firstname' => 'Love', 'lastname' => 'Moodle', 'mailformat' => 0, 'maildigest' => 0),
994 'course' => array('shortname' => '101', 'fullname' => 'Moodle 101'),
995 'forums' => array(
996 array(
997 'name' => 'Moodle Forum',
998 'forumposts' => array(
999 array(
1000 'name' => 'Hello Moodle',
1001 'message' => 'Welcome to Moodle',
1002 'messageformat' => FORMAT_MOODLE,
1003 'attachments' => array(
1004 array(
1005 'filename' => 'example.txt',
1006 'filecontents' => 'Basic information about the course'
1013 'expectations' => array(
1014 array(
1015 'subject' => '.*101.*Hello',
1016 'contents' => array(
1017 '~{$a',
1018 '~&(amp|lt|gt|quot|\#039);(?!course)',
1019 'Attachment example.txt:' . PHP_EOL .
1020 'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' . PHP_EOL,
1021 'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1'
1027 // Build the text cases.
1028 $textcases = array('Text mail without ampersands, quotes or lt/gt' => array('data' => $base));
1030 // Single and double quotes everywhere.
1031 $newcase = $base;
1032 $newcase['user']['lastname'] = 'Moodle\'"';
1033 $newcase['course']['shortname'] = '101\'"';
1034 $newcase['forums'][0]['name'] = 'Moodle Forum\'"';
1035 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'"';
1036 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'"';
1037 $newcase['expectations'][0]['contents'] = array(
1038 'Attachment example.txt:', '~{\$a', '~&amp;(quot|\#039);', 'Love Moodle\'', '101\'', 'Moodle Forum\'"',
1039 'Hello Moodle\'"', 'Welcome to Moodle\'"');
1040 $textcases['Text mail with quotes everywhere'] = array('data' => $newcase);
1042 // Lt and gt everywhere. This case is completely borked because format_string()
1043 // strips tags with $CFG->formatstringstriptags and also escapes < and > (correct
1044 // for web presentation but not for text email). See MDL-19829.
1045 $newcase = $base;
1046 $newcase['user']['lastname'] = 'Moodle>';
1047 $newcase['course']['shortname'] = '101>';
1048 $newcase['forums'][0]['name'] = 'Moodle Forum>';
1049 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle>';
1050 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle>';
1051 $newcase['expectations'][0]['contents'] = array(
1052 'Attachment example.txt:', '~{\$a', '~&amp;gt;', 'Love Moodle>', '101>', 'Moodle Forum>',
1053 'Hello Moodle>', 'Welcome to Moodle>');
1054 $textcases['Text mail with gt and lt everywhere'] = array('data' => $newcase);
1056 // Ampersands everywhere. This case is completely borked because format_string()
1057 // escapes ampersands (correct for web presentation but not for text email). See MDL-19829.
1058 $newcase = $base;
1059 $newcase['user']['lastname'] = 'Moodle&';
1060 $newcase['course']['shortname'] = '101&';
1061 $newcase['forums'][0]['name'] = 'Moodle Forum&';
1062 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle&';
1063 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle&';
1064 $newcase['expectations'][0]['contents'] = array(
1065 'Attachment example.txt:', '~{\$a', '~&amp;amp;', 'Love Moodle&', '101&', 'Moodle Forum&',
1066 'Hello Moodle&', 'Welcome to Moodle&');
1067 $textcases['Text mail with ampersands everywhere'] = array('data' => $newcase);
1069 // Text+image message i.e. @@PLUGINFILE@@ token handling.
1070 $newcase = $base;
1071 $newcase['forums'][0]['forumposts'][0]['name'] = 'Text and image';
1072 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle, '
1073 .'@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png !';
1074 $newcase['expectations'][0]['subject'] = '.*101.*Text and image';
1075 $newcase['expectations'][0]['contents'] = array(
1076 '~{$a',
1077 '~&(amp|lt|gt|quot|\#039);(?!course)',
1078 'Attachment example.txt:' . PHP_EOL .
1079 'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' . PHP_EOL ,
1080 'Text and image', 'Moodle Forum',
1081 'Welcome to Moodle, *' . PHP_EOL . '.*'
1082 .'https://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/'
1083 .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png *' . PHP_EOL . '.*!',
1084 'Love Moodle', '1\d1');
1085 $textcases['Text mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
1087 // Now the html cases.
1088 $htmlcases = array();
1090 // New base for html cases, no quotes, lts, gts or ampersands.
1091 $htmlbase = $base;
1092 $htmlbase['user']['mailformat'] = 1;
1093 $htmlbase['expectations'][0]['contents'] = array(
1094 '~{\$a',
1095 '~&(amp|lt|gt|quot|\#039);(?!course)',
1096 '<div class="attachments">( *\n *)?<a href',
1097 '<div class="subject">\n.*Hello Moodle', '>Moodle Forum', '>Welcome.*Moodle', '>Love Moodle', '>1\d1');
1098 $htmlcases['HTML mail without ampersands, quotes or lt/gt'] = array('data' => $htmlbase);
1100 // Single and double quotes, lt and gt, ampersands everywhere.
1101 $newcase = $htmlbase;
1102 $newcase['user']['lastname'] = 'Moodle\'">&';
1103 $newcase['course']['shortname'] = '101\'">&';
1104 $newcase['forums'][0]['name'] = 'Moodle Forum\'">&';
1105 $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'">&';
1106 $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'">&';
1107 $newcase['expectations'][0]['contents'] = array(
1108 '~{\$a',
1109 '~&amp;(amp|lt|gt|quot|\#039);',
1110 '<div class="attachments">( *\n *)?<a href',
1111 '<div class="subject">\n.*Hello Moodle\'"&gt;&amp;', '>Moodle Forum\'"&gt;&amp;',
1112 '>Welcome.*Moodle\'"&gt;&amp;', '>Love Moodle&\#039;&quot;&gt;&amp;', '>101\'"&gt;&amp');
1113 $htmlcases['HTML mail with quotes, gt, lt and ampersand everywhere'] = array('data' => $newcase);
1115 // Text+image message i.e. @@PLUGINFILE@@ token handling.
1116 $newcase = $htmlbase;
1117 $newcase['forums'][0]['forumposts'][0]['name'] = 'HTML text and image';
1118 $newcase['forums'][0]['forumposts'][0]['message'] = '<p>Welcome to Moodle, '
1119 .'<img src="@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png"'
1120 .' alt="" width="200" height="393" class="img-responsive" />!</p>';
1121 $newcase['expectations'][0]['subject'] = '.*101.*HTML text and image';
1122 $newcase['expectations'][0]['contents'] = array(
1123 '~{\$a',
1124 '~&(amp|lt|gt|quot|\#039);(?!course)',
1125 '<div class="attachments">( *\n *)?<a href',
1126 '<div class="subject">\n.*HTML text and image', '>Moodle Forum',
1127 '<p>Welcome to Moodle, '
1128 .'<img src="https://www.example.com/moodle/tokenpluginfile.php/[^/]*/\d+/mod_forum/post/\d+/'
1129 .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png"'
1130 .' alt="" width="200" height="393" class="img-responsive" />!</p>',
1131 '>Love Moodle', '>1\d1');
1132 $htmlcases['HTML mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
1134 return $textcases + $htmlcases;
1138 * Verify forum emails body using templates to generate the expected results.
1140 * @dataProvider forum_post_email_templates_provider
1141 * @param array $data provider samples.
1143 public function test_forum_post_email_templates($data) {
1144 global $DB;
1146 $this->resetAfterTest();
1148 // Create the course, with the specified options.
1149 $options = array();
1150 foreach ($data['course'] as $option => $value) {
1151 $options[$option] = $value;
1153 $course = $this->getDataGenerator()->create_course($options);
1155 // Create the user, with the specified options and enrol in the course.
1156 $options = array();
1157 foreach ($data['user'] as $option => $value) {
1158 $options[$option] = $value;
1160 $user = $this->getDataGenerator()->create_user($options);
1161 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1163 // Create forums, always force susbscribed (for easy), with the specified options.
1164 $posts = array();
1165 foreach ($data['forums'] as $dataforum) {
1166 $forumposts = isset($dataforum['forumposts']) ? $dataforum['forumposts'] : array();
1167 unset($dataforum['forumposts']);
1168 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1169 foreach ($dataforum as $option => $value) {
1170 $options[$option] = $value;
1172 $forum = $this->getDataGenerator()->create_module('forum', $options);
1174 // Create posts, always for immediate delivery (for easy), with the specified options.
1175 foreach ($forumposts as $forumpost) {
1176 $attachments = isset($forumpost['attachments']) ? $forumpost['attachments'] : array();
1177 unset($forumpost['attachments']);
1178 $postoptions = array('course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id,
1179 'mailnow' => 1, 'attachment' => !empty($attachments));
1180 foreach ($forumpost as $option => $value) {
1181 $postoptions[$option] = $value;
1183 list($discussion, $post) = $this->helper_post_to_forum($forum, $user, $postoptions);
1184 $posts[$post->subject] = $post; // Need this to verify cron output.
1186 // Add the attachments to the post.
1187 if ($attachments) {
1188 $fs = get_file_storage();
1189 foreach ($attachments as $attachment) {
1190 $filerecord = array(
1191 'contextid' => context_module::instance($forum->cmid)->id,
1192 'component' => 'mod_forum',
1193 'filearea' => 'attachment',
1194 'itemid' => $post->id,
1195 'filepath' => '/',
1196 'filename' => $attachment['filename']
1198 $fs->create_file_from_string($filerecord, $attachment['filecontents']);
1200 $DB->set_field('forum_posts', 'attachment', '1', array('id' => $post->id));
1205 // Clear the mailsink and close the messagesink.
1206 // (surely setup should provide us this cleared but...)
1207 $this->mailsink->clear();
1208 $this->messagesink->close();
1210 $expect = [
1211 'author' => (object) [
1212 'userid' => $user->id,
1213 'messages' => count($posts),
1216 $this->queue_tasks_and_assert($expect);
1218 $this->send_notifications_and_assert($user, $posts);
1220 // Get the mails.
1221 $mails = $this->mailsink->get_messages();
1223 // Start testing the expectations.
1224 $expectations = $data['expectations'];
1226 // Assert the number is the expected.
1227 $this->assertSame(count($expectations), count($mails));
1229 // Start processing mails, first localizing its expectations, then checking them.
1230 foreach ($mails as $mail) {
1231 // Find the corresponding expectation.
1232 $foundexpectation = null;
1233 foreach ($expectations as $key => $expectation) {
1234 // All expectations must have a subject for matching.
1235 if (!isset($expectation['subject'])) {
1236 $this->fail('Provider expectation missing mandatory subject');
1238 if (preg_match('!' . $expectation['subject'] . '!', $mail->subject)) {
1239 // If we already had found the expectation, there are non-unique subjects. Fail.
1240 if (isset($foundexpectation)) {
1241 $this->fail('Multiple expectations found (by subject matching). Please make them unique.');
1243 $foundexpectation = $expectation;
1244 unset($expectations[$key]);
1247 // Arrived here, we should have found the expectations.
1248 $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail');
1250 // If we have found the expectation and have contents to match, let's do it.
1251 if (isset($foundexpectation) and isset($foundexpectation['contents'])) {
1252 $mail->body = quoted_printable_decode($mail->body);
1253 if (!is_array($foundexpectation['contents'])) { // Accept both string and array.
1254 $foundexpectation['contents'] = array($foundexpectation['contents']);
1256 foreach ($foundexpectation['contents'] as $content) {
1257 if (strpos($content, '~') !== 0) {
1258 $this->assertRegexp('#' . $content . '#m', $mail->body);
1259 } else {
1260 preg_match('#' . substr($content, 1) . '#m', $mail->body, $matches);
1261 $this->assertNotRegexp('#' . substr($content, 1) . '#m', $mail->body);
1267 // Finished, there should not be remaining expectations.
1268 $this->assertCount(0, $expectations);
1272 * Ensure that posts already mailed are not re-sent.
1274 public function test_already_mailed() {
1275 global $DB;
1277 $this->resetAfterTest(true);
1279 // Create a course, with a forum.
1280 $course = $this->getDataGenerator()->create_course();
1282 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1283 $forum = $this->getDataGenerator()->create_module('forum', $options);
1285 // Create two users enrolled in the course as students.
1286 list($author, $recipient) = $this->helper_create_users($course, 2);
1288 // Post a discussion to the forum.
1289 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1290 $DB->set_field('forum_posts', 'mailed', 1);
1292 // No posts shoudl be considered.
1293 $this->queue_tasks_and_assert([]);
1295 // No notifications should be queued.
1296 $this->send_notifications_and_assert($author, []);
1297 $this->send_notifications_and_assert($recipient, []);
1301 * Ensure that posts marked mailnow are not suspect to the maxeditingtime.
1303 public function test_mailnow() {
1304 global $CFG, $DB;
1306 // Update the maxeditingtime to 1 day so that posts won't be sent.
1307 $CFG->maxeditingtime = DAYSECS;
1309 $this->resetAfterTest(true);
1311 // Create a course, with a forum.
1312 $course = $this->getDataGenerator()->create_course();
1314 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1315 $forum = $this->getDataGenerator()->create_module('forum', $options);
1317 // Create two users enrolled in the course as students.
1318 list($author, $recipient) = $this->helper_create_users($course, 2);
1320 // Post a discussion to the forum.
1321 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1323 // Post a discussion to the forum.
1324 list($discussion, $postmailednow) = $this->helper_post_to_forum($forum, $author, ['mailnow' => 1]);
1326 // Only the mailnow post should be considered.
1327 $expect = [
1328 'author' => (object) [
1329 'userid' => $author->id,
1330 'messages' => 1,
1332 'recipient' => (object) [
1333 'userid' => $recipient->id,
1334 'messages' => 1,
1337 $this->queue_tasks_and_assert($expect);
1339 // No notifications should be queued.
1340 $this->send_notifications_and_assert($author, [$postmailednow]);
1341 $this->send_notifications_and_assert($recipient, [$postmailednow]);
1345 * Ensure that if a user has no permission to view a post, then it is not sent.
1347 public function test_access_coursemodule_hidden() {
1348 global $CFG, $DB;
1350 $this->resetAfterTest(true);
1352 // Create a course, with a forum.
1353 $course = $this->getDataGenerator()->create_course();
1355 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1356 $forum = $this->getDataGenerator()->create_module('forum', $options);
1358 // Create two users enrolled in the course as students.
1359 list($author, $recipient) = $this->helper_create_users($course, 2);
1361 // Create one users enrolled in the course as an editing teacher.
1362 list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1364 // Post a discussion to the forum.
1365 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1367 // Hide the coursemodule.
1368 set_coursemodule_visible($forum->cmid, 0);
1370 // Only the mailnow post should be considered.
1371 $expect = [
1372 'author' => (object) [
1373 'userid' => $author->id,
1374 'messages' => 1,
1376 'recipient' => (object) [
1377 'userid' => $recipient->id,
1378 'messages' => 1,
1380 'editor' => (object) [
1381 'userid' => $editor->id,
1382 'messages' => 1,
1385 $this->queue_tasks_and_assert($expect);
1387 // No notifications should be queued.
1388 $this->send_notifications_and_assert($author, [], true);
1389 $this->send_notifications_and_assert($recipient, [], true);
1390 $this->send_notifications_and_assert($editor, [$post], true);
1394 * Ensure that if a user loses permission to view a post after it is queued, that it is not sent.
1396 public function test_access_coursemodule_hidden_after_queue() {
1397 global $CFG, $DB;
1399 $this->resetAfterTest(true);
1401 // Create a course, with a forum.
1402 $course = $this->getDataGenerator()->create_course();
1404 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1405 $forum = $this->getDataGenerator()->create_module('forum', $options);
1407 // Create two users enrolled in the course as students.
1408 list($author, $recipient) = $this->helper_create_users($course, 2);
1410 // Create one users enrolled in the course as an editing teacher.
1411 list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1413 // Post a discussion to the forum.
1414 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1416 // Only the mailnow post should be considered.
1417 $expect = [
1418 'author' => (object) [
1419 'userid' => $author->id,
1420 'messages' => 1,
1422 'recipient' => (object) [
1423 'userid' => $recipient->id,
1424 'messages' => 1,
1426 'editor' => (object) [
1427 'userid' => $editor->id,
1428 'messages' => 1,
1431 $this->queue_tasks_and_assert($expect);
1433 // Hide the coursemodule.
1434 set_coursemodule_visible($forum->cmid, 0);
1436 // No notifications should be queued for the students.
1437 $this->send_notifications_and_assert($author, [], true);
1438 $this->send_notifications_and_assert($recipient, [], true);
1440 // The editing teacher should still receive the post.
1441 $this->send_notifications_and_assert($editor, [$post]);
1445 * Ensure that messages are not sent until the timestart.
1447 public function test_access_before_timestart() {
1448 global $CFG, $DB;
1450 $this->resetAfterTest(true);
1452 // Create a course, with a forum.
1453 $course = $this->getDataGenerator()->create_course();
1455 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1456 $forum = $this->getDataGenerator()->create_module('forum', $options);
1458 // Create two users enrolled in the course as students.
1459 list($author, $recipient) = $this->helper_create_users($course, 2);
1461 // Create one users enrolled in the course as an editing teacher.
1462 list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1464 // Post a discussion to the forum.
1465 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1467 // Update the discussion to have a timestart in the future.
1468 $DB->set_field('forum_discussions', 'timestart', time() + DAYSECS);
1470 // None should be sent.
1471 $this->queue_tasks_and_assert([]);
1473 // No notifications should be queued for any user.
1474 $this->send_notifications_and_assert($author, []);
1475 $this->send_notifications_and_assert($recipient, []);
1476 $this->send_notifications_and_assert($editor, []);
1478 // Update the discussion to have a timestart in the past.
1479 $DB->set_field('forum_discussions', 'timestart', time() - DAYSECS);
1481 // Now should be sent to all.
1482 $expect = [
1483 'author' => (object) [
1484 'userid' => $author->id,
1485 'messages' => 1,
1487 'recipient' => (object) [
1488 'userid' => $recipient->id,
1489 'messages' => 1,
1491 'editor' => (object) [
1492 'userid' => $editor->id,
1493 'messages' => 1,
1496 $this->queue_tasks_and_assert($expect);
1498 // No notifications should be queued for any user.
1499 $this->send_notifications_and_assert($author, [$post]);
1500 $this->send_notifications_and_assert($recipient, [$post]);
1501 $this->send_notifications_and_assert($editor, [$post]);
1505 * Ensure that messages are not sent after the timeend.
1507 public function test_access_after_timeend() {
1508 global $CFG, $DB;
1510 $this->resetAfterTest(true);
1512 // Create a course, with a forum.
1513 $course = $this->getDataGenerator()->create_course();
1515 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1516 $forum = $this->getDataGenerator()->create_module('forum', $options);
1518 // Create two users enrolled in the course as students.
1519 list($author, $recipient) = $this->helper_create_users($course, 2);
1521 // Create one users enrolled in the course as an editing teacher.
1522 list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1524 // Post a discussion to the forum.
1525 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1527 // Update the discussion to have a timestart in the past.
1528 $DB->set_field('forum_discussions', 'timeend', time() - DAYSECS);
1530 // None should be sent.
1531 $this->queue_tasks_and_assert([]);
1533 // No notifications should be queued for any user.
1534 $this->send_notifications_and_assert($author, []);
1535 $this->send_notifications_and_assert($recipient, []);
1536 $this->send_notifications_and_assert($editor, []);
1538 // Update the discussion to have a timestart in the past.
1539 $DB->set_field('forum_discussions', 'timeend', time() + DAYSECS);
1541 // Now should be sent to all.
1542 $expect = [
1543 'author' => (object) [
1544 'userid' => $author->id,
1545 'messages' => 1,
1547 'recipient' => (object) [
1548 'userid' => $recipient->id,
1549 'messages' => 1,
1551 'editor' => (object) [
1552 'userid' => $editor->id,
1553 'messages' => 1,
1556 $this->queue_tasks_and_assert($expect);
1558 // No notifications should be queued for any user.
1559 $this->send_notifications_and_assert($author, [$post]);
1560 $this->send_notifications_and_assert($recipient, [$post]);
1561 $this->send_notifications_and_assert($editor, [$post]);
1565 * Test notification comes with customdata.
1567 public function test_notification_customdata() {
1568 $this->resetAfterTest(true);
1570 $course = $this->getDataGenerator()->create_course();
1572 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1573 $forum = $this->getDataGenerator()->create_module('forum', $options);
1575 list($author) = $this->helper_create_users($course, 1);
1576 list($commenter) = $this->helper_create_users($course, 1);
1578 $strre = get_string('re', 'forum');
1580 // New posts should not have Re: in the subject.
1581 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1582 $expect = [
1583 'author' => (object) [
1584 'userid' => $author->id,
1585 'messages' => 1,
1587 'commenter' => (object) [
1588 'userid' => $commenter->id,
1589 'messages' => 1,
1592 $this->queue_tasks_and_assert($expect);
1594 $this->send_notifications_and_assert($author, [$post]);
1595 $this->send_notifications_and_assert($commenter, [$post]);
1596 $messages = $this->messagesink->get_messages();
1597 $customdata = json_decode($messages[0]->customdata);
1598 $this->assertEquals($forum->id, $customdata->instance);
1599 $this->assertEquals($forum->cmid, $customdata->cmid);
1600 $this->assertEquals($post->id, $customdata->postid);
1601 $this->assertEquals($discussion->id, $customdata->discussionid);
1602 $this->assertObjectHasAttribute('notificationiconurl', $customdata);
1603 $this->assertObjectHasAttribute('actionbuttons', $customdata);
1604 $this->assertCount(1, (array) $customdata->actionbuttons);