Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

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