Proyectos de Subversion Moodle

Rev

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