Proyectos de Subversion Moodle

Rev

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