Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace mod_forum;
18
 
19
use mod_forum_tests_generator_trait;
20
use mod_forum_tests_cron_trait;
21
 
22
defined('MOODLE_INTERNAL') || die();
23
 
24
global $CFG;
25
require_once($CFG->dirroot . '/mod/forum/lib.php');
26
require_once(__DIR__ . '/cron_trait.php');
27
require_once(__DIR__ . '/generator_trait.php');
28
 
29
/**
30
 * The forum module mail generation tests.
31
 *
32
 * @package    mod_forum
33
 * @category   test
34
 * @copyright  2013 Andrew Nicols
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 *
37
 */
38
class mail_test extends \advanced_testcase {
39
    // Make use of the cron tester trait.
40
    use mod_forum_tests_cron_trait;
41
 
42
    // Make use of the test generator trait.
43
    use mod_forum_tests_generator_trait;
44
 
45
    /**
46
     * @var \phpunit_message_sink
47
     */
48
    protected $messagesink;
49
 
50
    /**
51
     * @var \phpunit_mailer_sink
52
     */
53
    protected $mailsink;
54
 
55
    /** @var \phpunit_event_sink */
56
    protected $eventsink;
57
 
58
    public function setUp(): void {
59
        global $CFG;
60
 
61
        // We must clear the subscription caches. This has to be done both before each test, and after in case of other
62
        // tests using these functions.
63
        \mod_forum\subscriptions::reset_forum_cache();
64
        \mod_forum\subscriptions::reset_discussion_cache();
65
 
66
        // Messaging is not compatible with transactions...
67
        $this->preventResetByRollback();
68
 
69
        // Catch all messages.
70
        $this->messagesink = $this->redirectMessages();
71
        $this->mailsink = $this->redirectEmails();
72
 
73
        // Forcibly reduce the maxeditingtime to a second in the past to
74
        // ensure that messages are sent out.
75
        $CFG->maxeditingtime = -1;
76
    }
77
 
78
    public function tearDown(): void {
79
        // We must clear the subscription caches. This has to be done both before each test, and after in case of other
80
        // tests using these functions.
81
        \mod_forum\subscriptions::reset_forum_cache();
82
 
83
        $this->messagesink->clear();
84
        $this->messagesink->close();
85
        unset($this->messagesink);
86
 
87
        $this->mailsink->clear();
88
        $this->mailsink->close();
89
        unset($this->mailsink);
90
    }
91
 
92
    /**
93
     * Perform message inbound setup for the mod_forum reply handler.
94
     */
95
    protected function helper_spoof_message_inbound_setup() {
96
        global $CFG, $DB;
97
        // Setup the default Inbound Message mailbox settings.
98
        $CFG->messageinbound_domain = 'example.com';
99
        $CFG->messageinbound_enabled = true;
100
 
101
        // Must be no longer than 15 characters.
102
        $CFG->messageinbound_mailbox = 'moodlemoodle123';
103
 
104
        $record = $DB->get_record('messageinbound_handlers', array('classname' => '\mod_forum\message\inbound\reply_handler'));
105
        $record->enabled = true;
106
        $record->id = $DB->update_record('messageinbound_handlers', $record);
107
    }
108
 
109
    public function test_cron_message_includes_courseid() {
110
        $this->resetAfterTest(true);
111
 
112
        // Create a course, with a forum.
113
        $course = $this->getDataGenerator()->create_course();
114
 
115
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
116
        $forum = $this->getDataGenerator()->create_module('forum', $options);
117
 
118
        // Create two users enrolled in the course as students.
119
        list($author, $recipient) = $this->helper_create_users($course, 2);
120
 
121
        // Post a discussion to the forum.
122
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
123
 
124
        $expect = [
125
            'author' => (object) [
126
                'userid' => $author->id,
127
                'messages' => 1,
128
            ],
129
            'recipient' => (object) [
130
                'userid' => $recipient->id,
131
                'messages' => 1,
132
            ],
133
        ];
134
        $this->queue_tasks_and_assert($expect);
135
 
136
        $this->messagesink->close();
137
        $this->eventsink = $this->redirectEvents();
138
        $this->send_notifications_and_assert($author, [$post]);
139
        $events = $this->eventsink->get_events();
140
        $event = reset($events);
141
 
142
        $this->assertEquals($course->id, $event->other['courseid']);
143
 
144
        $this->send_notifications_and_assert($recipient, [$post]);
145
    }
146
 
147
    public function test_forced_subscription() {
148
        global $DB;
149
        $this->resetAfterTest(true);
150
 
151
        // Create a course, with a forum.
152
        $course = $this->getDataGenerator()->create_course();
153
 
154
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
155
        $forum = $this->getDataGenerator()->create_module('forum', $options);
156
 
157
        // Create users enrolled in the course as students.
158
        list($author, $recipient, $unconfirmed, $deleted) = $this->helper_create_users($course, 4);
159
 
160
        // Make the third user unconfirmed (thence inactive) to make sure it does not break the notifications.
161
        $DB->set_field('user', 'confirmed', 0, ['id' => $unconfirmed->id]);
162
 
163
        // Mark the fourth user as deleted to make sure it does not break the notifications.
164
        $DB->set_field('user', 'deleted', 1, ['id' => $deleted->id]);
165
 
166
        // Post a discussion to the forum.
167
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
168
 
169
        $expect = [
170
            (object) [
171
                'userid' => $author->id,
172
                'messages' => 1,
173
            ],
174
            (object) [
175
                'userid' => $recipient->id,
176
                'messages' => 1,
177
            ],
178
            (object) [
179
                'userid' => $unconfirmed->id,
180
                'messages' => 0,
181
            ],
182
            (object) [
183
                'userid' => $deleted->id,
184
                'messages' => 0,
185
            ],
186
        ];
187
        $this->queue_tasks_and_assert($expect);
188
 
189
        $this->send_notifications_and_assert($author, [$post]);
190
        $this->send_notifications_and_assert($recipient, [$post]);
191
        $this->send_notifications_and_assert($unconfirmed, []);
192
        $this->send_notifications_and_assert($deleted, []);
193
    }
194
 
195
    /**
196
     * Ensure that for a forum with subscription disabled that standard users will not receive posts.
197
     */
198
    public function test_subscription_disabled_standard_users() {
199
        global $DB;
200
 
201
        $this->resetAfterTest(true);
202
 
203
        // Create a course, with a forum.
204
        $course = $this->getDataGenerator()->create_course();
205
 
206
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
207
        $forum = $this->getDataGenerator()->create_module('forum', $options);
208
 
209
        // Create two users enrolled in the course as students.
210
        list($author, $recipient) = $this->helper_create_users($course, 2);
211
 
212
        // Post a discussion to the forum.
213
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
214
 
215
        // Run cron and check that the expected number of users received the notification.
216
        $expect = [
217
            (object) [
218
                'userid' => $author->id,
219
                'messages' => 0,
220
            ],
221
            (object) [
222
                'userid' => $recipient->id,
223
                'messages' => 0,
224
            ],
225
        ];
226
        $this->queue_tasks_and_assert($expect);
227
 
228
        $this->send_notifications_and_assert($author, []);
229
        $this->send_notifications_and_assert($recipient, []);
230
    }
231
 
232
    /**
233
     * Ensure that for a forum with subscription disabled that a user subscribed to the forum will receive the post.
234
     */
235
    public function test_subscription_disabled_user_subscribed_forum() {
236
        global $DB;
237
 
238
        $this->resetAfterTest(true);
239
 
240
        // Create a course, with a forum.
241
        $course = $this->getDataGenerator()->create_course();
242
 
243
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
244
        $forum = $this->getDataGenerator()->create_module('forum', $options);
245
 
246
        // Create two users enrolled in the course as students.
247
        list($author, $recipient) = $this->helper_create_users($course, 2);
248
 
249
        // A user with the manageactivities capability within the course can subscribe.
250
        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
251
        assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], \context_course::instance($course->id));
252
 
253
        // Suscribe the recipient only.
254
        \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
255
 
256
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
257
            'userid'        => $recipient->id,
258
            'forum'         => $forum->id,
259
        )));
260
 
261
        // Post a discussion to the forum.
262
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
263
 
264
        // Run cron and check that the expected number of users received the notification.
265
        $expect = [
266
            'author' => (object) [
267
                'userid' => $author->id,
268
            ],
269
            'recipient' => (object) [
270
                'userid' => $recipient->id,
271
                'messages' => 1,
272
            ],
273
        ];
274
        $this->queue_tasks_and_assert($expect);
275
 
276
        $this->send_notifications_and_assert($author, []);
277
        $this->send_notifications_and_assert($recipient, [$post]);
278
    }
279
 
280
    /**
281
     * Ensure that for a forum with subscription disabled that a user subscribed to the discussion will receive the
282
     * post.
283
     */
284
    public function test_subscription_disabled_user_subscribed_discussion() {
285
        global $DB;
286
 
287
        $this->resetAfterTest(true);
288
 
289
        // Create a course, with a forum.
290
        $course = $this->getDataGenerator()->create_course();
291
 
292
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
293
        $forum = $this->getDataGenerator()->create_module('forum', $options);
294
 
295
        // Create two users enrolled in the course as students.
296
        list($author, $recipient) = $this->helper_create_users($course, 2);
297
 
298
        // A user with the manageactivities capability within the course can subscribe.
299
        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
300
        assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], \context_course::instance($course->id));
301
 
302
        // Run cron and check that the expected number of users received the notification.
303
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
304
 
305
        // Subscribe the user to the discussion.
306
        \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
307
        $this->helper_update_subscription_time($recipient, $discussion, -60);
308
 
309
        // Run cron and check that the expected number of users received the notification.
310
        $expect = [
311
            'author' => (object) [
312
                'userid' => $author->id,
313
            ],
314
            'recipient' => (object) [
315
                'userid' => $recipient->id,
316
                'messages' => 1,
317
            ],
318
        ];
319
        $this->queue_tasks_and_assert($expect);
320
 
321
        $this->send_notifications_and_assert($author, []);
322
        $this->send_notifications_and_assert($recipient, [$post]);
323
    }
324
 
325
    /**
326
     * Ensure that for a forum with automatic subscription that users receive posts.
327
     */
328
    public function test_automatic() {
329
        $this->resetAfterTest(true);
330
 
331
        // Create a course, with a forum.
332
        $course = $this->getDataGenerator()->create_course();
333
 
334
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
335
        $forum = $this->getDataGenerator()->create_module('forum', $options);
336
 
337
        // Create two users enrolled in the course as students.
338
        list($author, $recipient) = $this->helper_create_users($course, 2);
339
 
340
        // Post a discussion to the forum.
341
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
342
 
343
        $expect = [
344
            (object) [
345
                'userid' => $author->id,
346
                'messages' => 1,
347
            ],
348
            (object) [
349
                'userid' => $recipient->id,
350
                'messages' => 1,
351
            ],
352
        ];
353
        $this->queue_tasks_and_assert($expect);
354
 
355
        $this->send_notifications_and_assert($author, [$post]);
356
        $this->send_notifications_and_assert($recipient, [$post]);
357
    }
358
 
359
    /**
360
     * Ensure that private replies are not sent to users with an automatic subscription unless they are an expected
361
     * recipient.
362
     */
363
    public function test_automatic_with_private_reply() {
364
        $this->resetAfterTest(true);
365
 
366
        // Create a course, with a forum.
367
        $course = $this->getDataGenerator()->create_course();
368
        $forum = $this->getDataGenerator()->create_module('forum', [
369
                'course' => $course->id,
370
                'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
371
            ]);
372
 
373
        [$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
374
        [$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher');
375
 
376
        [$discussion, $post] = $this->helper_post_to_forum($forum, $student);
377
        $reply = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
378
                'privatereplyto' => $student->id,
379
            ]);
380
 
381
        // The private reply is queued to all messages as reply visibility may change between queueing, and sending.
382
        $expect = [
383
            (object) [
384
                'userid' => $student->id,
385
                'messages' => 2,
386
            ],
387
            (object) [
388
                'userid' => $otherstudent->id,
389
                'messages' => 2,
390
            ],
391
            (object) [
392
                'userid' => $teacher->id,
393
                'messages' => 2,
394
            ],
395
            (object) [
396
                'userid' => $otherteacher->id,
397
                'messages' => 2,
398
            ],
399
        ];
400
        $this->queue_tasks_and_assert($expect);
401
 
402
        // The actual messages sent will respect private replies.
403
        $this->send_notifications_and_assert($student, [$post, $reply]);
404
        $this->send_notifications_and_assert($teacher, [$post, $reply]);
405
        $this->send_notifications_and_assert($otherteacher, [$post, $reply]);
406
        $this->send_notifications_and_assert($otherstudent, [$post]);
407
    }
408
 
409
    public function test_optional() {
410
        $this->resetAfterTest(true);
411
 
412
        // Create a course, with a forum.
413
        $course = $this->getDataGenerator()->create_course();
414
 
415
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
416
        $forum = $this->getDataGenerator()->create_module('forum', $options);
417
 
418
        // Create two users enrolled in the course as students.
419
        list($author, $recipient) = $this->helper_create_users($course, 2);
420
 
421
        // Post a discussion to the forum.
422
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
423
 
424
        $expect = [
425
            (object) [
426
                'userid' => $author->id,
427
                'messages' => 0,
428
            ],
429
            (object) [
430
                'userid' => $recipient->id,
431
                'messages' => 0,
432
            ],
433
        ];
434
        $this->queue_tasks_and_assert($expect);
435
 
436
        $this->send_notifications_and_assert($author, []);
437
        $this->send_notifications_and_assert($recipient, []);
438
    }
439
 
440
    public function test_automatic_with_unsubscribed_user() {
441
        $this->resetAfterTest(true);
442
 
443
        // Create a course, with a forum.
444
        $course = $this->getDataGenerator()->create_course();
445
 
446
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
447
        $forum = $this->getDataGenerator()->create_module('forum', $options);
448
 
449
        // Create two users enrolled in the course as students.
450
        list($author, $recipient) = $this->helper_create_users($course, 2);
451
 
452
        // Unsubscribe the 'author' user from the forum.
453
        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
454
 
455
        // Post a discussion to the forum.
456
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
457
 
458
        $expect = [
459
            (object) [
460
                'userid' => $author->id,
461
                'messages' => 0,
462
            ],
463
            (object) [
464
                'userid' => $recipient->id,
465
                'messages' => 1,
466
            ],
467
        ];
468
        $this->queue_tasks_and_assert($expect);
469
 
470
        $this->send_notifications_and_assert($author, []);
471
        $this->send_notifications_and_assert($recipient, [$post]);
472
    }
473
 
474
    public function test_optional_with_subscribed_user() {
475
        $this->resetAfterTest(true);
476
 
477
        // Create a course, with a forum.
478
        $course = $this->getDataGenerator()->create_course();
479
 
480
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
481
        $forum = $this->getDataGenerator()->create_module('forum', $options);
482
 
483
        // Create two users enrolled in the course as students.
484
        list($author, $recipient) = $this->helper_create_users($course, 2);
485
 
486
        // Subscribe the 'recipient' user from the forum.
487
        \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
488
 
489
        // Post a discussion to the forum.
490
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
491
 
492
        $expect = [
493
            (object) [
494
                'userid' => $author->id,
495
                'messages' => 0,
496
            ],
497
            (object) [
498
                'userid' => $recipient->id,
499
                'messages' => 1,
500
            ],
501
        ];
502
        $this->queue_tasks_and_assert($expect);
503
 
504
        $this->send_notifications_and_assert($author, []);
505
        $this->send_notifications_and_assert($recipient, [$post]);
506
    }
507
 
508
    public function test_automatic_with_unsubscribed_discussion() {
509
        $this->resetAfterTest(true);
510
 
511
        // Create a course, with a forum.
512
        $course = $this->getDataGenerator()->create_course();
513
 
514
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
515
        $forum = $this->getDataGenerator()->create_module('forum', $options);
516
 
517
        // Create two users enrolled in the course as students.
518
        list($author, $recipient) = $this->helper_create_users($course, 2);
519
 
520
        // Post a discussion to the forum.
521
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
522
 
523
        // Unsubscribe the 'author' user from the discussion.
524
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
525
 
526
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
527
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($recipient->id, $forum, $discussion->id));
528
 
529
        $expect = [
530
            (object) [
531
                'userid' => $author->id,
532
                'messages' => 0,
533
            ],
534
            (object) [
535
                'userid' => $recipient->id,
536
                'messages' => 1,
537
            ],
538
        ];
539
        $this->queue_tasks_and_assert($expect);
540
 
541
        $this->send_notifications_and_assert($author, []);
542
        $this->send_notifications_and_assert($recipient, [$post]);
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
        $expect = [
568
            (object) [
569
                'userid' => $author->id,
570
                'messages' => 0,
571
            ],
572
            (object) [
573
                'userid' => $recipient->id,
574
                'messages' => 0,
575
            ],
576
        ];
577
        $this->queue_tasks_and_assert($expect);
578
 
579
        $this->send_notifications_and_assert($author, []);
580
        $this->send_notifications_and_assert($recipient, []);
581
 
582
        // Have a user reply to the discussion.
583
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
584
        $this->helper_update_post_time($reply, -30);
585
 
586
        // We expect only one user to receive this post.
587
        $expect = [
588
            (object) [
589
                'userid' => $author->id,
590
                'messages' => 0,
591
            ],
592
            (object) [
593
                'userid' => $recipient->id,
594
                'messages' => 1,
595
            ],
596
        ];
597
        $this->queue_tasks_and_assert($expect);
598
 
599
        $this->send_notifications_and_assert($author, []);
600
        $this->send_notifications_and_assert($recipient, [$reply]);
601
    }
602
 
603
    public function test_optional_with_subscribed_discussion_and_post() {
604
        $this->resetAfterTest(true);
605
 
606
        // Create a course, with a forum.
607
        $course = $this->getDataGenerator()->create_course();
608
 
609
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
610
        $forum = $this->getDataGenerator()->create_module('forum', $options);
611
 
612
        // Create two users enrolled in the course as students.
613
        list($author, $recipient) = $this->helper_create_users($course, 2);
614
 
615
        // Post a discussion to the forum.
616
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
617
        $this->helper_update_post_time($post, -90);
618
 
619
        // Have a user reply to the discussion before we subscribed.
620
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
621
        $this->helper_update_post_time($reply, -75);
622
 
623
        // Subscribe the 'recipient' user to the discussion.
624
        \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
625
        $this->helper_update_subscription_time($recipient, $discussion, -60);
626
 
627
        // Have a user reply to the discussion.
628
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
629
        $this->helper_update_post_time($reply, -30);
630
 
631
        // We expect only one user to receive this post.
632
        // The original post won't be received as it was written before the user subscribed.
633
        $expect = [
634
            (object) [
635
                'userid' => $author->id,
636
                'messages' => 0,
637
            ],
638
            (object) [
639
                'userid' => $recipient->id,
640
                'messages' => 1,
641
            ],
642
        ];
643
        $this->queue_tasks_and_assert($expect);
644
 
645
        $this->send_notifications_and_assert($author, []);
646
        $this->send_notifications_and_assert($recipient, [$reply]);
647
    }
648
 
649
    public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() {
650
        $this->resetAfterTest(true);
651
 
652
        // Create a course, with a forum.
653
        $course = $this->getDataGenerator()->create_course();
654
 
655
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
656
        $forum = $this->getDataGenerator()->create_module('forum', $options);
657
 
658
        // Create two users enrolled in the course as students.
659
        list($author, $recipient) = $this->helper_create_users($course, 2);
660
 
661
        // Post a discussion to the forum.
662
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
663
        $this->helper_update_post_time($post, -90);
664
 
665
        // Unsubscribe the 'author' user from the forum.
666
        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
667
 
668
        // Then re-subscribe them to the discussion.
669
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
670
        $this->helper_update_subscription_time($author, $discussion, -60);
671
 
672
        $expect = [
673
            (object) [
674
                'userid' => $author->id,
675
                'messages' => 0,
676
            ],
677
            (object) [
678
                'userid' => $recipient->id,
679
                'messages' => 1,
680
            ],
681
        ];
682
        $this->queue_tasks_and_assert($expect);
683
 
684
        $this->send_notifications_and_assert($author, []);
685
        $this->send_notifications_and_assert($recipient, [$post]);
686
 
687
        // Now post a reply to the original post.
688
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
689
        $this->helper_update_post_time($reply, -30);
690
 
691
        $expect = [
692
            (object) [
693
                'userid' => $author->id,
694
                'messages' => 1,
695
            ],
696
            (object) [
697
                'userid' => $recipient->id,
698
                'messages' => 1,
699
            ],
700
        ];
701
        $this->queue_tasks_and_assert($expect);
702
 
703
        $this->send_notifications_and_assert($author, [$reply]);
704
        $this->send_notifications_and_assert($recipient, [$reply]);
705
    }
706
 
707
    public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() {
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
        // Create two users enrolled in the course as students.
717
        list($author, $recipient) = $this->helper_create_users($course, 2);
718
 
719
        // Post a discussion to the forum.
720
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
721
 
722
        // Unsubscribe the 'recipient' user from the discussion.
723
        \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
724
 
725
        // Then unsubscribe them from the discussion.
726
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion);
727
 
728
        // We don't expect any users to receive this post.
729
        $expect = [
730
            (object) [
731
                'userid' => $author->id,
732
                'messages' => 0,
733
            ],
734
            (object) [
735
                'userid' => $recipient->id,
736
                'messages' => 0,
737
            ],
738
        ];
739
        $this->queue_tasks_and_assert($expect);
740
 
741
        $this->send_notifications_and_assert($author, []);
742
        $this->send_notifications_and_assert($recipient, []);
743
    }
744
 
745
    /**
746
     * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after
747
     * they subscribed to the discussion.
748
     */
749
    public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() {
750
        $this->resetAfterTest(true);
751
 
752
        // Create a course, with a forum.
753
        $course = $this->getDataGenerator()->create_course();
754
 
755
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
756
        $forum = $this->getDataGenerator()->create_module('forum', $options);
757
 
758
        $expectedmessages = array();
759
 
760
        // Create a user enrolled in the course as a student.
761
        list($author) = $this->helper_create_users($course, 1);
762
 
763
        // Post a discussion to the forum.
764
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
765
        $this->helper_update_post_time($post, -90);
766
 
767
        $expectedmessages[] = array(
768
            'id' => $post->id,
769
            'subject' => $post->subject,
770
            'count' => 0,
771
        );
772
 
773
        // Then subscribe the user to the discussion.
774
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
775
        $this->helper_update_subscription_time($author, $discussion, -60);
776
 
777
        // Then post a reply to the first discussion.
778
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
779
        $this->helper_update_post_time($reply, -30);
780
 
781
        $expect = [
782
            (object) [
783
                'userid' => $author->id,
784
                'messages' => 1,
785
            ],
786
        ];
787
        $this->queue_tasks_and_assert($expect);
788
 
789
        $this->send_notifications_and_assert($author, [$reply]);
790
    }
791
 
792
    public function test_subscription_by_inactive_users() {
793
        global $DB;
794
        $this->resetAfterTest(true);
795
 
796
        $course = $this->getDataGenerator()->create_course();
797
 
798
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
799
        $forum = $this->getDataGenerator()->create_module('forum', $options);
800
 
801
        // Create two users enrolled in the course as students.
802
        list($author, $u1, $u2, $u3) = $this->helper_create_users($course, 4);
803
 
804
        // Subscribe the three users to the forum.
805
        \mod_forum\subscriptions::subscribe_user($u1->id, $forum);
806
        \mod_forum\subscriptions::subscribe_user($u2->id, $forum);
807
        \mod_forum\subscriptions::subscribe_user($u3->id, $forum);
808
 
809
        // Make the first user inactive - suspended.
810
        $DB->set_field('user', 'suspended', 1, ['id' => $u1->id]);
811
 
812
        // Make the second user inactive - unable to log in.
813
        $DB->set_field('user', 'auth', 'nologin', ['id' => $u2->id]);
814
 
815
        // Post a discussion to the forum.
816
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
817
 
818
        $expect = [
819
            (object) [
820
                'userid' => $u1->id,
821
                'messages' => 0,
822
            ],
823
            (object) [
824
                'userid' => $u2->id,
825
                'messages' => 0,
826
            ],
827
            (object) [
828
                'userid' => $u3->id,
829
                'messages' => 1,
830
            ],
831
        ];
832
 
833
        $this->queue_tasks_and_assert($expect);
834
        $this->send_notifications_and_assert($u1, []);
835
        $this->send_notifications_and_assert($u2, []);
836
        $this->send_notifications_and_assert($u3, [$post]);
837
    }
838
 
839
    public function test_forum_message_inbound_multiple_posts() {
840
        $this->resetAfterTest(true);
841
 
842
        // Create a course, with a forum.
843
        $course = $this->getDataGenerator()->create_course();
844
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
845
        $forum = $this->getDataGenerator()->create_module('forum', $options);
846
 
847
        // Create a user enrolled in the course as a student.
848
        list($author) = $this->helper_create_users($course, 1);
849
 
850
        $expectedmessages = array();
851
 
852
        // Post a discussion to the forum.
853
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
854
        $this->helper_update_post_time($post, -90);
855
 
856
        $expectedmessages[] = (object) [
857
            'id' => $post->id,
858
            'subject' => $post->subject,
859
            'count' => 0,
860
        ];
861
 
862
        // Then post a reply to the first discussion.
863
        $reply = $this->helper_post_to_discussion($forum, $discussion, $author);
864
        $this->helper_update_post_time($reply, -60);
865
 
866
        $expectedmessages[] = (object) [
867
            'id' => $reply->id,
868
            'subject' => $reply->subject,
869
            'count' => 1,
870
        ];
871
 
872
        // Ensure that messageinbound is enabled and configured for the forum handler.
873
        $this->helper_spoof_message_inbound_setup();
874
 
875
        $author->emailstop = '0';
876
        set_user_preference('message_provider_mod_forum_posts_enabled', 'email', $author);
877
 
878
        // Run cron and check that the expected number of users received the notification.
879
        // Clear the mailsink, and close the messagesink.
880
        $this->mailsink->clear();
881
        $this->messagesink->close();
882
 
883
        $expect = [
884
            'author' => (object) [
885
                'userid' => $author->id,
886
                'messages' => count($expectedmessages),
887
            ],
888
        ];
889
        $this->queue_tasks_and_assert($expect);
890
 
891
        $this->send_notifications_and_assert($author, $expectedmessages);
892
        $messages = $this->mailsink->get_messages();
893
 
894
        // There should be the expected number of messages.
895
        $this->assertEquals(2, count($messages));
896
 
897
        foreach ($messages as $message) {
898
            $this->assertMatchesRegularExpression('/Reply-To: moodlemoodle123\+[^@]*@example.com/', $message->header);
899
        }
900
    }
901
 
902
    public function test_long_subject() {
903
        $this->resetAfterTest(true);
904
 
905
        // Create a course, with a forum.
906
        $course = $this->getDataGenerator()->create_course();
907
 
908
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
909
        $forum = $this->getDataGenerator()->create_module('forum', $options);
910
 
911
        // Create a user enrolled in the course as student.
912
        list($author) = $this->helper_create_users($course, 1);
913
 
914
        // Post a discussion to the forum.
915
        $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.';
916
        $a = (object)array('courseshortname' => $course->shortname, 'forumname' => $forum->name, 'subject' => $subject);
917
        $expectedsubject = get_string('postmailsubject', 'forum', $a);
918
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author, array('name' => $subject));
919
 
920
        // Run cron and check that the expected number of users received the notification.
921
        $expect = [
922
            'author' => (object) [
923
                'userid' => $author->id,
924
                'messages' => 1,
925
            ],
926
        ];
927
        $this->queue_tasks_and_assert($expect);
928
 
929
        $this->send_notifications_and_assert($author, [$post]);
930
        $messages = $this->messagesink->get_messages_by_component('mod_forum');
931
        $message = reset($messages);
932
        $this->assertEquals($author->id, $message->useridfrom);
933
        $this->assertEquals($expectedsubject, $message->subject);
934
    }
935
 
936
    /**
937
     * Test inital email and reply email subjects
938
     */
939
    public function test_subjects() {
940
        $this->resetAfterTest(true);
941
 
942
        $course = $this->getDataGenerator()->create_course();
943
 
944
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
945
        $forum = $this->getDataGenerator()->create_module('forum', $options);
946
 
947
        list($author) = $this->helper_create_users($course, 1);
948
        list($commenter) = $this->helper_create_users($course, 1);
949
 
950
        $strre = get_string('re', 'forum');
951
 
952
        // New posts should not have Re: in the subject.
953
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
954
        $expect = [
955
            'author' => (object) [
956
                'userid' => $author->id,
957
                'messages' => 1,
958
            ],
959
            'commenter' => (object) [
960
                'userid' => $commenter->id,
961
                'messages' => 1,
962
            ],
963
        ];
964
        $this->queue_tasks_and_assert($expect);
965
 
966
        $this->send_notifications_and_assert($author, [$post]);
967
        $this->send_notifications_and_assert($commenter, [$post]);
968
        $messages = $this->messagesink->get_messages();
969
        $this->assertStringNotContainsString($strre, $messages[0]->subject);
970
        $this->messagesink->clear();
971
 
972
        // Replies should have Re: in the subject.
973
        $reply = $this->helper_post_to_discussion($forum, $discussion, $commenter);
974
 
975
        $expect = [
976
            'author' => (object) [
977
                'userid' => $author->id,
978
                'messages' => 1,
979
            ],
980
            'commenter' => (object) [
981
                'userid' => $commenter->id,
982
                'messages' => 1,
983
            ],
984
        ];
985
        $this->queue_tasks_and_assert($expect);
986
 
987
        $this->send_notifications_and_assert($commenter, [$reply]);
988
        $this->send_notifications_and_assert($author, [$reply]);
989
        $messages = $this->messagesink->get_messages();
990
        $this->assertStringContainsString($strre, $messages[0]->subject);
991
        $this->assertStringContainsString($strre, $messages[1]->subject);
992
    }
993
 
994
    /**
995
     * dataProvider for test_forum_post_email_templates().
996
     */
997
    public function forum_post_email_templates_provider() {
998
        // Base information, we'll build variations based on it.
999
        $base = array(
1000
            'user' => array('firstname' => 'Love', 'lastname' => 'Moodle', 'mailformat' => 0, 'maildigest' => 0),
1001
            'course' => array('shortname' => '101', 'fullname' => 'Moodle 101'),
1002
            'forums' => array(
1003
                array(
1004
                    'name' => 'Moodle Forum',
1005
                    'forumposts' => array(
1006
                        array(
1007
                            'name' => 'Hello Moodle',
1008
                            'message' => 'Welcome to Moodle',
1009
                            'messageformat' => FORMAT_MOODLE,
1010
                            'attachments' => array(
1011
                                array(
1012
                                    'filename' => 'example.txt',
1013
                                    'filecontents' => 'Basic information about the course'
1014
                                ),
1015
                            ),
1016
                        ),
1017
                    ),
1018
                ),
1019
            ),
1020
            'expectations' => array(
1021
                array(
1022
                    'subject' => '.*101.*Hello',
1023
                    'contents' => array(
1024
                        '~{$a',
1025
                        '~&(amp|lt|gt|quot|\#039);(?!course)',
1026
                        'Attachment example.txt:' . '\r*\n' .
1027
                            'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' . '\r*\n',
1028
                        'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1'
1029
                    ),
1030
                ),
1031
            ),
1032
        );
1033
 
1034
        // Build the text cases.
1035
        $textcases = array('Text mail without ampersands, quotes or lt/gt' => array('data' => $base));
1036
 
1037
        // Single and double quotes everywhere.
1038
        $newcase = $base;
1039
        $newcase['user']['lastname'] = 'Moodle\'"';
1040
        $newcase['course']['shortname'] = '101\'"';
1041
        $newcase['forums'][0]['name'] = 'Moodle Forum\'"';
1042
        $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'"';
1043
        $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'"';
1044
        $newcase['expectations'][0]['contents'] = array(
1045
            'Attachment example.txt:', '~{\$a', '~&amp;(quot|\#039);', 'Love Moodle\'', '101\'', 'Moodle Forum\'"',
1046
            'Hello Moodle\'"', 'Welcome to Moodle\'"');
1047
        $textcases['Text mail with quotes everywhere'] = array('data' => $newcase);
1048
 
1049
        // Lt and gt everywhere. This case is completely borked because format_string()
1050
        // strips tags with $CFG->formatstringstriptags and also escapes < and > (correct
1051
        // for web presentation but not for text email). See MDL-19829.
1052
        $newcase = $base;
1053
        $newcase['user']['lastname'] = 'Moodle>';
1054
        $newcase['course']['shortname'] = '101>';
1055
        $newcase['forums'][0]['name'] = 'Moodle Forum>';
1056
        $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle>';
1057
        $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle>';
1058
        $newcase['expectations'][0]['contents'] = array(
1059
            'Attachment example.txt:', '~{\$a', '~&amp;gt;', 'Love Moodle>', '101>', 'Moodle Forum>',
1060
            'Hello Moodle>', 'Welcome to Moodle>');
1061
        $textcases['Text mail with gt and lt everywhere'] = array('data' => $newcase);
1062
 
1063
        // Ampersands everywhere. This case is completely borked because format_string()
1064
        // escapes ampersands (correct for web presentation but not for text email). See MDL-19829.
1065
        $newcase = $base;
1066
        $newcase['user']['lastname'] = 'Moodle&';
1067
        $newcase['course']['shortname'] = '101&';
1068
        $newcase['forums'][0]['name'] = 'Moodle Forum&';
1069
        $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle&';
1070
        $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle&';
1071
        $newcase['expectations'][0]['contents'] = array(
1072
            'Attachment example.txt:', '~{\$a', '~&amp;amp;', 'Love Moodle&', '101&', 'Moodle Forum&',
1073
            'Hello Moodle&', 'Welcome to Moodle&');
1074
        $textcases['Text mail with ampersands everywhere'] = array('data' => $newcase);
1075
 
1076
        // Text+image message i.e. @@PLUGINFILE@@ token handling.
1077
        $newcase = $base;
1078
        $newcase['forums'][0]['forumposts'][0]['name'] = 'Text and image';
1079
        $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle, '
1080
            .'@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png !';
1081
        $newcase['expectations'][0]['subject'] = '.*101.*Text and image';
1082
        $newcase['expectations'][0]['contents'] = array(
1083
            '~{$a',
1084
            '~&(amp|lt|gt|quot|\#039);(?!course)',
1085
            'Attachment example.txt:' . '\r*\n' .
1086
            'https://www.example.com/moodle/pluginfile.php/\d*/mod_forum/attachment/\d*/example.txt' .  '\r*\n' ,
1087
            'Text and image', 'Moodle Forum',
1088
            'Welcome to Moodle, *' . '\r*\n' . '.*'
1089
                .'https://www.example.com/moodle/pluginfile.php/\d+/mod_forum/post/\d+/'
1090
                .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png *' . '\r*\n' . '.*!',
1091
            'Love Moodle', '1\d1');
1092
        $textcases['Text mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
1093
 
1094
        // Now the html cases.
1095
        $htmlcases = array();
1096
 
1097
        // New base for html cases, no quotes, lts, gts or ampersands.
1098
        $htmlbase = $base;
1099
        $htmlbase['user']['mailformat'] = 1;
1100
        $htmlbase['expectations'][0]['contents'] = array(
1101
            '~{\$a',
1102
            '~&(amp|lt|gt|quot|\#039);(?!course|lang|version|iosappid|androidappid)',
1103
            '<div class="attachments">( *\n *)?<a href',
1104
            '<div class="subject">\n.*Hello Moodle', '>Moodle Forum', '>Welcome.*Moodle', '>Love Moodle', '>1\d1');
1105
        $htmlcases['HTML mail without ampersands, quotes or lt/gt'] = array('data' => $htmlbase);
1106
 
1107
        // Single and double quotes, lt and gt, ampersands everywhere.
1108
        $newcase = $htmlbase;
1109
        $newcase['user']['lastname'] = 'Moodle\'">&';
1110
        $newcase['course']['shortname'] = '101\'">&';
1111
        $newcase['forums'][0]['name'] = 'Moodle Forum\'">&';
1112
        $newcase['forums'][0]['forumposts'][0]['name'] = 'Hello Moodle\'">&';
1113
        $newcase['forums'][0]['forumposts'][0]['message'] = 'Welcome to Moodle\'">&';
1114
        $newcase['expectations'][0]['contents'] = array(
1115
            '~{\$a',
1116
            '~&amp;(amp|lt|gt|quot|\#039);',
1117
            '<div class="attachments">( *\n *)?<a href',
1118
            '<div class="subject">\n.*Hello Moodle\'"&gt;&amp;', '>Moodle Forum\'"&gt;&amp;',
1119
            '>Welcome.*Moodle\'"&gt;&amp;', '>Love Moodle&\#039;&quot;&gt;&amp;', '>101\'"&gt;&amp');
1120
        $htmlcases['HTML mail with quotes, gt, lt and ampersand  everywhere'] = array('data' => $newcase);
1121
 
1122
        // Text+image message i.e. @@PLUGINFILE@@ token handling.
1123
        $newcase = $htmlbase;
1124
        $newcase['forums'][0]['forumposts'][0]['name'] = 'HTML text and image';
1125
        $newcase['forums'][0]['forumposts'][0]['message'] = '<p>Welcome to Moodle, '
1126
            .'<img src="@@PLUGINFILE@@/Screen%20Shot%202016-03-22%20at%205.54.36%20AM%20%281%29.png"'
1127
            .' alt="" width="200" height="393" class="img-fluid" />!</p>';
1128
        $newcase['expectations'][0]['subject'] = '.*101.*HTML text and image';
1129
        $newcase['expectations'][0]['contents'] = array(
1130
            '~{\$a',
1131
            '~&(amp|lt|gt|quot|\#039);(?!course|lang|version|iosappid|androidappid)',
1132
            '<div class="attachments">( *\n *)?<a href',
1133
            '<div class="subject">\n.*HTML text and image', '>Moodle Forum',
1134
            '<p>Welcome to Moodle, '
1135
            .'<img src="https://www.example.com/moodle/tokenpluginfile.php/[^/]*/\d+/mod_forum/post/\d+/'
1136
                .'Screen%20Shot%202016-03-22%20at%205\.54\.36%20AM%20%281%29\.png"'
1137
                .' alt="" width="200" height="393" class="img-fluid" />!</p>',
1138
            '>Love Moodle', '>1\d1');
1139
        $htmlcases['HTML mail with text+image message i.e. @@PLUGINFILE@@ token handling'] = array('data' => $newcase);
1140
 
1141
        return $textcases + $htmlcases;
1142
    }
1143
 
1144
    /**
1145
     * Verify forum emails body using templates to generate the expected results.
1146
     *
1147
     * @dataProvider forum_post_email_templates_provider
1148
     * @param array $data provider samples.
1149
     */
1150
    public function test_forum_post_email_templates($data) {
1151
        global $DB;
1152
 
1153
        $this->resetAfterTest();
1154
 
1155
        // Create the course, with the specified options.
1156
        $options = array();
1157
        foreach ($data['course'] as $option => $value) {
1158
            $options[$option] = $value;
1159
        }
1160
        $course = $this->getDataGenerator()->create_course($options);
1161
 
1162
        // Create the user, with the specified options and enrol in the course.
1163
        $options = array();
1164
        foreach ($data['user'] as $option => $value) {
1165
            $options[$option] = $value;
1166
        }
1167
        $user = $this->getDataGenerator()->create_user($options);
1168
        $this->getDataGenerator()->enrol_user($user->id, $course->id);
1169
 
1170
        // Create forums, always force susbscribed (for easy), with the specified options.
1171
        $posts = array();
1172
        foreach ($data['forums'] as $dataforum) {
1173
            $forumposts = isset($dataforum['forumposts']) ? $dataforum['forumposts'] : array();
1174
            unset($dataforum['forumposts']);
1175
            $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1176
            foreach ($dataforum as $option => $value) {
1177
                $options[$option] = $value;
1178
            }
1179
            $forum = $this->getDataGenerator()->create_module('forum', $options);
1180
 
1181
            // Create posts, always for immediate delivery (for easy), with the specified options.
1182
            foreach ($forumposts as $forumpost) {
1183
                $attachments = isset($forumpost['attachments']) ? $forumpost['attachments'] : array();
1184
                unset($forumpost['attachments']);
1185
                $postoptions = array('course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id,
1186
                    'mailnow' => 1, 'attachment' => !empty($attachments));
1187
                foreach ($forumpost as $option => $value) {
1188
                    $postoptions[$option] = $value;
1189
                }
1190
                list($discussion, $post) = $this->helper_post_to_forum($forum, $user, $postoptions);
1191
                $posts[$post->subject] = $post; // Need this to verify cron output.
1192
 
1193
                // Add the attachments to the post.
1194
                if ($attachments) {
1195
                    $fs = get_file_storage();
1196
                    foreach ($attachments as $attachment) {
1197
                        $filerecord = array(
1198
                            'contextid' => \context_module::instance($forum->cmid)->id,
1199
                            'component' => 'mod_forum',
1200
                            'filearea'  => 'attachment',
1201
                            'itemid'    => $post->id,
1202
                            'filepath'  => '/',
1203
                            'filename'  => $attachment['filename']
1204
                        );
1205
                        $fs->create_file_from_string($filerecord, $attachment['filecontents']);
1206
                    }
1207
                    $DB->set_field('forum_posts', 'attachment', '1', array('id' => $post->id));
1208
                }
1209
            }
1210
        }
1211
 
1212
        // Clear the mailsink and close the messagesink.
1213
        // (surely setup should provide us this cleared but...)
1214
        $this->mailsink->clear();
1215
        $this->messagesink->close();
1216
 
1217
        $expect = [
1218
            'author' => (object) [
1219
                'userid' => $user->id,
1220
                'messages' => count($posts),
1221
            ],
1222
        ];
1223
        $this->queue_tasks_and_assert($expect);
1224
 
1225
        $this->send_notifications_and_assert($user, $posts);
1226
 
1227
        // Get the mails.
1228
        $mails = $this->mailsink->get_messages();
1229
 
1230
        // Start testing the expectations.
1231
        $expectations = $data['expectations'];
1232
 
1233
        // Assert the number is the expected.
1234
        $this->assertSame(count($expectations), count($mails));
1235
 
1236
        // Start processing mails, first localizing its expectations, then checking them.
1237
        foreach ($mails as $mail) {
1238
            // Find the corresponding expectation.
1239
            $foundexpectation = null;
1240
            foreach ($expectations as $key => $expectation) {
1241
                // All expectations must have a subject for matching.
1242
                if (!isset($expectation['subject'])) {
1243
                    $this->fail('Provider expectation missing mandatory subject');
1244
                }
1245
                if (preg_match('!' . $expectation['subject'] . '!', $mail->subject)) {
1246
                    // If we already had found the expectation, there are non-unique subjects. Fail.
1247
                    if (isset($foundexpectation)) {
1248
                        $this->fail('Multiple expectations found (by subject matching). Please make them unique.');
1249
                    }
1250
                    $foundexpectation = $expectation;
1251
                    unset($expectations[$key]);
1252
                }
1253
            }
1254
            // Arrived here, we should have found the expectations.
1255
            $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail');
1256
 
1257
            // If we have found the expectation and have contents to match, let's do it.
1258
            if (isset($foundexpectation) and isset($foundexpectation['contents'])) {
1259
                $mail->body = quoted_printable_decode($mail->body);
1260
                if (!is_array($foundexpectation['contents'])) { // Accept both string and array.
1261
                    $foundexpectation['contents'] = array($foundexpectation['contents']);
1262
                }
1263
                foreach ($foundexpectation['contents'] as $content) {
1264
                    if (strpos($content, '~') !== 0) {
1265
                        $this->assertMatchesRegularExpression('#' . $content . '#m', $mail->body);
1266
                    } else {
1267
                        preg_match('#' . substr($content, 1) . '#m', $mail->body, $matches);
1268
                        $this->assertDoesNotMatchRegularExpression('#' . substr($content, 1) . '#m', $mail->body);
1269
                    }
1270
                }
1271
            }
1272
        }
1273
 
1274
        // Finished, there should not be remaining expectations.
1275
        $this->assertCount(0, $expectations);
1276
    }
1277
 
1278
    /**
1279
     * Ensure that posts already mailed are not re-sent.
1280
     */
1281
    public function test_already_mailed() {
1282
        global $DB;
1283
 
1284
        $this->resetAfterTest(true);
1285
 
1286
        // Create a course, with a forum.
1287
        $course = $this->getDataGenerator()->create_course();
1288
 
1289
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1290
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1291
 
1292
        // Create two users enrolled in the course as students.
1293
        list($author, $recipient) = $this->helper_create_users($course, 2);
1294
 
1295
        // Post a discussion to the forum.
1296
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1297
        $DB->set_field('forum_posts', 'mailed', 1);
1298
 
1299
        // No posts shoudl be considered.
1300
        $this->queue_tasks_and_assert([]);
1301
 
1302
        // No notifications should be queued.
1303
        $this->send_notifications_and_assert($author, []);
1304
        $this->send_notifications_and_assert($recipient, []);
1305
    }
1306
 
1307
    /**
1308
     * Ensure that posts marked mailnow are not suspect to the maxeditingtime.
1309
     */
1310
    public function test_mailnow() {
1311
        global $CFG, $DB;
1312
 
1313
        // Update the maxeditingtime to 1 day so that posts won't be sent.
1314
        $CFG->maxeditingtime = DAYSECS;
1315
 
1316
        $this->resetAfterTest(true);
1317
 
1318
        // Create a course, with a forum.
1319
        $course = $this->getDataGenerator()->create_course();
1320
 
1321
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1322
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1323
 
1324
        // Create two users enrolled in the course as students.
1325
        list($author, $recipient) = $this->helper_create_users($course, 2);
1326
 
1327
        // Post a discussion to the forum.
1328
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1329
 
1330
        // Post a discussion to the forum.
1331
        list($discussion, $postmailednow) = $this->helper_post_to_forum($forum, $author, ['mailnow' => 1]);
1332
 
1333
        // Only the mailnow post should be considered.
1334
        $expect = [
1335
            'author' => (object) [
1336
                'userid' => $author->id,
1337
                'messages' => 1,
1338
            ],
1339
            'recipient' => (object) [
1340
                'userid' => $recipient->id,
1341
                'messages' => 1,
1342
            ],
1343
        ];
1344
        $this->queue_tasks_and_assert($expect);
1345
 
1346
        // No notifications should be queued.
1347
        $this->send_notifications_and_assert($author, [$postmailednow]);
1348
        $this->send_notifications_and_assert($recipient, [$postmailednow]);
1349
    }
1350
 
1351
    /**
1352
     * Ensure that if a user has no permission to view a post, then it is not sent.
1353
     */
1354
    public function test_access_coursemodule_hidden() {
1355
        global $CFG, $DB;
1356
 
1357
        $this->resetAfterTest(true);
1358
 
1359
        // Create a course, with a forum.
1360
        $course = $this->getDataGenerator()->create_course();
1361
 
1362
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1363
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1364
 
1365
        // Create two users enrolled in the course as students.
1366
        list($author, $recipient) = $this->helper_create_users($course, 2);
1367
 
1368
        // Create one users enrolled in the course as an editing teacher.
1369
        list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1370
 
1371
        // Post a discussion to the forum.
1372
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1373
 
1374
        // Hide the coursemodule.
1375
        set_coursemodule_visible($forum->cmid, 0);
1376
 
1377
        // Only the mailnow post should be considered.
1378
        $expect = [
1379
            'author' => (object) [
1380
                'userid' => $author->id,
1381
                'messages' => 1,
1382
            ],
1383
            'recipient' => (object) [
1384
                'userid' => $recipient->id,
1385
                'messages' => 1,
1386
            ],
1387
            'editor' => (object) [
1388
                'userid' => $editor->id,
1389
                'messages' => 1,
1390
            ],
1391
        ];
1392
        $this->queue_tasks_and_assert($expect);
1393
 
1394
        // No notifications should be queued.
1395
        $this->send_notifications_and_assert($author, [], true);
1396
        $this->send_notifications_and_assert($recipient, [], true);
1397
        $this->send_notifications_and_assert($editor, [$post], true);
1398
    }
1399
 
1400
    /**
1401
     * Ensure that if a user loses permission to view a post after it is queued, that it is not sent.
1402
     */
1403
    public function test_access_coursemodule_hidden_after_queue() {
1404
        global $CFG, $DB;
1405
 
1406
        $this->resetAfterTest(true);
1407
 
1408
        // Create a course, with a forum.
1409
        $course = $this->getDataGenerator()->create_course();
1410
 
1411
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1412
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1413
 
1414
        // Create two users enrolled in the course as students.
1415
        list($author, $recipient) = $this->helper_create_users($course, 2);
1416
 
1417
        // Create one users enrolled in the course as an editing teacher.
1418
        list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1419
 
1420
        // Post a discussion to the forum.
1421
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1422
 
1423
        // Only the mailnow post should be considered.
1424
        $expect = [
1425
            'author' => (object) [
1426
                'userid' => $author->id,
1427
                'messages' => 1,
1428
            ],
1429
            'recipient' => (object) [
1430
                'userid' => $recipient->id,
1431
                'messages' => 1,
1432
            ],
1433
            'editor' => (object) [
1434
                'userid' => $editor->id,
1435
                'messages' => 1,
1436
            ],
1437
        ];
1438
        $this->queue_tasks_and_assert($expect);
1439
 
1440
        // Hide the coursemodule.
1441
        set_coursemodule_visible($forum->cmid, 0);
1442
 
1443
        // No notifications should be queued for the students.
1444
        $this->send_notifications_and_assert($author, [], true);
1445
        $this->send_notifications_and_assert($recipient, [], true);
1446
 
1447
        // The editing teacher should still receive the post.
1448
        $this->send_notifications_and_assert($editor, [$post]);
1449
    }
1450
 
1451
    /**
1452
     * Ensure that messages are not sent until the timestart.
1453
     */
1454
    public function test_access_before_timestart() {
1455
        global $CFG, $DB;
1456
 
1457
        $this->resetAfterTest(true);
1458
 
1459
        // Create a course, with a forum.
1460
        $course = $this->getDataGenerator()->create_course();
1461
 
1462
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1463
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1464
 
1465
        // Create two users enrolled in the course as students.
1466
        list($author, $recipient) = $this->helper_create_users($course, 2);
1467
 
1468
        // Create one users enrolled in the course as an editing teacher.
1469
        list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1470
 
1471
        // Post a discussion to the forum.
1472
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1473
 
1474
        // Update the discussion to have a timestart in the future.
1475
        $DB->set_field('forum_discussions', 'timestart', time() + DAYSECS);
1476
 
1477
        // None should be sent.
1478
        $this->queue_tasks_and_assert([]);
1479
 
1480
        // No notifications should be queued for any user.
1481
        $this->send_notifications_and_assert($author, []);
1482
        $this->send_notifications_and_assert($recipient, []);
1483
        $this->send_notifications_and_assert($editor, []);
1484
 
1485
        // Update the discussion to have a timestart in the past.
1486
        $DB->set_field('forum_discussions', 'timestart', time() - DAYSECS);
1487
 
1488
        // Now should be sent to all.
1489
        $expect = [
1490
            'author' => (object) [
1491
                'userid' => $author->id,
1492
                'messages' => 1,
1493
            ],
1494
            'recipient' => (object) [
1495
                'userid' => $recipient->id,
1496
                'messages' => 1,
1497
            ],
1498
            'editor' => (object) [
1499
                'userid' => $editor->id,
1500
                'messages' => 1,
1501
            ],
1502
        ];
1503
        $this->queue_tasks_and_assert($expect);
1504
 
1505
        // No notifications should be queued for any user.
1506
        $this->send_notifications_and_assert($author, [$post]);
1507
        $this->send_notifications_and_assert($recipient, [$post]);
1508
        $this->send_notifications_and_assert($editor, [$post]);
1509
    }
1510
 
1511
    /**
1512
     * Ensure that messages are not sent after the timeend.
1513
     */
1514
    public function test_access_after_timeend() {
1515
        global $CFG, $DB;
1516
 
1517
        $this->resetAfterTest(true);
1518
 
1519
        // Create a course, with a forum.
1520
        $course = $this->getDataGenerator()->create_course();
1521
 
1522
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
1523
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1524
 
1525
        // Create two users enrolled in the course as students.
1526
        list($author, $recipient) = $this->helper_create_users($course, 2);
1527
 
1528
        // Create one users enrolled in the course as an editing teacher.
1529
        list($editor) = $this->helper_create_users($course, 1, 'editingteacher');
1530
 
1531
        // Post a discussion to the forum.
1532
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1533
 
1534
        // Update the discussion to have a timestart in the past.
1535
        $DB->set_field('forum_discussions', 'timeend', time() - DAYSECS);
1536
 
1537
        // None should be sent.
1538
        $this->queue_tasks_and_assert([]);
1539
 
1540
        // No notifications should be queued for any user.
1541
        $this->send_notifications_and_assert($author, []);
1542
        $this->send_notifications_and_assert($recipient, []);
1543
        $this->send_notifications_and_assert($editor, []);
1544
 
1545
        // Update the discussion to have a timestart in the past.
1546
        $DB->set_field('forum_discussions', 'timeend', time() + DAYSECS);
1547
 
1548
        // Now should be sent to all.
1549
        $expect = [
1550
            'author' => (object) [
1551
                'userid' => $author->id,
1552
                'messages' => 1,
1553
            ],
1554
            'recipient' => (object) [
1555
                'userid' => $recipient->id,
1556
                'messages' => 1,
1557
            ],
1558
            'editor' => (object) [
1559
                'userid' => $editor->id,
1560
                'messages' => 1,
1561
            ],
1562
        ];
1563
        $this->queue_tasks_and_assert($expect);
1564
 
1565
        // No notifications should be queued for any user.
1566
        $this->send_notifications_and_assert($author, [$post]);
1567
        $this->send_notifications_and_assert($recipient, [$post]);
1568
        $this->send_notifications_and_assert($editor, [$post]);
1569
    }
1570
 
1571
    /**
1572
     * Test notification comes with customdata.
1573
     */
1574
    public function test_notification_customdata() {
1575
        $this->resetAfterTest(true);
1576
 
1577
        $course = $this->getDataGenerator()->create_course();
1578
 
1579
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
1580
        $forum = $this->getDataGenerator()->create_module('forum', $options);
1581
 
1582
        list($author) = $this->helper_create_users($course, 1);
1583
        list($commenter) = $this->helper_create_users($course, 1);
1584
 
1585
        $strre = get_string('re', 'forum');
1586
 
1587
        // New posts should not have Re: in the subject.
1588
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
1589
        $expect = [
1590
            'author' => (object) [
1591
                'userid' => $author->id,
1592
                'messages' => 1,
1593
            ],
1594
            'commenter' => (object) [
1595
                'userid' => $commenter->id,
1596
                'messages' => 1,
1597
            ],
1598
        ];
1599
        $this->queue_tasks_and_assert($expect);
1600
 
1601
        $this->send_notifications_and_assert($author, [$post]);
1602
        $this->send_notifications_and_assert($commenter, [$post]);
1603
        $messages = $this->messagesink->get_messages_by_component('mod_forum');
1604
        $messages = reset($messages);
1605
        $customdata = json_decode($messages->customdata);
1606
        $this->assertEquals($forum->id, $customdata->instance);
1607
        $this->assertEquals($forum->cmid, $customdata->cmid);
1608
        $this->assertEquals($post->id, $customdata->postid);
1609
        $this->assertEquals($discussion->id, $customdata->discussionid);
1610
        $this->assertObjectHasProperty('notificationiconurl', $customdata);
1611
        $this->assertObjectHasProperty('actionbuttons', $customdata);
1612
        $this->assertCount(1, (array) $customdata->actionbuttons);
1613
    }
1614
}