ef135e5eac33a300fc469d12b2176e29d78fbeb9
[moodle.git] / mod / forum / tests / lib_test.php
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/>.
17 /**
18  * The module forums tests
19  *
20  * @package    mod_forum
21  * @copyright  2013 Frédéric Massart
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
28 require_once($CFG->dirroot . '/mod/forum/lib.php');
29 require_once($CFG->dirroot . '/rating/lib.php');
31 class mod_forum_lib_testcase extends advanced_testcase {
33     public function setUp() {
34         // We must clear the subscription caches. This has to be done both before each test, and after in case of other
35         // tests using these functions.
36         \mod_forum\subscriptions::reset_forum_cache();
37     }
39     public function tearDown() {
40         // We must clear the subscription caches. This has to be done both before each test, and after in case of other
41         // tests using these functions.
42         \mod_forum\subscriptions::reset_forum_cache();
43     }
45     public function test_forum_trigger_content_uploaded_event() {
46         $this->resetAfterTest();
48         $user = $this->getDataGenerator()->create_user();
49         $course = $this->getDataGenerator()->create_course();
50         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
51         $context = context_module::instance($forum->cmid);
53         $this->setUser($user->id);
54         $fakepost = (object) array('id' => 123, 'message' => 'Yay!', 'discussion' => 100);
55         $cm = get_coursemodule_from_instance('forum', $forum->id);
57         $fs = get_file_storage();
58         $dummy = (object) array(
59             'contextid' => $context->id,
60             'component' => 'mod_forum',
61             'filearea' => 'attachment',
62             'itemid' => $fakepost->id,
63             'filepath' => '/',
64             'filename' => 'myassignmnent.pdf'
65         );
66         $fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
68         $data = new stdClass();
69         $sink = $this->redirectEvents();
70         forum_trigger_content_uploaded_event($fakepost, $cm, 'some triggered from value');
71         $events = $sink->get_events();
73         $this->assertCount(1, $events);
74         $event = reset($events);
75         $this->assertInstanceOf('\mod_forum\event\assessable_uploaded', $event);
76         $this->assertEquals($context->id, $event->contextid);
77         $this->assertEquals($fakepost->id, $event->objectid);
78         $this->assertEquals($fakepost->message, $event->other['content']);
79         $this->assertEquals($fakepost->discussion, $event->other['discussionid']);
80         $this->assertCount(1, $event->other['pathnamehashes']);
81         $this->assertEquals($fi->get_pathnamehash(), $event->other['pathnamehashes'][0]);
82         $expected = new stdClass();
83         $expected->modulename = 'forum';
84         $expected->name = 'some triggered from value';
85         $expected->cmid = $forum->cmid;
86         $expected->itemid = $fakepost->id;
87         $expected->courseid = $course->id;
88         $expected->userid = $user->id;
89         $expected->content = $fakepost->message;
90         $expected->pathnamehashes = array($fi->get_pathnamehash());
91         $this->assertEventLegacyData($expected, $event);
92         $this->assertEventContextNotUsed($event);
93     }
95     public function test_forum_get_courses_user_posted_in() {
96         $this->resetAfterTest();
98         $user1 = $this->getDataGenerator()->create_user();
99         $user2 = $this->getDataGenerator()->create_user();
100         $user3 = $this->getDataGenerator()->create_user();
102         $course1 = $this->getDataGenerator()->create_course();
103         $course2 = $this->getDataGenerator()->create_course();
104         $course3 = $this->getDataGenerator()->create_course();
106         // Create 3 forums, one in each course.
107         $record = new stdClass();
108         $record->course = $course1->id;
109         $forum1 = $this->getDataGenerator()->create_module('forum', $record);
111         $record = new stdClass();
112         $record->course = $course2->id;
113         $forum2 = $this->getDataGenerator()->create_module('forum', $record);
115         $record = new stdClass();
116         $record->course = $course3->id;
117         $forum3 = $this->getDataGenerator()->create_module('forum', $record);
119         // Add a second forum in course 1.
120         $record = new stdClass();
121         $record->course = $course1->id;
122         $forum4 = $this->getDataGenerator()->create_module('forum', $record);
124         // Add discussions to course 1 started by user1.
125         $record = new stdClass();
126         $record->course = $course1->id;
127         $record->userid = $user1->id;
128         $record->forum = $forum1->id;
129         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
131         $record = new stdClass();
132         $record->course = $course1->id;
133         $record->userid = $user1->id;
134         $record->forum = $forum4->id;
135         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
137         // Add discussions to course2 started by user1.
138         $record = new stdClass();
139         $record->course = $course2->id;
140         $record->userid = $user1->id;
141         $record->forum = $forum2->id;
142         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
144         // Add discussions to course 3 started by user2.
145         $record = new stdClass();
146         $record->course = $course3->id;
147         $record->userid = $user2->id;
148         $record->forum = $forum3->id;
149         $discussion3 = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
151         // Add post to course 3 by user1.
152         $record = new stdClass();
153         $record->course = $course3->id;
154         $record->userid = $user1->id;
155         $record->forum = $forum3->id;
156         $record->discussion = $discussion3->id;
157         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
159         // User 3 hasn't posted anything, so shouldn't get any results.
160         $user3courses = forum_get_courses_user_posted_in($user3);
161         $this->assertEmpty($user3courses);
163         // User 2 has only posted in course3.
164         $user2courses = forum_get_courses_user_posted_in($user2);
165         $this->assertCount(1, $user2courses);
166         $user2course = array_shift($user2courses);
167         $this->assertEquals($course3->id, $user2course->id);
168         $this->assertEquals($course3->shortname, $user2course->shortname);
170         // User 1 has posted in all 3 courses.
171         $user1courses = forum_get_courses_user_posted_in($user1);
172         $this->assertCount(3, $user1courses);
173         foreach ($user1courses as $course) {
174             $this->assertContains($course->id, array($course1->id, $course2->id, $course3->id));
175             $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname,
176                 $course3->shortname));
178         }
180         // User 1 has only started a discussion in course 1 and 2 though.
181         $user1courses = forum_get_courses_user_posted_in($user1, true);
182         $this->assertCount(2, $user1courses);
183         foreach ($user1courses as $course) {
184             $this->assertContains($course->id, array($course1->id, $course2->id));
185             $this->assertContains($course->shortname, array($course1->shortname, $course2->shortname));
186         }
187     }
189     /**
190      * Test the logic in the forum_tp_can_track_forums() function.
191      */
192     public function test_forum_tp_can_track_forums() {
193         global $CFG;
195         $this->resetAfterTest();
197         $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
198         $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
199         $course = $this->getDataGenerator()->create_course();
200         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
201         $forumoff = $this->getDataGenerator()->create_module('forum', $options);
203         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
204         $forumforce = $this->getDataGenerator()->create_module('forum', $options);
206         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
207         $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
209         // Allow force.
210         $CFG->forum_allowforcedreadtracking = 1;
212         // User on, forum off, should be off.
213         $result = forum_tp_can_track_forums($forumoff, $useron);
214         $this->assertEquals(false, $result);
216         // User on, forum on, should be on.
217         $result = forum_tp_can_track_forums($forumforce, $useron);
218         $this->assertEquals(true, $result);
220         // User on, forum optional, should be on.
221         $result = forum_tp_can_track_forums($forumoptional, $useron);
222         $this->assertEquals(true, $result);
224         // User off, forum off, should be off.
225         $result = forum_tp_can_track_forums($forumoff, $useroff);
226         $this->assertEquals(false, $result);
228         // User off, forum force, should be on.
229         $result = forum_tp_can_track_forums($forumforce, $useroff);
230         $this->assertEquals(true, $result);
232         // User off, forum optional, should be off.
233         $result = forum_tp_can_track_forums($forumoptional, $useroff);
234         $this->assertEquals(false, $result);
236         // Don't allow force.
237         $CFG->forum_allowforcedreadtracking = 0;
239         // User on, forum off, should be off.
240         $result = forum_tp_can_track_forums($forumoff, $useron);
241         $this->assertEquals(false, $result);
243         // User on, forum on, should be on.
244         $result = forum_tp_can_track_forums($forumforce, $useron);
245         $this->assertEquals(true, $result);
247         // User on, forum optional, should be on.
248         $result = forum_tp_can_track_forums($forumoptional, $useron);
249         $this->assertEquals(true, $result);
251         // User off, forum off, should be off.
252         $result = forum_tp_can_track_forums($forumoff, $useroff);
253         $this->assertEquals(false, $result);
255         // User off, forum force, should be off.
256         $result = forum_tp_can_track_forums($forumforce, $useroff);
257         $this->assertEquals(false, $result);
259         // User off, forum optional, should be off.
260         $result = forum_tp_can_track_forums($forumoptional, $useroff);
261         $this->assertEquals(false, $result);
263     }
265     /**
266      * Test the logic in the test_forum_tp_is_tracked() function.
267      */
268     public function test_forum_tp_is_tracked() {
269         global $CFG;
271         $this->resetAfterTest();
273         $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
274         $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
275         $course = $this->getDataGenerator()->create_course();
276         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
277         $forumoff = $this->getDataGenerator()->create_module('forum', $options);
279         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
280         $forumforce = $this->getDataGenerator()->create_module('forum', $options);
282         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
283         $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
285         // Allow force.
286         $CFG->forum_allowforcedreadtracking = 1;
288         // User on, forum off, should be off.
289         $result = forum_tp_is_tracked($forumoff, $useron);
290         $this->assertEquals(false, $result);
292         // User on, forum force, should be on.
293         $result = forum_tp_is_tracked($forumforce, $useron);
294         $this->assertEquals(true, $result);
296         // User on, forum optional, should be on.
297         $result = forum_tp_is_tracked($forumoptional, $useron);
298         $this->assertEquals(true, $result);
300         // User off, forum off, should be off.
301         $result = forum_tp_is_tracked($forumoff, $useroff);
302         $this->assertEquals(false, $result);
304         // User off, forum force, should be on.
305         $result = forum_tp_is_tracked($forumforce, $useroff);
306         $this->assertEquals(true, $result);
308         // User off, forum optional, should be off.
309         $result = forum_tp_is_tracked($forumoptional, $useroff);
310         $this->assertEquals(false, $result);
312         // Don't allow force.
313         $CFG->forum_allowforcedreadtracking = 0;
315         // User on, forum off, should be off.
316         $result = forum_tp_is_tracked($forumoff, $useron);
317         $this->assertEquals(false, $result);
319         // User on, forum force, should be on.
320         $result = forum_tp_is_tracked($forumforce, $useron);
321         $this->assertEquals(true, $result);
323         // User on, forum optional, should be on.
324         $result = forum_tp_is_tracked($forumoptional, $useron);
325         $this->assertEquals(true, $result);
327         // User off, forum off, should be off.
328         $result = forum_tp_is_tracked($forumoff, $useroff);
329         $this->assertEquals(false, $result);
331         // User off, forum force, should be off.
332         $result = forum_tp_is_tracked($forumforce, $useroff);
333         $this->assertEquals(false, $result);
335         // User off, forum optional, should be off.
336         $result = forum_tp_is_tracked($forumoptional, $useroff);
337         $this->assertEquals(false, $result);
339         // Stop tracking so we can test again.
340         forum_tp_stop_tracking($forumforce->id, $useron->id);
341         forum_tp_stop_tracking($forumoptional->id, $useron->id);
342         forum_tp_stop_tracking($forumforce->id, $useroff->id);
343         forum_tp_stop_tracking($forumoptional->id, $useroff->id);
345         // Allow force.
346         $CFG->forum_allowforcedreadtracking = 1;
348         // User on, preference off, forum force, should be on.
349         $result = forum_tp_is_tracked($forumforce, $useron);
350         $this->assertEquals(true, $result);
352         // User on, preference off, forum optional, should be on.
353         $result = forum_tp_is_tracked($forumoptional, $useron);
354         $this->assertEquals(false, $result);
356         // User off, preference off, forum force, should be on.
357         $result = forum_tp_is_tracked($forumforce, $useroff);
358         $this->assertEquals(true, $result);
360         // User off, preference off, forum optional, should be off.
361         $result = forum_tp_is_tracked($forumoptional, $useroff);
362         $this->assertEquals(false, $result);
364         // Don't allow force.
365         $CFG->forum_allowforcedreadtracking = 0;
367         // User on, preference off, forum force, should be on.
368         $result = forum_tp_is_tracked($forumforce, $useron);
369         $this->assertEquals(false, $result);
371         // User on, preference off, forum optional, should be on.
372         $result = forum_tp_is_tracked($forumoptional, $useron);
373         $this->assertEquals(false, $result);
375         // User off, preference off, forum force, should be off.
376         $result = forum_tp_is_tracked($forumforce, $useroff);
377         $this->assertEquals(false, $result);
379         // User off, preference off, forum optional, should be off.
380         $result = forum_tp_is_tracked($forumoptional, $useroff);
381         $this->assertEquals(false, $result);
382     }
384     /**
385      * Test the logic in the forum_tp_get_course_unread_posts() function.
386      */
387     public function test_forum_tp_get_course_unread_posts() {
388         global $CFG;
390         $this->resetAfterTest();
392         $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
393         $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
394         $course = $this->getDataGenerator()->create_course();
395         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
396         $forumoff = $this->getDataGenerator()->create_module('forum', $options);
398         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
399         $forumforce = $this->getDataGenerator()->create_module('forum', $options);
401         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
402         $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
404         // Add discussions to the tracking off forum.
405         $record = new stdClass();
406         $record->course = $course->id;
407         $record->userid = $useron->id;
408         $record->forum = $forumoff->id;
409         $discussionoff = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
411         // Add discussions to the tracking forced forum.
412         $record = new stdClass();
413         $record->course = $course->id;
414         $record->userid = $useron->id;
415         $record->forum = $forumforce->id;
416         $discussionforce = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
418         // Add post to the tracking forced discussion.
419         $record = new stdClass();
420         $record->course = $course->id;
421         $record->userid = $useroff->id;
422         $record->forum = $forumforce->id;
423         $record->discussion = $discussionforce->id;
424         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
426         // Add discussions to the tracking optional forum.
427         $record = new stdClass();
428         $record->course = $course->id;
429         $record->userid = $useron->id;
430         $record->forum = $forumoptional->id;
431         $discussionoptional = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
433         // Allow force.
434         $CFG->forum_allowforcedreadtracking = 1;
436         $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
437         $this->assertEquals(2, count($result));
438         $this->assertEquals(false, isset($result[$forumoff->id]));
439         $this->assertEquals(true, isset($result[$forumforce->id]));
440         $this->assertEquals(2, $result[$forumforce->id]->unread);
441         $this->assertEquals(true, isset($result[$forumoptional->id]));
442         $this->assertEquals(1, $result[$forumoptional->id]->unread);
444         $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
445         $this->assertEquals(1, count($result));
446         $this->assertEquals(false, isset($result[$forumoff->id]));
447         $this->assertEquals(true, isset($result[$forumforce->id]));
448         $this->assertEquals(2, $result[$forumforce->id]->unread);
449         $this->assertEquals(false, isset($result[$forumoptional->id]));
451         // Don't allow force.
452         $CFG->forum_allowforcedreadtracking = 0;
454         $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
455         $this->assertEquals(2, count($result));
456         $this->assertEquals(false, isset($result[$forumoff->id]));
457         $this->assertEquals(true, isset($result[$forumforce->id]));
458         $this->assertEquals(2, $result[$forumforce->id]->unread);
459         $this->assertEquals(true, isset($result[$forumoptional->id]));
460         $this->assertEquals(1, $result[$forumoptional->id]->unread);
462         $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
463         $this->assertEquals(0, count($result));
464         $this->assertEquals(false, isset($result[$forumoff->id]));
465         $this->assertEquals(false, isset($result[$forumforce->id]));
466         $this->assertEquals(false, isset($result[$forumoptional->id]));
468         // Stop tracking so we can test again.
469         forum_tp_stop_tracking($forumforce->id, $useron->id);
470         forum_tp_stop_tracking($forumoptional->id, $useron->id);
471         forum_tp_stop_tracking($forumforce->id, $useroff->id);
472         forum_tp_stop_tracking($forumoptional->id, $useroff->id);
474         // Allow force.
475         $CFG->forum_allowforcedreadtracking = 1;
477         $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
478         $this->assertEquals(1, count($result));
479         $this->assertEquals(false, isset($result[$forumoff->id]));
480         $this->assertEquals(true, isset($result[$forumforce->id]));
481         $this->assertEquals(2, $result[$forumforce->id]->unread);
482         $this->assertEquals(false, isset($result[$forumoptional->id]));
484         $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
485         $this->assertEquals(1, count($result));
486         $this->assertEquals(false, isset($result[$forumoff->id]));
487         $this->assertEquals(true, isset($result[$forumforce->id]));
488         $this->assertEquals(2, $result[$forumforce->id]->unread);
489         $this->assertEquals(false, isset($result[$forumoptional->id]));
491         // Don't allow force.
492         $CFG->forum_allowforcedreadtracking = 0;
494         $result = forum_tp_get_course_unread_posts($useron->id, $course->id);
495         $this->assertEquals(0, count($result));
496         $this->assertEquals(false, isset($result[$forumoff->id]));
497         $this->assertEquals(false, isset($result[$forumforce->id]));
498         $this->assertEquals(false, isset($result[$forumoptional->id]));
500         $result = forum_tp_get_course_unread_posts($useroff->id, $course->id);
501         $this->assertEquals(0, count($result));
502         $this->assertEquals(false, isset($result[$forumoff->id]));
503         $this->assertEquals(false, isset($result[$forumforce->id]));
504         $this->assertEquals(false, isset($result[$forumoptional->id]));
505     }
507     /**
508      * Test the logic in the test_forum_tp_get_untracked_forums() function.
509      */
510     public function test_forum_tp_get_untracked_forums() {
511         global $CFG;
513         $this->resetAfterTest();
515         $useron = $this->getDataGenerator()->create_user(array('trackforums' => 1));
516         $useroff = $this->getDataGenerator()->create_user(array('trackforums' => 0));
517         $course = $this->getDataGenerator()->create_course();
518         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OFF); // Off.
519         $forumoff = $this->getDataGenerator()->create_module('forum', $options);
521         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_FORCED); // On.
522         $forumforce = $this->getDataGenerator()->create_module('forum', $options);
524         $options = array('course' => $course->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL); // Optional.
525         $forumoptional = $this->getDataGenerator()->create_module('forum', $options);
527         // Allow force.
528         $CFG->forum_allowforcedreadtracking = 1;
530         // On user with force on.
531         $result = forum_tp_get_untracked_forums($useron->id, $course->id);
532         $this->assertEquals(1, count($result));
533         $this->assertEquals(true, isset($result[$forumoff->id]));
535         // Off user with force on.
536         $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
537         $this->assertEquals(2, count($result));
538         $this->assertEquals(true, isset($result[$forumoff->id]));
539         $this->assertEquals(true, isset($result[$forumoptional->id]));
541         // Don't allow force.
542         $CFG->forum_allowforcedreadtracking = 0;
544         // On user with force off.
545         $result = forum_tp_get_untracked_forums($useron->id, $course->id);
546         $this->assertEquals(1, count($result));
547         $this->assertEquals(true, isset($result[$forumoff->id]));
549         // Off user with force off.
550         $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
551         $this->assertEquals(3, count($result));
552         $this->assertEquals(true, isset($result[$forumoff->id]));
553         $this->assertEquals(true, isset($result[$forumoptional->id]));
554         $this->assertEquals(true, isset($result[$forumforce->id]));
556         // Stop tracking so we can test again.
557         forum_tp_stop_tracking($forumforce->id, $useron->id);
558         forum_tp_stop_tracking($forumoptional->id, $useron->id);
559         forum_tp_stop_tracking($forumforce->id, $useroff->id);
560         forum_tp_stop_tracking($forumoptional->id, $useroff->id);
562         // Allow force.
563         $CFG->forum_allowforcedreadtracking = 1;
565         // On user with force on.
566         $result = forum_tp_get_untracked_forums($useron->id, $course->id);
567         $this->assertEquals(2, count($result));
568         $this->assertEquals(true, isset($result[$forumoff->id]));
569         $this->assertEquals(true, isset($result[$forumoptional->id]));
571         // Off user with force on.
572         $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
573         $this->assertEquals(2, count($result));
574         $this->assertEquals(true, isset($result[$forumoff->id]));
575         $this->assertEquals(true, isset($result[$forumoptional->id]));
577         // Don't allow force.
578         $CFG->forum_allowforcedreadtracking = 0;
580         // On user with force off.
581         $result = forum_tp_get_untracked_forums($useron->id, $course->id);
582         $this->assertEquals(3, count($result));
583         $this->assertEquals(true, isset($result[$forumoff->id]));
584         $this->assertEquals(true, isset($result[$forumoptional->id]));
585         $this->assertEquals(true, isset($result[$forumforce->id]));
587         // Off user with force off.
588         $result = forum_tp_get_untracked_forums($useroff->id, $course->id);
589         $this->assertEquals(3, count($result));
590         $this->assertEquals(true, isset($result[$forumoff->id]));
591         $this->assertEquals(true, isset($result[$forumoptional->id]));
592         $this->assertEquals(true, isset($result[$forumforce->id]));
593     }
595     /**
596      * Test subscription using automatic subscription on create.
597      */
598     public function test_forum_auto_subscribe_on_create() {
599         global $CFG;
601         $this->resetAfterTest();
603         $usercount = 5;
604         $course = $this->getDataGenerator()->create_course();
605         $users = array();
607         for ($i = 0; $i < $usercount; $i++) {
608             $user = $this->getDataGenerator()->create_user();
609             $users[] = $user;
610             $this->getDataGenerator()->enrol_user($user->id, $course->id);
611         }
613         $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE); // Automatic Subscription.
614         $forum = $this->getDataGenerator()->create_module('forum', $options);
616         $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
617         $this->assertEquals($usercount, count($result));
618         foreach ($users as $user) {
619             $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
620         }
621     }
623     /**
624      * Test subscription using forced subscription on create.
625      */
626     public function test_forum_forced_subscribe_on_create() {
627         global $CFG;
629         $this->resetAfterTest();
631         $usercount = 5;
632         $course = $this->getDataGenerator()->create_course();
633         $users = array();
635         for ($i = 0; $i < $usercount; $i++) {
636             $user = $this->getDataGenerator()->create_user();
637             $users[] = $user;
638             $this->getDataGenerator()->enrol_user($user->id, $course->id);
639         }
641         $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE); // Forced subscription.
642         $forum = $this->getDataGenerator()->create_module('forum', $options);
644         $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
645         $this->assertEquals($usercount, count($result));
646         foreach ($users as $user) {
647             $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
648         }
649     }
651     /**
652      * Test subscription using optional subscription on create.
653      */
654     public function test_forum_optional_subscribe_on_create() {
655         global $CFG;
657         $this->resetAfterTest();
659         $usercount = 5;
660         $course = $this->getDataGenerator()->create_course();
661         $users = array();
663         for ($i = 0; $i < $usercount; $i++) {
664             $user = $this->getDataGenerator()->create_user();
665             $users[] = $user;
666             $this->getDataGenerator()->enrol_user($user->id, $course->id);
667         }
669         $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE); // Subscription optional.
670         $forum = $this->getDataGenerator()->create_module('forum', $options);
672         $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
673         // No subscriptions by default.
674         $this->assertEquals(0, count($result));
675         foreach ($users as $user) {
676             $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
677         }
678     }
680     /**
681      * Test subscription using disallow subscription on create.
682      */
683     public function test_forum_disallow_subscribe_on_create() {
684         global $CFG;
686         $this->resetAfterTest();
688         $usercount = 5;
689         $course = $this->getDataGenerator()->create_course();
690         $users = array();
692         for ($i = 0; $i < $usercount; $i++) {
693             $user = $this->getDataGenerator()->create_user();
694             $users[] = $user;
695             $this->getDataGenerator()->enrol_user($user->id, $course->id);
696         }
698         $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE); // Subscription prevented.
699         $forum = $this->getDataGenerator()->create_module('forum', $options);
701         $result = \mod_forum\subscriptions::fetch_subscribed_users($forum);
702         // No subscriptions by default.
703         $this->assertEquals(0, count($result));
704         foreach ($users as $user) {
705             $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
706         }
707     }
709     /**
710      * Test that context fetching returns the appropriate context.
711      */
712     public function test_forum_get_context() {
713         global $DB, $PAGE;
715         $this->resetAfterTest();
717         // Setup test data.
718         $course = $this->getDataGenerator()->create_course();
719         $coursecontext = \context_course::instance($course->id);
721         $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
722         $forum = $this->getDataGenerator()->create_module('forum', $options);
723         $forumcm = get_coursemodule_from_instance('forum', $forum->id);
724         $forumcontext = \context_module::instance($forumcm->id);
726         // First check that specifying the context results in the correct context being returned.
727         // Do this before we set up the page object and we should return from the coursemodule record.
728         // There should be no DB queries here because the context type was correct.
729         $startcount = $DB->perf_get_reads();
730         $result = forum_get_context($forum->id, $forumcontext);
731         $aftercount = $DB->perf_get_reads();
732         $this->assertEquals($forumcontext, $result);
733         $this->assertEquals(0, $aftercount - $startcount);
735         // And a context which is not the correct type.
736         // This tests will result in a DB query to fetch the course_module.
737         $startcount = $DB->perf_get_reads();
738         $result = forum_get_context($forum->id, $coursecontext);
739         $aftercount = $DB->perf_get_reads();
740         $this->assertEquals($forumcontext, $result);
741         $this->assertEquals(1, $aftercount - $startcount);
743         // Now do not specify a context at all.
744         // This tests will result in a DB query to fetch the course_module.
745         $startcount = $DB->perf_get_reads();
746         $result = forum_get_context($forum->id);
747         $aftercount = $DB->perf_get_reads();
748         $this->assertEquals($forumcontext, $result);
749         $this->assertEquals(1, $aftercount - $startcount);
751         // Set up the default page event to use the forum.
752         $PAGE = new moodle_page();
753         $PAGE->set_context($forumcontext);
754         $PAGE->set_cm($forumcm, $course, $forum);
756         // Now specify a context which is not a context_module.
757         // There should be no DB queries here because we use the PAGE.
758         $startcount = $DB->perf_get_reads();
759         $result = forum_get_context($forum->id, $coursecontext);
760         $aftercount = $DB->perf_get_reads();
761         $this->assertEquals($forumcontext, $result);
762         $this->assertEquals(0, $aftercount - $startcount);
764         // Now do not specify a context at all.
765         // There should be no DB queries here because we use the PAGE.
766         $startcount = $DB->perf_get_reads();
767         $result = forum_get_context($forum->id);
768         $aftercount = $DB->perf_get_reads();
769         $this->assertEquals($forumcontext, $result);
770         $this->assertEquals(0, $aftercount - $startcount);
772         // Now specify the page context of the course instead..
773         $PAGE = new moodle_page();
774         $PAGE->set_context($coursecontext);
776         // Now specify a context which is not a context_module.
777         // This tests will result in a DB query to fetch the course_module.
778         $startcount = $DB->perf_get_reads();
779         $result = forum_get_context($forum->id, $coursecontext);
780         $aftercount = $DB->perf_get_reads();
781         $this->assertEquals($forumcontext, $result);
782         $this->assertEquals(1, $aftercount - $startcount);
784         // Now do not specify a context at all.
785         // This tests will result in a DB query to fetch the course_module.
786         $startcount = $DB->perf_get_reads();
787         $result = forum_get_context($forum->id);
788         $aftercount = $DB->perf_get_reads();
789         $this->assertEquals($forumcontext, $result);
790         $this->assertEquals(1, $aftercount - $startcount);
791     }
793     /**
794      * Test getting the neighbour threads of a discussion.
795      */
796     public function test_forum_get_neighbours() {
797         global $CFG, $DB;
798         $this->resetAfterTest();
800         $timenow = time();
801         $timenext = $timenow;
803         // Setup test data.
804         $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
805         $course = $this->getDataGenerator()->create_course();
806         $user = $this->getDataGenerator()->create_user();
807         $user2 = $this->getDataGenerator()->create_user();
809         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
810         $cm = get_coursemodule_from_instance('forum', $forum->id);
811         $context = context_module::instance($cm->id);
813         $record = new stdClass();
814         $record->course = $course->id;
815         $record->userid = $user->id;
816         $record->forum = $forum->id;
817         $record->timemodified = time();
818         $disc1 = $forumgen->create_discussion($record);
819         $record->timemodified++;
820         $disc2 = $forumgen->create_discussion($record);
821         $record->timemodified++;
822         $disc3 = $forumgen->create_discussion($record);
823         $record->timemodified++;
824         $disc4 = $forumgen->create_discussion($record);
825         $record->timemodified++;
826         $disc5 = $forumgen->create_discussion($record);
828         // Getting the neighbours.
829         $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
830         $this->assertEmpty($neighbours['prev']);
831         $this->assertEquals($disc2->id, $neighbours['next']->id);
833         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
834         $this->assertEquals($disc1->id, $neighbours['prev']->id);
835         $this->assertEquals($disc3->id, $neighbours['next']->id);
837         $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
838         $this->assertEquals($disc2->id, $neighbours['prev']->id);
839         $this->assertEquals($disc4->id, $neighbours['next']->id);
841         $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
842         $this->assertEquals($disc3->id, $neighbours['prev']->id);
843         $this->assertEquals($disc5->id, $neighbours['next']->id);
845         $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
846         $this->assertEquals($disc4->id, $neighbours['prev']->id);
847         $this->assertEmpty($neighbours['next']);
849         // Post in some discussions. We manually update the discussion record because
850         // the data generator plays with timemodified in a way that would break this test.
851         $record->timemodified++;
852         $disc1->timemodified = $record->timemodified;
853         $DB->update_record('forum_discussions', $disc1);
855         $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
856         $this->assertEquals($disc4->id, $neighbours['prev']->id);
857         $this->assertEquals($disc1->id, $neighbours['next']->id);
859         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
860         $this->assertEmpty($neighbours['prev']);
861         $this->assertEquals($disc3->id, $neighbours['next']->id);
863         $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
864         $this->assertEquals($disc5->id, $neighbours['prev']->id);
865         $this->assertEmpty($neighbours['next']);
867         // After some discussions were created.
868         $record->timemodified++;
869         $disc6 = $forumgen->create_discussion($record);
870         $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
871         $this->assertEquals($disc1->id, $neighbours['prev']->id);
872         $this->assertEmpty($neighbours['next']);
874         $record->timemodified++;
875         $disc7 = $forumgen->create_discussion($record);
876         $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum);
877         $this->assertEquals($disc6->id, $neighbours['prev']->id);
878         $this->assertEmpty($neighbours['next']);
880         // Adding timed discussions.
881         $CFG->forum_enabletimedposts = true;
882         $now = $record->timemodified;
883         $past = $now - 60;
884         $future = $now + 60;
886         $record = new stdClass();
887         $record->course = $course->id;
888         $record->userid = $user->id;
889         $record->forum = $forum->id;
890         $record->timestart = $past;
891         $record->timeend = $future;
892         $record->timemodified = $now;
893         $record->timemodified++;
894         $disc8 = $forumgen->create_discussion($record);
895         $record->timemodified++;
896         $record->timestart = $future;
897         $record->timeend = 0;
898         $disc9 = $forumgen->create_discussion($record);
899         $record->timemodified++;
900         $record->timestart = 0;
901         $record->timeend = 0;
902         $disc10 = $forumgen->create_discussion($record);
903         $record->timemodified++;
904         $record->timestart = 0;
905         $record->timeend = $past;
906         $disc11 = $forumgen->create_discussion($record);
907         $record->timemodified++;
908         $record->timestart = $past;
909         $record->timeend = $future;
910         $disc12 = $forumgen->create_discussion($record);
911         $record->timemodified++;
912         $record->timestart = $future + 1; // Should be last post for those that can see it.
913         $record->timeend = 0;
914         $disc13 = $forumgen->create_discussion($record);
916         // Admin user ignores the timed settings of discussions.
917         // Post ordering taking into account timestart:
918         //  8 = t
919         // 10 = t+3
920         // 11 = t+4
921         // 12 = t+5
922         //  9 = t+60
923         // 13 = t+61.
924         $this->setAdminUser();
925         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
926         $this->assertEquals($disc7->id, $neighbours['prev']->id);
927         $this->assertEquals($disc10->id, $neighbours['next']->id);
929         $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
930         $this->assertEquals($disc12->id, $neighbours['prev']->id);
931         $this->assertEquals($disc13->id, $neighbours['next']->id);
933         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
934         $this->assertEquals($disc8->id, $neighbours['prev']->id);
935         $this->assertEquals($disc11->id, $neighbours['next']->id);
937         $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
938         $this->assertEquals($disc10->id, $neighbours['prev']->id);
939         $this->assertEquals($disc12->id, $neighbours['next']->id);
941         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
942         $this->assertEquals($disc11->id, $neighbours['prev']->id);
943         $this->assertEquals($disc9->id, $neighbours['next']->id);
945         $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum);
946         $this->assertEquals($disc9->id, $neighbours['prev']->id);
947         $this->assertEmpty($neighbours['next']);
949         // Normal user can see their own timed discussions.
950         $this->setUser($user);
951         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
952         $this->assertEquals($disc7->id, $neighbours['prev']->id);
953         $this->assertEquals($disc10->id, $neighbours['next']->id);
955         $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
956         $this->assertEquals($disc12->id, $neighbours['prev']->id);
957         $this->assertEquals($disc13->id, $neighbours['next']->id);
959         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
960         $this->assertEquals($disc8->id, $neighbours['prev']->id);
961         $this->assertEquals($disc11->id, $neighbours['next']->id);
963         $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
964         $this->assertEquals($disc10->id, $neighbours['prev']->id);
965         $this->assertEquals($disc12->id, $neighbours['next']->id);
967         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
968         $this->assertEquals($disc11->id, $neighbours['prev']->id);
969         $this->assertEquals($disc9->id, $neighbours['next']->id);
971         $neighbours = forum_get_discussion_neighbours($cm, $disc13, $forum);
972         $this->assertEquals($disc9->id, $neighbours['prev']->id);
973         $this->assertEmpty($neighbours['next']);
975         // Normal user does not ignore timed settings.
976         $this->setUser($user2);
977         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
978         $this->assertEquals($disc7->id, $neighbours['prev']->id);
979         $this->assertEquals($disc10->id, $neighbours['next']->id);
981         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
982         $this->assertEquals($disc8->id, $neighbours['prev']->id);
983         $this->assertEquals($disc12->id, $neighbours['next']->id);
985         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
986         $this->assertEquals($disc10->id, $neighbours['prev']->id);
987         $this->assertEmpty($neighbours['next']);
989         // Reset to normal mode.
990         $CFG->forum_enabletimedposts = false;
991         $this->setAdminUser();
993         // Two discussions with identical timemodified will sort by id.
994         $record->timemodified += 25;
995         $DB->update_record('forum_discussions', (object) array('id' => $disc3->id, 'timemodified' => $record->timemodified));
996         $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified));
997         $DB->update_record('forum_discussions', (object) array('id' => $disc12->id, 'timemodified' => $record->timemodified - 5));
998         $disc2 = $DB->get_record('forum_discussions', array('id' => $disc2->id));
999         $disc3 = $DB->get_record('forum_discussions', array('id' => $disc3->id));
1001         $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1002         $this->assertEquals($disc2->id, $neighbours['prev']->id);
1003         $this->assertEmpty($neighbours['next']);
1005         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1006         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1007         $this->assertEquals($disc3->id, $neighbours['next']->id);
1009         // Set timemodified to not be identical.
1010         $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $record->timemodified - 1));
1012         // Test pinned posts behave correctly.
1013         $disc8->pinned = FORUM_DISCUSSION_PINNED;
1014         $DB->update_record('forum_discussions', (object) array('id' => $disc8->id, 'pinned' => $disc8->pinned));
1015         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1016         $this->assertEquals($disc3->id, $neighbours['prev']->id);
1017         $this->assertEmpty($neighbours['next']);
1019         $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1020         $this->assertEquals($disc2->id, $neighbours['prev']->id);
1021         $this->assertEquals($disc8->id, $neighbours['next']->id);
1023         // Test 3 pinned posts.
1024         $disc6->pinned = FORUM_DISCUSSION_PINNED;
1025         $DB->update_record('forum_discussions', (object) array('id' => $disc6->id, 'pinned' => $disc6->pinned));
1026         $disc4->pinned = FORUM_DISCUSSION_PINNED;
1027         $DB->update_record('forum_discussions', (object) array('id' => $disc4->id, 'pinned' => $disc4->pinned));
1029         $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
1030         $this->assertEquals($disc4->id, $neighbours['prev']->id);
1031         $this->assertEquals($disc8->id, $neighbours['next']->id);
1033         $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
1034         $this->assertEquals($disc3->id, $neighbours['prev']->id);
1035         $this->assertEquals($disc6->id, $neighbours['next']->id);
1037         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1038         $this->assertEquals($disc6->id, $neighbours['prev']->id);
1039         $this->assertEmpty($neighbours['next']);
1040     }
1042     /**
1043      * Test getting the neighbour threads of a blog-like forum.
1044      */
1045     public function test_forum_get_neighbours_blog() {
1046         global $CFG, $DB;
1047         $this->resetAfterTest();
1049         $timenow = time();
1050         $timenext = $timenow;
1052         // Setup test data.
1053         $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1054         $course = $this->getDataGenerator()->create_course();
1055         $user = $this->getDataGenerator()->create_user();
1056         $user2 = $this->getDataGenerator()->create_user();
1058         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog'));
1059         $cm = get_coursemodule_from_instance('forum', $forum->id);
1060         $context = context_module::instance($cm->id);
1062         $record = new stdClass();
1063         $record->course = $course->id;
1064         $record->userid = $user->id;
1065         $record->forum = $forum->id;
1066         $record->timemodified = time();
1067         $disc1 = $forumgen->create_discussion($record);
1068         $record->timemodified++;
1069         $disc2 = $forumgen->create_discussion($record);
1070         $record->timemodified++;
1071         $disc3 = $forumgen->create_discussion($record);
1072         $record->timemodified++;
1073         $disc4 = $forumgen->create_discussion($record);
1074         $record->timemodified++;
1075         $disc5 = $forumgen->create_discussion($record);
1077         // Getting the neighbours.
1078         $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1079         $this->assertEmpty($neighbours['prev']);
1080         $this->assertEquals($disc2->id, $neighbours['next']->id);
1082         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1083         $this->assertEquals($disc1->id, $neighbours['prev']->id);
1084         $this->assertEquals($disc3->id, $neighbours['next']->id);
1086         $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1087         $this->assertEquals($disc2->id, $neighbours['prev']->id);
1088         $this->assertEquals($disc4->id, $neighbours['next']->id);
1090         $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum);
1091         $this->assertEquals($disc3->id, $neighbours['prev']->id);
1092         $this->assertEquals($disc5->id, $neighbours['next']->id);
1094         $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum);
1095         $this->assertEquals($disc4->id, $neighbours['prev']->id);
1096         $this->assertEmpty($neighbours['next']);
1098         // Make sure that the thread's timemodified does not affect the order.
1099         $record->timemodified++;
1100         $disc1->timemodified = $record->timemodified;
1101         $DB->update_record('forum_discussions', $disc1);
1103         $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum);
1104         $this->assertEmpty($neighbours['prev']);
1105         $this->assertEquals($disc2->id, $neighbours['next']->id);
1107         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1108         $this->assertEquals($disc1->id, $neighbours['prev']->id);
1109         $this->assertEquals($disc3->id, $neighbours['next']->id);
1111         // Add another blog post.
1112         $record->timemodified++;
1113         $disc6 = $forumgen->create_discussion($record);
1114         $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum);
1115         $this->assertEquals($disc5->id, $neighbours['prev']->id);
1116         $this->assertEmpty($neighbours['next']);
1118         $record->timemodified++;
1119         $disc7 = $forumgen->create_discussion($record);
1120         $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum);
1121         $this->assertEquals($disc6->id, $neighbours['prev']->id);
1122         $this->assertEmpty($neighbours['next']);
1124         // Adding timed discussions.
1125         $CFG->forum_enabletimedposts = true;
1126         $now = $record->timemodified;
1127         $past = $now - 60;
1128         $future = $now + 60;
1130         $record = new stdClass();
1131         $record->course = $course->id;
1132         $record->userid = $user->id;
1133         $record->forum = $forum->id;
1134         $record->timestart = $past;
1135         $record->timeend = $future;
1136         $record->timemodified = $now;
1137         $record->timemodified++;
1138         $disc8 = $forumgen->create_discussion($record);
1139         $record->timemodified++;
1140         $record->timestart = $future;
1141         $record->timeend = 0;
1142         $disc9 = $forumgen->create_discussion($record);
1143         $record->timemodified++;
1144         $record->timestart = 0;
1145         $record->timeend = 0;
1146         $disc10 = $forumgen->create_discussion($record);
1147         $record->timemodified++;
1148         $record->timestart = 0;
1149         $record->timeend = $past;
1150         $disc11 = $forumgen->create_discussion($record);
1151         $record->timemodified++;
1152         $record->timestart = $past;
1153         $record->timeend = $future;
1154         $disc12 = $forumgen->create_discussion($record);
1156         // Admin user ignores the timed settings of discussions.
1157         $this->setAdminUser();
1158         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1159         $this->assertEquals($disc7->id, $neighbours['prev']->id);
1160         $this->assertEquals($disc9->id, $neighbours['next']->id);
1162         $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1163         $this->assertEquals($disc8->id, $neighbours['prev']->id);
1164         $this->assertEquals($disc10->id, $neighbours['next']->id);
1166         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1167         $this->assertEquals($disc9->id, $neighbours['prev']->id);
1168         $this->assertEquals($disc11->id, $neighbours['next']->id);
1170         $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1171         $this->assertEquals($disc10->id, $neighbours['prev']->id);
1172         $this->assertEquals($disc12->id, $neighbours['next']->id);
1174         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1175         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1176         $this->assertEmpty($neighbours['next']);
1178         // Normal user can see their own timed discussions.
1179         $this->setUser($user);
1180         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1181         $this->assertEquals($disc7->id, $neighbours['prev']->id);
1182         $this->assertEquals($disc9->id, $neighbours['next']->id);
1184         $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum);
1185         $this->assertEquals($disc8->id, $neighbours['prev']->id);
1186         $this->assertEquals($disc10->id, $neighbours['next']->id);
1188         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1189         $this->assertEquals($disc9->id, $neighbours['prev']->id);
1190         $this->assertEquals($disc11->id, $neighbours['next']->id);
1192         $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum);
1193         $this->assertEquals($disc10->id, $neighbours['prev']->id);
1194         $this->assertEquals($disc12->id, $neighbours['next']->id);
1196         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1197         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1198         $this->assertEmpty($neighbours['next']);
1200         // Normal user does not ignore timed settings.
1201         $this->setUser($user2);
1202         $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum);
1203         $this->assertEquals($disc7->id, $neighbours['prev']->id);
1204         $this->assertEquals($disc10->id, $neighbours['next']->id);
1206         $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum);
1207         $this->assertEquals($disc8->id, $neighbours['prev']->id);
1208         $this->assertEquals($disc12->id, $neighbours['next']->id);
1210         $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum);
1211         $this->assertEquals($disc10->id, $neighbours['prev']->id);
1212         $this->assertEmpty($neighbours['next']);
1214         // Reset to normal mode.
1215         $CFG->forum_enabletimedposts = false;
1216         $this->setAdminUser();
1218         $record->timemodified++;
1219         // Two blog posts with identical creation time will sort by id.
1220         $DB->update_record('forum_posts', (object) array('id' => $disc2->firstpost, 'created' => $record->timemodified));
1221         $DB->update_record('forum_posts', (object) array('id' => $disc3->firstpost, 'created' => $record->timemodified));
1223         $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum);
1224         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1225         $this->assertEquals($disc3->id, $neighbours['next']->id);
1227         $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum);
1228         $this->assertEquals($disc2->id, $neighbours['prev']->id);
1229         $this->assertEmpty($neighbours['next']);
1230     }
1232     /**
1233      * Test getting the neighbour threads of a discussion.
1234      */
1235     public function test_forum_get_neighbours_with_groups() {
1236         $this->resetAfterTest();
1238         $timenow = time();
1239         $timenext = $timenow;
1241         // Setup test data.
1242         $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1243         $course = $this->getDataGenerator()->create_course();
1244         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1245         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1246         $user1 = $this->getDataGenerator()->create_user();
1247         $user2 = $this->getDataGenerator()->create_user();
1248         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1249         $this->getDataGenerator()->enrol_user($user2->id, $course->id);
1250         $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id));
1252         $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => VISIBLEGROUPS));
1253         $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => SEPARATEGROUPS));
1254         $cm1 = get_coursemodule_from_instance('forum', $forum1->id);
1255         $cm2 = get_coursemodule_from_instance('forum', $forum2->id);
1256         $context1 = context_module::instance($cm1->id);
1257         $context2 = context_module::instance($cm2->id);
1259         // Creating discussions in both forums.
1260         $record = new stdClass();
1261         $record->course = $course->id;
1262         $record->userid = $user1->id;
1263         $record->forum = $forum1->id;
1264         $record->groupid = $group1->id;
1265         $record->timemodified = time();
1266         $disc11 = $forumgen->create_discussion($record);
1267         $record->forum = $forum2->id;
1268         $record->timemodified++;
1269         $disc21 = $forumgen->create_discussion($record);
1271         $record->timemodified++;
1272         $record->userid = $user2->id;
1273         $record->forum = $forum1->id;
1274         $record->groupid = $group2->id;
1275         $disc12 = $forumgen->create_discussion($record);
1276         $record->forum = $forum2->id;
1277         $disc22 = $forumgen->create_discussion($record);
1279         $record->timemodified++;
1280         $record->userid = $user1->id;
1281         $record->forum = $forum1->id;
1282         $record->groupid = null;
1283         $disc13 = $forumgen->create_discussion($record);
1284         $record->forum = $forum2->id;
1285         $disc23 = $forumgen->create_discussion($record);
1287         $record->timemodified++;
1288         $record->userid = $user2->id;
1289         $record->forum = $forum1->id;
1290         $record->groupid = $group2->id;
1291         $disc14 = $forumgen->create_discussion($record);
1292         $record->forum = $forum2->id;
1293         $disc24 = $forumgen->create_discussion($record);
1295         $record->timemodified++;
1296         $record->userid = $user1->id;
1297         $record->forum = $forum1->id;
1298         $record->groupid = $group1->id;
1299         $disc15 = $forumgen->create_discussion($record);
1300         $record->forum = $forum2->id;
1301         $disc25 = $forumgen->create_discussion($record);
1303         // Admin user can see all groups.
1304         $this->setAdminUser();
1305         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1306         $this->assertEmpty($neighbours['prev']);
1307         $this->assertEquals($disc12->id, $neighbours['next']->id);
1308         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1309         $this->assertEmpty($neighbours['prev']);
1310         $this->assertEquals($disc22->id, $neighbours['next']->id);
1312         $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1313         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1314         $this->assertEquals($disc13->id, $neighbours['next']->id);
1315         $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1316         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1317         $this->assertEquals($disc23->id, $neighbours['next']->id);
1319         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1320         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1321         $this->assertEquals($disc14->id, $neighbours['next']->id);
1322         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1323         $this->assertEquals($disc22->id, $neighbours['prev']->id);
1324         $this->assertEquals($disc24->id, $neighbours['next']->id);
1326         $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1);
1327         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1328         $this->assertEquals($disc15->id, $neighbours['next']->id);
1329         $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1330         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1331         $this->assertEquals($disc25->id, $neighbours['next']->id);
1333         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1334         $this->assertEquals($disc14->id, $neighbours['prev']->id);
1335         $this->assertEmpty($neighbours['next']);
1336         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1337         $this->assertEquals($disc24->id, $neighbours['prev']->id);
1338         $this->assertEmpty($neighbours['next']);
1340         // Admin user is only viewing group 1.
1341         $_POST['group'] = $group1->id;
1342         $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1343         $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1345         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1346         $this->assertEmpty($neighbours['prev']);
1347         $this->assertEquals($disc13->id, $neighbours['next']->id);
1348         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1349         $this->assertEmpty($neighbours['prev']);
1350         $this->assertEquals($disc23->id, $neighbours['next']->id);
1352         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1353         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1354         $this->assertEquals($disc15->id, $neighbours['next']->id);
1355         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1356         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1357         $this->assertEquals($disc25->id, $neighbours['next']->id);
1359         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1360         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1361         $this->assertEmpty($neighbours['next']);
1362         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1363         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1364         $this->assertEmpty($neighbours['next']);
1366         // Normal user viewing non-grouped posts (this is only possible in visible groups).
1367         $this->setUser($user1);
1368         $_POST['group'] = 0;
1369         $this->assertEquals(0, groups_get_activity_group($cm1, true));
1371         // They can see anything in visible groups.
1372         $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1373         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1374         $this->assertEquals($disc13->id, $neighbours['next']->id);
1375         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1376         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1377         $this->assertEquals($disc14->id, $neighbours['next']->id);
1379         // Normal user, orphan of groups, can only see non-grouped posts in separate groups.
1380         $this->setUser($user2);
1381         $_POST['group'] = 0;
1382         $this->assertEquals(0, groups_get_activity_group($cm2, true));
1384         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1385         $this->assertEmpty($neighbours['prev']);
1386         $this->assertEmpty($neighbours['next']);
1388         $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1389         $this->assertEmpty($neighbours['prev']);
1390         $this->assertEquals($disc23->id, $neighbours['next']->id);
1392         $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1393         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1394         $this->assertEmpty($neighbours['next']);
1396         // Switching to viewing group 1.
1397         $this->setUser($user1);
1398         $_POST['group'] = $group1->id;
1399         $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1400         $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1402         // They can see non-grouped or same group.
1403         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1404         $this->assertEmpty($neighbours['prev']);
1405         $this->assertEquals($disc13->id, $neighbours['next']->id);
1406         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1407         $this->assertEmpty($neighbours['prev']);
1408         $this->assertEquals($disc23->id, $neighbours['next']->id);
1410         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1411         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1412         $this->assertEquals($disc15->id, $neighbours['next']->id);
1413         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1414         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1415         $this->assertEquals($disc25->id, $neighbours['next']->id);
1417         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1418         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1419         $this->assertEmpty($neighbours['next']);
1420         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1421         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1422         $this->assertEmpty($neighbours['next']);
1424         // Querying the neighbours of a discussion passing the wrong CM.
1425         $this->expectException('coding_exception');
1426         forum_get_discussion_neighbours($cm2, $disc11, $forum2);
1427     }
1429     /**
1430      * Test getting the neighbour threads of a blog-like forum with groups involved.
1431      */
1432     public function test_forum_get_neighbours_with_groups_blog() {
1433         $this->resetAfterTest();
1435         $timenow = time();
1436         $timenext = $timenow;
1438         // Setup test data.
1439         $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
1440         $course = $this->getDataGenerator()->create_course();
1441         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1442         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1443         $user1 = $this->getDataGenerator()->create_user();
1444         $user2 = $this->getDataGenerator()->create_user();
1445         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1446         $this->getDataGenerator()->enrol_user($user2->id, $course->id);
1447         $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id));
1449         $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog',
1450                 'groupmode' => VISIBLEGROUPS));
1451         $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog',
1452                 'groupmode' => SEPARATEGROUPS));
1453         $cm1 = get_coursemodule_from_instance('forum', $forum1->id);
1454         $cm2 = get_coursemodule_from_instance('forum', $forum2->id);
1455         $context1 = context_module::instance($cm1->id);
1456         $context2 = context_module::instance($cm2->id);
1458         // Creating blog posts in both forums.
1459         $record = new stdClass();
1460         $record->course = $course->id;
1461         $record->userid = $user1->id;
1462         $record->forum = $forum1->id;
1463         $record->groupid = $group1->id;
1464         $record->timemodified = time();
1465         $disc11 = $forumgen->create_discussion($record);
1466         $record->timenow = $timenext++;
1467         $record->forum = $forum2->id;
1468         $record->timemodified++;
1469         $disc21 = $forumgen->create_discussion($record);
1471         $record->timemodified++;
1472         $record->userid = $user2->id;
1473         $record->forum = $forum1->id;
1474         $record->groupid = $group2->id;
1475         $disc12 = $forumgen->create_discussion($record);
1476         $record->forum = $forum2->id;
1477         $disc22 = $forumgen->create_discussion($record);
1479         $record->timemodified++;
1480         $record->userid = $user1->id;
1481         $record->forum = $forum1->id;
1482         $record->groupid = null;
1483         $disc13 = $forumgen->create_discussion($record);
1484         $record->forum = $forum2->id;
1485         $disc23 = $forumgen->create_discussion($record);
1487         $record->timemodified++;
1488         $record->userid = $user2->id;
1489         $record->forum = $forum1->id;
1490         $record->groupid = $group2->id;
1491         $disc14 = $forumgen->create_discussion($record);
1492         $record->forum = $forum2->id;
1493         $disc24 = $forumgen->create_discussion($record);
1495         $record->timemodified++;
1496         $record->userid = $user1->id;
1497         $record->forum = $forum1->id;
1498         $record->groupid = $group1->id;
1499         $disc15 = $forumgen->create_discussion($record);
1500         $record->forum = $forum2->id;
1501         $disc25 = $forumgen->create_discussion($record);
1503         // Admin user can see all groups.
1504         $this->setAdminUser();
1505         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1506         $this->assertEmpty($neighbours['prev']);
1507         $this->assertEquals($disc12->id, $neighbours['next']->id);
1508         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1509         $this->assertEmpty($neighbours['prev']);
1510         $this->assertEquals($disc22->id, $neighbours['next']->id);
1512         $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1513         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1514         $this->assertEquals($disc13->id, $neighbours['next']->id);
1515         $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1516         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1517         $this->assertEquals($disc23->id, $neighbours['next']->id);
1519         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1520         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1521         $this->assertEquals($disc14->id, $neighbours['next']->id);
1522         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1523         $this->assertEquals($disc22->id, $neighbours['prev']->id);
1524         $this->assertEquals($disc24->id, $neighbours['next']->id);
1526         $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1);
1527         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1528         $this->assertEquals($disc15->id, $neighbours['next']->id);
1529         $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1530         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1531         $this->assertEquals($disc25->id, $neighbours['next']->id);
1533         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1534         $this->assertEquals($disc14->id, $neighbours['prev']->id);
1535         $this->assertEmpty($neighbours['next']);
1536         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1537         $this->assertEquals($disc24->id, $neighbours['prev']->id);
1538         $this->assertEmpty($neighbours['next']);
1540         // Admin user is only viewing group 1.
1541         $_POST['group'] = $group1->id;
1542         $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1543         $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1545         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1546         $this->assertEmpty($neighbours['prev']);
1547         $this->assertEquals($disc13->id, $neighbours['next']->id);
1548         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1549         $this->assertEmpty($neighbours['prev']);
1550         $this->assertEquals($disc23->id, $neighbours['next']->id);
1552         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1553         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1554         $this->assertEquals($disc15->id, $neighbours['next']->id);
1555         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1556         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1557         $this->assertEquals($disc25->id, $neighbours['next']->id);
1559         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1560         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1561         $this->assertEmpty($neighbours['next']);
1562         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1563         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1564         $this->assertEmpty($neighbours['next']);
1566         // Normal user viewing non-grouped posts (this is only possible in visible groups).
1567         $this->setUser($user1);
1568         $_POST['group'] = 0;
1569         $this->assertEquals(0, groups_get_activity_group($cm1, true));
1571         // They can see anything in visible groups.
1572         $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1);
1573         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1574         $this->assertEquals($disc13->id, $neighbours['next']->id);
1575         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1576         $this->assertEquals($disc12->id, $neighbours['prev']->id);
1577         $this->assertEquals($disc14->id, $neighbours['next']->id);
1579         // Normal user, orphan of groups, can only see non-grouped posts in separate groups.
1580         $this->setUser($user2);
1581         $_POST['group'] = 0;
1582         $this->assertEquals(0, groups_get_activity_group($cm2, true));
1584         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1585         $this->assertEmpty($neighbours['prev']);
1586         $this->assertEmpty($neighbours['next']);
1588         $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2);
1589         $this->assertEmpty($neighbours['prev']);
1590         $this->assertEquals($disc23->id, $neighbours['next']->id);
1592         $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2);
1593         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1594         $this->assertEmpty($neighbours['next']);
1596         // Switching to viewing group 1.
1597         $this->setUser($user1);
1598         $_POST['group'] = $group1->id;
1599         $this->assertEquals($group1->id, groups_get_activity_group($cm1, true));
1600         $this->assertEquals($group1->id, groups_get_activity_group($cm2, true));
1602         // They can see non-grouped or same group.
1603         $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1);
1604         $this->assertEmpty($neighbours['prev']);
1605         $this->assertEquals($disc13->id, $neighbours['next']->id);
1606         $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2);
1607         $this->assertEmpty($neighbours['prev']);
1608         $this->assertEquals($disc23->id, $neighbours['next']->id);
1610         $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1);
1611         $this->assertEquals($disc11->id, $neighbours['prev']->id);
1612         $this->assertEquals($disc15->id, $neighbours['next']->id);
1613         $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2);
1614         $this->assertEquals($disc21->id, $neighbours['prev']->id);
1615         $this->assertEquals($disc25->id, $neighbours['next']->id);
1617         $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1);
1618         $this->assertEquals($disc13->id, $neighbours['prev']->id);
1619         $this->assertEmpty($neighbours['next']);
1620         $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2);
1621         $this->assertEquals($disc23->id, $neighbours['prev']->id);
1622         $this->assertEmpty($neighbours['next']);
1624         // Querying the neighbours of a discussion passing the wrong CM.
1625         $this->expectException('coding_exception');
1626         forum_get_discussion_neighbours($cm2, $disc11, $forum2);
1627     }
1629     public function test_count_discussion_replies_basic() {
1630         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1632         // Count the discussion replies in the forum.
1633         $result = forum_count_discussion_replies($forum->id);
1634         $this->assertCount(10, $result);
1635     }
1637     public function test_count_discussion_replies_limited() {
1638         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1639         // Adding limits shouldn't make a difference.
1640         $result = forum_count_discussion_replies($forum->id, "", 20);
1641         $this->assertCount(10, $result);
1642     }
1644     public function test_count_discussion_replies_paginated() {
1645         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1646         // Adding paging shouldn't make any difference.
1647         $result = forum_count_discussion_replies($forum->id, "", -1, 0, 100);
1648         $this->assertCount(10, $result);
1649     }
1651     public function test_count_discussion_replies_paginated_sorted() {
1652         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1653         // Specifying the forumsort should also give a good result. This follows a different path.
1654         $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 100);
1655         $this->assertCount(10, $result);
1656         foreach ($result as $row) {
1657             // Grab the first discussionid.
1658             $discussionid = array_shift($discussionids);
1659             $this->assertEquals($discussionid, $row->discussion);
1660         }
1661     }
1663     public function test_count_discussion_replies_limited_sorted() {
1664         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1665         // Adding limits, and a forumsort shouldn't make a difference.
1666         $result = forum_count_discussion_replies($forum->id, "d.id asc", 20);
1667         $this->assertCount(10, $result);
1668         foreach ($result as $row) {
1669             // Grab the first discussionid.
1670             $discussionid = array_shift($discussionids);
1671             $this->assertEquals($discussionid, $row->discussion);
1672         }
1673     }
1675     public function test_count_discussion_replies_paginated_sorted_small() {
1676         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1677         // Grabbing a smaller subset and they should be ordered as expected.
1678         $result = forum_count_discussion_replies($forum->id, "d.id asc", -1, 0, 5);
1679         $this->assertCount(5, $result);
1680         foreach ($result as $row) {
1681             // Grab the first discussionid.
1682             $discussionid = array_shift($discussionids);
1683             $this->assertEquals($discussionid, $row->discussion);
1684         }
1685     }
1687     public function test_count_discussion_replies_paginated_sorted_small_reverse() {
1688         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1689         // Grabbing a smaller subset and they should be ordered as expected.
1690         $result = forum_count_discussion_replies($forum->id, "d.id desc", -1, 0, 5);
1691         $this->assertCount(5, $result);
1692         foreach ($result as $row) {
1693             // Grab the last discussionid.
1694             $discussionid = array_pop($discussionids);
1695             $this->assertEquals($discussionid, $row->discussion);
1696         }
1697     }
1699     public function test_count_discussion_replies_limited_sorted_small_reverse() {
1700         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1701         // Adding limits, and a forumsort shouldn't make a difference.
1702         $result = forum_count_discussion_replies($forum->id, "d.id desc", 5);
1703         $this->assertCount(5, $result);
1704         foreach ($result as $row) {
1705             // Grab the last discussionid.
1706             $discussionid = array_pop($discussionids);
1707             $this->assertEquals($discussionid, $row->discussion);
1708         }
1709     }
1710     public function test_discussion_pinned_sort() {
1711         list($forum, $discussionids) = $this->create_multiple_discussions_with_replies(10, 5);
1712         $cm = get_coursemodule_from_instance('forum', $forum->id);
1713         $discussions = forum_get_discussions($cm);
1714         // First discussion should be pinned.
1715         $first = reset($discussions);
1716         $this->assertEquals(1, $first->pinned, "First discussion should be pinned discussion");
1717     }
1718     public function test_forum_view() {
1719         global $CFG;
1721         $CFG->enablecompletion = 1;
1722         $this->resetAfterTest();
1724         // Setup test data.
1725         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
1726         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
1727                                                             array('completion' => 2, 'completionview' => 1));
1728         $context = context_module::instance($forum->cmid);
1729         $cm = get_coursemodule_from_instance('forum', $forum->id);
1731         // Trigger and capture the event.
1732         $sink = $this->redirectEvents();
1734         $this->setAdminUser();
1735         forum_view($forum, $course, $cm, $context);
1737         $events = $sink->get_events();
1738         // 2 additional events thanks to completion.
1739         $this->assertCount(3, $events);
1740         $event = array_pop($events);
1742         // Checking that the event contains the expected values.
1743         $this->assertInstanceOf('\mod_forum\event\course_module_viewed', $event);
1744         $this->assertEquals($context, $event->get_context());
1745         $url = new \moodle_url('/mod/forum/view.php', array('f' => $forum->id));
1746         $this->assertEquals($url, $event->get_url());
1747         $this->assertEventContextNotUsed($event);
1748         $this->assertNotEmpty($event->get_name());
1750         // Check completion status.
1751         $completion = new completion_info($course);
1752         $completiondata = $completion->get_data($cm);
1753         $this->assertEquals(1, $completiondata->completionstate);
1755     }
1757     /**
1758      * Test forum_discussion_view.
1759      */
1760     public function test_forum_discussion_view() {
1761         global $CFG, $USER;
1763         $this->resetAfterTest();
1765         // Setup test data.
1766         $course = $this->getDataGenerator()->create_course();
1767         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1768         $discussion = $this->create_single_discussion_with_replies($forum, $USER, 2);
1770         $context = context_module::instance($forum->cmid);
1771         $cm = get_coursemodule_from_instance('forum', $forum->id);
1773         // Trigger and capture the event.
1774         $sink = $this->redirectEvents();
1776         $this->setAdminUser();
1777         forum_discussion_view($context, $forum, $discussion);
1779         $events = $sink->get_events();
1780         $this->assertCount(1, $events);
1781         $event = array_pop($events);
1783         // Checking that the event contains the expected values.
1784         $this->assertInstanceOf('\mod_forum\event\discussion_viewed', $event);
1785         $this->assertEquals($context, $event->get_context());
1786         $expected = array($course->id, 'forum', 'view discussion', "discuss.php?d={$discussion->id}",
1787             $discussion->id, $forum->cmid);
1788         $this->assertEventLegacyLogData($expected, $event);
1789         $this->assertEventContextNotUsed($event);
1791         $this->assertNotEmpty($event->get_name());
1793     }
1795     /**
1796      * Create a new course, forum, and user with a number of discussions and replies.
1797      *
1798      * @param int $discussioncount The number of discussions to create
1799      * @param int $replycount The number of replies to create in each discussion
1800      * @return array Containing the created forum object, and the ids of the created discussions.
1801      */
1802     protected function create_multiple_discussions_with_replies($discussioncount, $replycount) {
1803         $this->resetAfterTest();
1805         // Setup the content.
1806         $user = $this->getDataGenerator()->create_user();
1807         $course = $this->getDataGenerator()->create_course();
1808         $record = new stdClass();
1809         $record->course = $course->id;
1810         $forum = $this->getDataGenerator()->create_module('forum', $record);
1812         // Create 10 discussions with replies.
1813         $discussionids = array();
1814         for ($i = 0; $i < $discussioncount; $i++) {
1815             // Pin 3rd discussion.
1816             if ($i == 3) {
1817                 $discussion = $this->create_single_discussion_pinned_with_replies($forum, $user, $replycount);
1818             } else {
1819                 $discussion = $this->create_single_discussion_with_replies($forum, $user, $replycount);
1820             }
1822             $discussionids[] = $discussion->id;
1823         }
1824         return array($forum, $discussionids);
1825     }
1827     /**
1828      * Create a discussion with a number of replies.
1829      *
1830      * @param object $forum The forum which has been created
1831      * @param object $user The user making the discussion and replies
1832      * @param int $replycount The number of replies
1833      * @return object $discussion
1834      */
1835     protected function create_single_discussion_with_replies($forum, $user, $replycount) {
1836         global $DB;
1838         $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
1840         $record = new stdClass();
1841         $record->course = $forum->course;
1842         $record->forum = $forum->id;
1843         $record->userid = $user->id;
1844         $discussion = $generator->create_discussion($record);
1846         // Retrieve the first post.
1847         $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1849         // Create the replies.
1850         $post = new stdClass();
1851         $post->userid = $user->id;
1852         $post->discussion = $discussion->id;
1853         $post->parent = $replyto->id;
1855         for ($i = 0; $i < $replycount; $i++) {
1856             $generator->create_post($post);
1857         }
1859         return $discussion;
1860     }
1861     /**
1862      * Create a discussion with a number of replies.
1863      *
1864      * @param object $forum The forum which has been created
1865      * @param object $user The user making the discussion and replies
1866      * @param int $replycount The number of replies
1867      * @return object $discussion
1868      */
1869     protected function create_single_discussion_pinned_with_replies($forum, $user, $replycount) {
1870         global $DB;
1872         $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
1874         $record = new stdClass();
1875         $record->course = $forum->course;
1876         $record->forum = $forum->id;
1877         $record->userid = $user->id;
1878         $record->pinned = FORUM_DISCUSSION_PINNED;
1879         $discussion = $generator->create_discussion($record);
1881         // Retrieve the first post.
1882         $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1884         // Create the replies.
1885         $post = new stdClass();
1886         $post->userid = $user->id;
1887         $post->discussion = $discussion->id;
1888         $post->parent = $replyto->id;
1890         for ($i = 0; $i < $replycount; $i++) {
1891             $generator->create_post($post);
1892         }
1894         return $discussion;
1895     }
1897     /**
1898      * Tests for mod_forum_rating_can_see_item_ratings().
1899      *
1900      * @throws coding_exception
1901      * @throws rating_exception
1902      */
1903     public function test_mod_forum_rating_can_see_item_ratings() {
1904         global $DB;
1906         $this->resetAfterTest();
1908         // Setup test data.
1909         $course = new stdClass();
1910         $course->groupmode = SEPARATEGROUPS;
1911         $course->groupmodeforce = true;
1912         $course = $this->getDataGenerator()->create_course($course);
1913         $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
1914         $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
1915         $cm = get_coursemodule_from_instance('forum', $forum->id);
1916         $context = context_module::instance($cm->id);
1918         // Create users.
1919         $user1 = $this->getDataGenerator()->create_user();
1920         $user2 = $this->getDataGenerator()->create_user();
1921         $user3 = $this->getDataGenerator()->create_user();
1922         $user4 = $this->getDataGenerator()->create_user();
1924         // Groups and stuff.
1925         $role = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
1926         $this->getDataGenerator()->enrol_user($user1->id, $course->id, $role->id);
1927         $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
1928         $this->getDataGenerator()->enrol_user($user3->id, $course->id, $role->id);
1929         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $role->id);
1931         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1932         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1933         groups_add_member($group1, $user1);
1934         groups_add_member($group1, $user2);
1935         groups_add_member($group2, $user3);
1936         groups_add_member($group2, $user4);
1938         $record = new stdClass();
1939         $record->course = $forum->course;
1940         $record->forum = $forum->id;
1941         $record->userid = $user1->id;
1942         $record->groupid = $group1->id;
1943         $discussion = $generator->create_discussion($record);
1945         // Retrieve the first post.
1946         $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1948         $ratingoptions = new stdClass;
1949         $ratingoptions->context = $context;
1950         $ratingoptions->ratingarea = 'post';
1951         $ratingoptions->component = 'mod_forum';
1952         $ratingoptions->itemid  = $post->id;
1953         $ratingoptions->scaleid = 2;
1954         $ratingoptions->userid  = $user2->id;
1955         $rating = new rating($ratingoptions);
1956         $rating->update_rating(2);
1958         // Now try to access it as various users.
1959         unassign_capability('moodle/site:accessallgroups', $role->id);
1960         $params = array('contextid' => 2,
1961                         'component' => 'mod_forum',
1962                         'ratingarea' => 'post',
1963                         'itemid' => $post->id,
1964                         'scaleid' => 2);
1965         $this->setUser($user1);
1966         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1967         $this->setUser($user2);
1968         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1969         $this->setUser($user3);
1970         $this->assertFalse(mod_forum_rating_can_see_item_ratings($params));
1971         $this->setUser($user4);
1972         $this->assertFalse(mod_forum_rating_can_see_item_ratings($params));
1974         // Now try with accessallgroups cap and make sure everything is visible.
1975         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $role->id, $context->id);
1976         $this->setUser($user1);
1977         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1978         $this->setUser($user2);
1979         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1980         $this->setUser($user3);
1981         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1982         $this->setUser($user4);
1983         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1985         // Change group mode and verify visibility.
1986         $course->groupmode = VISIBLEGROUPS;
1987         $DB->update_record('course', $course);
1988         unassign_capability('moodle/site:accessallgroups', $role->id);
1989         $this->setUser($user1);
1990         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1991         $this->setUser($user2);
1992         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1993         $this->setUser($user3);
1994         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1995         $this->setUser($user4);
1996         $this->assertTrue(mod_forum_rating_can_see_item_ratings($params));
1998     }
2000     /**
2001      * Test forum_get_discussions
2002      */
2003     public function test_forum_get_discussions_with_groups() {
2004         global $DB;
2006         $this->resetAfterTest(true);
2008         // Create course to add the module.
2009         $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
2010         $user1 = self::getDataGenerator()->create_user();
2011         $user2 = self::getDataGenerator()->create_user();
2012         $user3 = self::getDataGenerator()->create_user();
2014         $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2015         self::getDataGenerator()->enrol_user($user1->id, $course->id, $role->id);
2016         self::getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
2017         self::getDataGenerator()->enrol_user($user3->id, $course->id, $role->id);
2019         // Forum forcing separate gropus.
2020         $record = new stdClass();
2021         $record->course = $course->id;
2022         $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
2023         $cm = get_coursemodule_from_instance('forum', $forum->id);
2025         // Create groups.
2026         $group1 = self::getDataGenerator()->create_group(array('courseid' => $course->id));
2027         $group2 = self::getDataGenerator()->create_group(array('courseid' => $course->id));
2028         $group3 = self::getDataGenerator()->create_group(array('courseid' => $course->id));
2030         // Add the user1 to g1 and g2 groups.
2031         groups_add_member($group1->id, $user1->id);
2032         groups_add_member($group2->id, $user1->id);
2034         // Add the user 2 and 3 to only one group.
2035         groups_add_member($group1->id, $user2->id);
2036         groups_add_member($group3->id, $user3->id);
2038         // Add a few discussions.
2039         $record = array();
2040         $record['course'] = $course->id;
2041         $record['forum'] = $forum->id;
2042         $record['userid'] = $user1->id;
2043         $record['groupid'] = $group1->id;
2044         $discussiong1u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2046         $record['groupid'] = $group2->id;
2047         $discussiong2u1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2049         $record['userid'] = $user2->id;
2050         $record['groupid'] = $group1->id;
2051         $discussiong1u2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2053         $record['userid'] = $user3->id;
2054         $record['groupid'] = $group3->id;
2055         $discussiong3u3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2057         self::setUser($user1);
2058         // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions.
2059         $discussions = forum_get_discussions($cm);
2060         self::assertCount(2, $discussions);
2061         foreach ($discussions as $discussion) {
2062             self::assertEquals($group1->id, $discussion->groupid);
2063         }
2065         // Get all my discussions.
2066         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0);
2067         self::assertCount(3, $discussions);
2069         // Get all my g1 discussions.
2070         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group1->id);
2071         self::assertCount(2, $discussions);
2072         foreach ($discussions as $discussion) {
2073             self::assertEquals($group1->id, $discussion->groupid);
2074         }
2076         // Get all my g2 discussions.
2077         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id);
2078         self::assertCount(1, $discussions);
2079         $discussion = array_shift($discussions);
2080         self::assertEquals($group2->id, $discussion->groupid);
2081         self::assertEquals($user1->id, $discussion->userid);
2082         self::assertEquals($discussiong2u1->id, $discussion->discussion);
2084         // Get all my g3 discussions (I'm not enrolled in that group).
2085         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id);
2086         self::assertCount(0, $discussions);
2088         // This group does not exist.
2089         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id + 1000);
2090         self::assertCount(0, $discussions);
2092         self::setUser($user2);
2094         // Test retrieve discussions not passing the groupid parameter. We will receive only first group discussions.
2095         $discussions = forum_get_discussions($cm);
2096         self::assertCount(2, $discussions);
2097         foreach ($discussions as $discussion) {
2098             self::assertEquals($group1->id, $discussion->groupid);
2099         }
2101         // Get all my viewable discussions.
2102         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, 0);
2103         self::assertCount(2, $discussions);
2104         foreach ($discussions as $discussion) {
2105             self::assertEquals($group1->id, $discussion->groupid);
2106         }
2108         // Get all my g2 discussions (I'm not enrolled in that group).
2109         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group2->id);
2110         self::assertCount(0, $discussions);
2112         // Get all my g3 discussions (I'm not enrolled in that group).
2113         $discussions = forum_get_discussions($cm, '', true, -1, -1, false, -1, 0, $group3->id);
2114         self::assertCount(0, $discussions);
2116     }
2118     /**
2119      * Test forum_user_can_post_discussion
2120      */
2121     public function test_forum_user_can_post_discussion() {
2122         global $CFG, $DB;
2124         $this->resetAfterTest(true);
2126         // Create course to add the module.
2127         $course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
2128         $user = self::getDataGenerator()->create_user();
2129         $this->getDataGenerator()->enrol_user($user->id, $course->id);
2131         // Forum forcing separate gropus.
2132         $record = new stdClass();
2133         $record->course = $course->id;
2134         $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
2135         $cm = get_coursemodule_from_instance('forum', $forum->id);
2136         $context = context_module::instance($cm->id);
2138         self::setUser($user);
2140         // The user is not enroled in any group, try to post in a forum with separate groups.
2141         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2142         $this->assertFalse($can);
2144         // Create a group.
2145         $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2147         // Try to post in a group the user is not enrolled.
2148         $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context);
2149         $this->assertFalse($can);
2151         // Add the user to a group.
2152         groups_add_member($group->id, $user->id);
2154         // Try to post in a group the user is not enrolled.
2155         $can = forum_user_can_post_discussion($forum, $group->id + 1, -1, $cm, $context);
2156         $this->assertFalse($can);
2158         // Now try to post in the user group. (null means it will guess the group).
2159         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2160         $this->assertTrue($can);
2162         $can = forum_user_can_post_discussion($forum, $group->id, -1, $cm, $context);
2163         $this->assertTrue($can);
2165         // Test all groups.
2166         $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context);
2167         $this->assertFalse($can);
2169         $this->setAdminUser();
2170         $can = forum_user_can_post_discussion($forum, -1, -1, $cm, $context);
2171         $this->assertTrue($can);
2173         // Change forum type.
2174         $forum->type = 'news';
2175         $DB->update_record('forum', $forum);
2177         // Admin can post news.
2178         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2179         $this->assertTrue($can);
2181         // Normal users don't.
2182         self::setUser($user);
2183         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2184         $this->assertFalse($can);
2186         // Change forum type.
2187         $forum->type = 'eachuser';
2188         $DB->update_record('forum', $forum);
2190         // I didn't post yet, so I should be able to post.
2191         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2192         $this->assertTrue($can);
2194         // Post now.
2195         $record = new stdClass();
2196         $record->course = $course->id;
2197         $record->userid = $user->id;
2198         $record->forum = $forum->id;
2199         $record->groupid = $group->id;
2200         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2202         // I already posted, I shouldn't be able to post.
2203         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2204         $this->assertFalse($can);
2206         // Last check with no groups, normal forum and course.
2207         $course->groupmode = NOGROUPS;
2208         $course->groupmodeforce = 0;
2209         $DB->update_record('course', $course);
2211         $forum->type = 'general';
2212         $forum->groupmode = NOGROUPS;
2213         $DB->update_record('forum', $forum);
2215         $can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
2216         $this->assertTrue($can);
2217     }
2219     /**
2220      * Test forum_user_has_posted_discussion with no groups.
2221      */
2222     public function test_forum_user_has_posted_discussion_no_groups() {
2223         global $CFG;
2225         $this->resetAfterTest(true);
2227         $course = self::getDataGenerator()->create_course();
2228         $author = self::getDataGenerator()->create_user();
2229         $other = self::getDataGenerator()->create_user();
2230         $this->getDataGenerator()->enrol_user($author->id, $course->id);
2231         $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]);
2233         self::setUser($author);
2235         // Neither user has posted.
2236         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id));
2237         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id));
2239         // Post in the forum.
2240         $record = new stdClass();
2241         $record->course = $course->id;
2242         $record->userid = $author->id;
2243         $record->forum = $forum->id;
2244         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2246         // The author has now posted, but the other user has not.
2247         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id));
2248         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $other->id));
2249     }
2251     /**
2252      * Test forum_user_has_posted_discussion with multiple forums
2253      */
2254     public function test_forum_user_has_posted_discussion_multiple_forums() {
2255         global $CFG;
2257         $this->resetAfterTest(true);
2259         $course = self::getDataGenerator()->create_course();
2260         $author = self::getDataGenerator()->create_user();
2261         $this->getDataGenerator()->enrol_user($author->id, $course->id);
2262         $forum1 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]);
2263         $forum2 = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ]);
2265         self::setUser($author);
2267         // No post in either forum.
2268         $this->assertFalse(forum_user_has_posted_discussion($forum1->id, $author->id));
2269         $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id));
2271         // Post in the forum.
2272         $record = new stdClass();
2273         $record->course = $course->id;
2274         $record->userid = $author->id;
2275         $record->forum = $forum1->id;
2276         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2278         // The author has now posted in forum1, but not forum2.
2279         $this->assertTrue(forum_user_has_posted_discussion($forum1->id, $author->id));
2280         $this->assertFalse(forum_user_has_posted_discussion($forum2->id, $author->id));
2281     }
2283     /**
2284      * Test forum_user_has_posted_discussion with multiple groups.
2285      */
2286     public function test_forum_user_has_posted_discussion_multiple_groups() {
2287         global $CFG;
2289         $this->resetAfterTest(true);
2291         $course = self::getDataGenerator()->create_course();
2292         $author = self::getDataGenerator()->create_user();
2293         $this->getDataGenerator()->enrol_user($author->id, $course->id);
2295         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2296         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2297         groups_add_member($group1->id, $author->id);
2298         groups_add_member($group2->id, $author->id);
2300         $forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id ], [
2301                     'groupmode' => SEPARATEGROUPS,
2302                 ]);
2304         self::setUser($author);
2306         // The user has not posted in either group.
2307         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id));
2308         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id));
2309         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id));
2311         // Post in one group.
2312         $record = new stdClass();
2313         $record->course = $course->id;
2314         $record->userid = $author->id;
2315         $record->forum = $forum->id;
2316         $record->groupid = $group1->id;
2317         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2319         // The author has now posted in one group, but the other user has not.
2320         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id));
2321         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id));
2322         $this->assertFalse(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id));
2324         // Post in the other group.
2325         $record = new stdClass();
2326         $record->course = $course->id;
2327         $record->userid = $author->id;
2328         $record->forum = $forum->id;
2329         $record->groupid = $group2->id;
2330         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2332         // The author has now posted in one group, but the other user has not.
2333         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id));
2334         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group1->id));
2335         $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id));
2336     }
2338     /**
2339      * Tests the mod_forum_myprofile_navigation() function.
2340      */
2341     public function test_mod_forum_myprofile_navigation() {
2342         $this->resetAfterTest(true);
2344         // Set up the test.
2345         $tree = new \core_user\output\myprofile\tree();
2346         $user = $this->getDataGenerator()->create_user();
2347         $course = $this->getDataGenerator()->create_course();
2348         $iscurrentuser = true;
2350         // Set as the current user.
2351         $this->setUser($user);
2353         // Check the node tree is correct.
2354         mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course);
2355         $reflector = new ReflectionObject($tree);
2356         $nodes = $reflector->getProperty('nodes');
2357         $nodes->setAccessible(true);
2358         $this->assertArrayHasKey('forumposts', $nodes->getValue($tree));
2359         $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree));
2360     }
2362     /**
2363      * Tests the mod_forum_myprofile_navigation() function as a guest.
2364      */
2365     public function test_mod_forum_myprofile_navigation_as_guest() {
2366         global $USER;
2368         $this->resetAfterTest(true);
2370         // Set up the test.
2371         $tree = new \core_user\output\myprofile\tree();
2372         $course = $this->getDataGenerator()->create_course();
2373         $iscurrentuser = true;
2375         // Set user as guest.
2376         $this->setGuestUser();
2378         // Check the node tree is correct.
2379         mod_forum_myprofile_navigation($tree, $USER, $iscurrentuser, $course);
2380         $reflector = new ReflectionObject($tree);
2381         $nodes = $reflector->getProperty('nodes');
2382         $nodes->setAccessible(true);
2383         $this->assertArrayNotHasKey('forumposts', $nodes->getValue($tree));
2384         $this->assertArrayNotHasKey('forumdiscussions', $nodes->getValue($tree));
2385     }
2387     /**
2388      * Tests the mod_forum_myprofile_navigation() function as a user viewing another user's profile.
2389      */
2390     public function test_mod_forum_myprofile_navigation_different_user() {
2391         $this->resetAfterTest(true);
2393         // Set up the test.
2394         $tree = new \core_user\output\myprofile\tree();
2395         $user = $this->getDataGenerator()->create_user();
2396         $user2 = $this->getDataGenerator()->create_user();
2397         $course = $this->getDataGenerator()->create_course();
2398         $iscurrentuser = true;
2400         // Set to different user's profile.
2401         $this->setUser($user2);
2403         // Check the node tree is correct.
2404         mod_forum_myprofile_navigation($tree, $user, $iscurrentuser, $course);
2405         $reflector = new ReflectionObject($tree);
2406         $nodes = $reflector->getProperty('nodes');
2407         $nodes->setAccessible(true);
2408         $this->assertArrayHasKey('forumposts', $nodes->getValue($tree));
2409         $this->assertArrayHasKey('forumdiscussions', $nodes->getValue($tree));
2410     }
2412     public function test_print_overview() {
2413         $this->resetAfterTest();
2414         $course1 = self::getDataGenerator()->create_course();
2415         $course2 = self::getDataGenerator()->create_course();
2417         // Create an author user.
2418         $author = self::getDataGenerator()->create_user();
2419         $this->getDataGenerator()->enrol_user($author->id, $course1->id);
2420         $this->getDataGenerator()->enrol_user($author->id, $course2->id);
2422         // Create a viewer user.
2423         $viewer = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2424         $this->getDataGenerator()->enrol_user($viewer->id, $course1->id);
2425         $this->getDataGenerator()->enrol_user($viewer->id, $course2->id);
2427         // Create two forums - one in each course.
2428         $record = new stdClass();
2429         $record->course = $course1->id;
2430         $forum1 = self::getDataGenerator()->create_module('forum', (object) array('course' => $course1->id));
2431         $forum2 = self::getDataGenerator()->create_module('forum', (object) array('course' => $course2->id));
2433         // A standard post in the forum.
2434         $record = new stdClass();
2435         $record->course = $course1->id;
2436         $record->userid = $author->id;
2437         $record->forum = $forum1->id;
2438         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2440         $this->setUser($viewer->id);
2441         $courses = array(
2442             $course1->id => clone $course1,
2443             $course2->id => clone $course2,
2444         );
2446         foreach ($courses as $courseid => $course) {
2447             $courses[$courseid]->lastaccess = 0;
2448         }
2449         $results = array();
2450         forum_print_overview($courses, $results);
2452         // There should be one entry for course1, and no others.
2453         $this->assertCount(1, $results);
2455         // There should be one entry for a forum in course1.
2456         $this->assertCount(1, $results[$course1->id]);
2457         $this->assertArrayHasKey('forum', $results[$course1->id]);
2458     }
2460     public function test_print_overview_groups() {
2461         $this->resetAfterTest();
2462         $course1 = self::getDataGenerator()->create_course();
2463         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
2464         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
2466         // Create an author user.
2467         $author = self::getDataGenerator()->create_user();
2468         $this->getDataGenerator()->enrol_user($author->id, $course1->id);
2470         // Create two viewer users - one in each group.
2471         $viewer1 = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2472         $this->getDataGenerator()->enrol_user($viewer1->id, $course1->id);
2473         $this->getDataGenerator()->create_group_member(array('userid' => $viewer1->id, 'groupid' => $group1->id));
2475         $viewer2 = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2476         $this->getDataGenerator()->enrol_user($viewer2->id, $course1->id);
2477         $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group2->id));
2479         // Create a forum.
2480         $record = new stdClass();
2481         $record->course = $course1->id;
2482         $forum1 = self::getDataGenerator()->create_module('forum', (object) array(
2483             'course'        => $course1->id,
2484             'groupmode'     => SEPARATEGROUPS,
2485         ));
2487         // A post in the forum for group1.
2488         $record = new stdClass();
2489         $record->course     = $course1->id;
2490         $record->userid     = $author->id;
2491         $record->forum      = $forum1->id;
2492         $record->groupid    = $group1->id;
2493         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2495         $course1->lastaccess = 0;
2496         $courses = array($course1->id => $course1);
2498         // As viewer1 (same group as post).
2499         $this->setUser($viewer1->id);
2500         $results = array();
2501         forum_print_overview($courses, $results);
2503         // There should be one entry for course1.
2504         $this->assertCount(1, $results);
2506         // There should be one entry for a forum in course1.
2507         $this->assertCount(1, $results[$course1->id]);
2508         $this->assertArrayHasKey('forum', $results[$course1->id]);
2510         $this->setUser($viewer2->id);
2511         $results = array();
2512         forum_print_overview($courses, $results);
2514         // There should be one entry for course1.
2515         $this->assertCount(0, $results);
2516     }
2518     /**
2519      * @dataProvider print_overview_timed_provider
2520      */
2521     public function test_print_overview_timed($config, $hasresult) {
2522         $this->resetAfterTest();
2523         $course1 = self::getDataGenerator()->create_course();
2525         // Create an author user.
2526         $author = self::getDataGenerator()->create_user();
2527         $this->getDataGenerator()->enrol_user($author->id, $course1->id);
2529         // Create a viewer user.
2530         $viewer = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2531         $this->getDataGenerator()->enrol_user($viewer->id, $course1->id);
2533         // Create a forum.
2534         $record = new stdClass();
2535         $record->course = $course1->id;
2536         $forum1 = self::getDataGenerator()->create_module('forum', (object) array('course' => $course1->id));
2538         // A timed post with a timestart in the past (24 hours ago).
2539         $record = new stdClass();
2540         $record->course = $course1->id;
2541         $record->userid = $author->id;
2542         $record->forum = $forum1->id;
2543         if (isset($config['timestartmodifier'])) {
2544             $record->timestart = time() + $config['timestartmodifier'];
2545         }
2546         if (isset($config['timeendmodifier'])) {
2547             $record->timeend = time() + $config['timeendmodifier'];
2548         }
2549         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2551         $course1->lastaccess = 0;
2552         $courses = array($course1->id => $course1);
2554         // As viewer, check the forum_print_overview result.
2555         $this->setUser($viewer->id);
2556         $results = array();
2557         forum_print_overview($courses, $results);
2559         if ($hasresult) {
2560             // There should be one entry for course1.
2561             $this->assertCount(1, $results);
2563             // There should be one entry for a forum in course1.
2564             $this->assertCount(1, $results[$course1->id]);
2565             $this->assertArrayHasKey('forum', $results[$course1->id]);
2566         } else {
2567             // There should be no entries for any course.
2568             $this->assertCount(0, $results);
2569         }
2570     }
2572     /**
2573      * @dataProvider print_overview_timed_provider
2574      */
2575     public function test_print_overview_timed_groups($config, $hasresult) {
2576         $this->resetAfterTest();
2577         $course1 = self::getDataGenerator()->create_course();
2578         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
2579         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
2581         // Create an author user.
2582         $author = self::getDataGenerator()->create_user();
2583         $this->getDataGenerator()->enrol_user($author->id, $course1->id);
2585         // Create two viewer users - one in each group.
2586         $viewer1 = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2587         $this->getDataGenerator()->enrol_user($viewer1->id, $course1->id);
2588         $this->getDataGenerator()->create_group_member(array('userid' => $viewer1->id, 'groupid' => $group1->id));
2590         $viewer2 = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
2591         $this->getDataGenerator()->enrol_user($viewer2->id, $course1->id);
2592         $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group2->id));
2594         // Create a forum.
2595         $record = new stdClass();
2596         $record->course = $course1->id;
2597         $forum1 = self::getDataGenerator()->create_module('forum', (object) array(
2598             'course'        => $course1->id,
2599             'groupmode'     => SEPARATEGROUPS,
2600         ));
2602         // A post in the forum for group1.
2603         $record = new stdClass();
2604         $record->course     = $course1->id;
2605         $record->userid     = $author->id;
2606         $record->forum      = $forum1->id;
2607         $record->groupid    = $group1->id;
2608         if (isset($config['timestartmodifier'])) {
2609             $record->timestart = time() + $config['timestartmodifier'];
2610         }
2611         if (isset($config['timeendmodifier'])) {
2612             $record->timeend = time() + $config['timeendmodifier'];
2613         }
2614         $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2616         $course1->lastaccess = 0;
2617         $courses = array($course1->id => $course1);
2619         // As viewer1 (same group as post).
2620         $this->setUser($viewer1->id);
2621         $results = array();
2622         forum_print_overview($courses, $results);
2624         if ($hasresult) {
2625             // There should be one entry for course1.
2626             $this->assertCount(1, $results);
2628             // There should be one entry for a forum in course1.
2629             $this->assertCount(1, $results[$course1->id]);
2630             $this->assertArrayHasKey('forum', $results[$course1->id]);
2631         } else {
2632             // There should be no entries for any course.
2633             $this->assertCount(0, $results);
2634         }
2636         $this->setUser($viewer2->id);
2637         $results = array();
2638         forum_print_overview($courses, $results);
2640         // There should be one entry for course1.
2641         $this->assertCount(0, $results);
2642     }
2644     public function print_overview_timed_provider() {
2645         return array(
2646             'timestart_past' => array(
2647                 'discussionconfig' => array(
2648                     'timestartmodifier' => -86000,
2649                 ),
2650                 'hasresult'         => true,
2651             ),
2652             'timestart_future' => array(
2653                 'discussionconfig' => array(
2654                     'timestartmodifier' => 86000,
2655                 ),
2656                 'hasresult'         => false,
2657             ),
2658             'timeend_past' => array(
2659                 'discussionconfig' => array(
2660                     'timeendmodifier'   => -86000,
2661                 ),
2662                 'hasresult'         => false,
2663             ),
2664             'timeend_future' => array(
2665                 'discussionconfig' => array(
2666                     'timeendmodifier'   => 86000,
2667                 ),
2668                 'hasresult'         => true,
2669             ),
2670         );
2671     }
2673     /**
2674      * Test test_pinned_discussion_with_group.
2675      */
2676     public function test_pinned_discussion_with_group() {
2677         global $SESSION;
2679         $this->resetAfterTest();
2680         $course1 = $this->getDataGenerator()->create_course();
2681         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
2683         // Create an author user.
2684         $author = $this->getDataGenerator()->create_user();
2685         $this->getDataGenerator()->enrol_user($author->id, $course1->id);
2687         // Create two viewer users - one in a group, one not.
2688         $viewer1 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1));
2689         $this->getDataGenerator()->enrol_user($viewer1->id, $course1->id);
2691         $viewer2 = $this->getDataGenerator()->create_user((object) array('trackforums' => 1));
2692         $this->getDataGenerator()->enrol_user($viewer2->id, $course1->id);
2693         $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group1->id));
2695         $forum1 = $this->getDataGenerator()->create_module('forum', (object) array(
2696             'course' => $course1->id,
2697             'groupmode' => SEPARATEGROUPS,
2698         ));
2700         $coursemodule = get_coursemodule_from_instance('forum', $forum1->id);
2702         $alldiscussions = array();
2703         $group1discussions = array();
2705         // Create 4 discussions in all participants group and group1, where the first
2706         // discussion is pinned in each group.
2707         $allrecord = new stdClass();
2708         $allrecord->course = $course1->id;
2709         $allrecord->userid = $author->id;
2710         $allrecord->forum = $forum1->id;
2711         $allrecord->pinned = FORUM_DISCUSSION_PINNED;
2713         $group1record = new stdClass();
2714         $group1record->course = $course1->id;
2715         $group1record->userid = $author->id;
2716         $group1record->forum = $forum1->id;
2717         $group1record->groupid = $group1->id;
2718         $group1record->pinned = FORUM_DISCUSSION_PINNED;
2720         $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord);
2721         $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record);
2723         // Create unpinned discussions.
2724         $allrecord->pinned = FORUM_DISCUSSION_UNPINNED;
2725         $group1record->pinned = FORUM_DISCUSSION_UNPINNED;
2726         for ($i = 0; $i < 3; $i++) {
2727             $alldiscussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($allrecord);
2728             $group1discussions[] = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($group1record);
2729         }
2731         // As viewer1 (no group). This user shouldn't see any of group1's discussions
2732         // so their expected discussion order is (where rightmost is highest priority):
2733         // Ad1, ad2, ad3, ad0.
2734         $this->setUser($viewer1->id);
2736         // CHECK 1.
2737         // Take the neighbours of ad3, which should be prev: ad2 and next: ad0.
2738         $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1);
2739         // Ad2 check.
2740         $this->assertEquals($alldiscussions[2]->id, $neighbours['prev']->id);
2741         // Ad0 check.
2742         $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id);
2744         // CHECK 2.
2745         // Take the neighbours of ad0, which should be prev: ad3 and next: null.
2746         $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[0], $forum1);
2747         // Ad3 check.
2748         $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id);
2749         // Null check.
2750         $this->assertEmpty($neighbours['next']);
2752         // CHECK 3.
2753         // Take the neighbours of ad1, which should be prev: null and next: ad2.
2754         $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1);
2755         // Null check.
2756         $this->assertEmpty($neighbours['prev']);
2757         // Ad2 check.
2758         $this->assertEquals($alldiscussions[2]->id, $neighbours['next']->id);
2760         // Temporary hack to workaround for MDL-52656.
2761         $SESSION->currentgroup = null;
2763         // As viewer2 (group1). This user should see all of group1's posts and the all participants group.
2764         // The expected discussion order is (rightmost is highest priority):
2765         // Ad1, gd1, ad2, gd2, ad3, gd3, ad0, gd0.
2766         $this->setUser($viewer2->id);
2768         // CHECK 1.
2769         // Take the neighbours of ad1, which should be prev: null and next: gd1.
2770         $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[1], $forum1);
2771         // Null check.
2772         $this->assertEmpty($neighbours['prev']);
2773         // Gd1 check.
2774         $this->assertEquals($group1discussions[1]->id, $neighbours['next']->id);
2776         // CHECK 2.
2777         // Take the neighbours of ad3, which should be prev: gd2 and next: gd3.
2778         $neighbours = forum_get_discussion_neighbours($coursemodule, $alldiscussions[3], $forum1);
2779         // Gd2 check.
2780         $this->assertEquals($group1discussions[2]->id, $neighbours['prev']->id);
2781         // Gd3 check.
2782         $this->assertEquals($group1discussions[3]->id, $neighbours['next']->id);
2784         // CHECK 3.
2785         // Take the neighbours of gd3, which should be prev: ad3 and next: ad0.
2786         $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[3], $forum1);
2787         // Ad3 check.
2788         $this->assertEquals($alldiscussions[3]->id, $neighbours['prev']->id);
2789         // Ad0 check.
2790         $this->assertEquals($alldiscussions[0]->id, $neighbours['next']->id);
2792         // CHECK 4.
2793         // Take the neighbours of gd0, which should be prev: ad0 and next: null.
2794         $neighbours = forum_get_discussion_neighbours($coursemodule, $group1discussions[0], $forum1);
2795         // Ad0 check.
2796         $this->assertEquals($alldiscussions[0]->id, $neighbours['prev']->id);
2797         // Null check.
2798         $this->assertEmpty($neighbours['next']);
2799     }
2801     /**
2802      * Test test_pinned_with_timed_discussions.
2803      */
2804     public function test_pinned_with_timed_discussions() {
2805         global $CFG;
2807         $CFG->forum_enabletimedposts = true;
2809         $this->resetAfterTest();
2810         $course = $this->getDataGenerator()->create_course();
2812         // Create an user.
2813         $user = $this->getDataGenerator()->create_user();
2814         $this->getDataGenerator()->enrol_user($user->id, $course->id);
2816         // Create a forum.
2817         $record = new stdClass();
2818         $record->course = $course->id;
2819         $forum = $this->getDataGenerator()->create_module('forum', (object) array(
2820             'course' => $course->id,
2821             'groupmode' => SEPARATEGROUPS,
2822         ));
2824         $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
2825         $now = time();
2826         $discussions = array();
2827         $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
2829         $record = new stdClass();
2830         $record->course = $course->id;
2831         $record->userid = $user->id;
2832         $record->forum = $forum->id;
2833         $record->pinned = FORUM_DISCUSSION_PINNED;
2834         $record->timemodified = $now;
2836         $discussions[] = $discussiongenerator->create_discussion($record);
2838         $record->pinned = FORUM_DISCUSSION_UNPINNED;
2839         $record->timestart = $now + 10;
2841         $discussions[] = $discussiongenerator->create_discussion($record);
2843         $record->timestart = $now;
2845         $discussions[] = $discussiongenerator->create_discussion($record);
2847         // Expected order of discussions:
2848         // D2, d1, d0.
2849         $this->setUser($user->id);
2851         // CHECK 1.
2852         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum);
2853         // Null check.
2854         $this->assertEmpty($neighbours['prev']);
2855         // D1 check.
2856         $this->assertEquals($discussions[1]->id, $neighbours['next']->id);
2858         // CHECK 2.
2859         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum);
2860         // D2 check.
2861         $this->assertEquals($discussions[2]->id, $neighbours['prev']->id);
2862         // D0 check.
2863         $this->assertEquals($discussions[0]->id, $neighbours['next']->id);
2865         // CHECK 3.
2866         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum);
2867         // D2 check.
2868         $this->assertEquals($discussions[1]->id, $neighbours['prev']->id);
2869         // Null check.
2870         $this->assertEmpty($neighbours['next']);
2871     }
2873     /**
2874      * Test test_pinned_timed_discussions_with_timed_discussions.
2875      */
2876     public function test_pinned_timed_discussions_with_timed_discussions() {
2877         global $CFG;
2879         $CFG->forum_enabletimedposts = true;
2881         $this->resetAfterTest();
2882         $course = $this->getDataGenerator()->create_course();
2884         // Create an user.
2885         $user = $this->getDataGenerator()->create_user();
2886         $this->getDataGenerator()->enrol_user($user->id, $course->id);
2888         // Create a forum.
2889         $record = new stdClass();
2890         $record->course = $course->id;
2891         $forum = $this->getDataGenerator()->create_module('forum', (object) array(
2892             'course' => $course->id,
2893             'groupmode' => SEPARATEGROUPS,
2894         ));
2896         $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
2897         $now = time();
2898         $discussions = array();
2899         $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
2901         $record = new stdClass();
2902         $record->course = $course->id;
2903         $record->userid = $user->id;
2904         $record->forum = $forum->id;
2905         $record->pinned = FORUM_DISCUSSION_PINNED;
2906         $record->timemodified = $now;
2907         $record->timestart = $now + 10;
2909         $discussions[] = $discussiongenerator->create_discussion($record);
2911         $record->pinned = FORUM_DISCUSSION_UNPINNED;
2913         $discussions[] = $discussiongenerator->create_discussion($record);
2915         $record->timestart = $now;
2917         $discussions[] = $discussiongenerator->create_discussion($record);
2919         $record->pinned = FORUM_DISCUSSION_PINNED;
2921         $discussions[] = $discussiongenerator->create_discussion($record);
2923         // Expected order of discussions:
2924         // D2, d1, d3, d0.
2925         $this->setUser($user->id);
2927         // CHECK 1.
2928         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[2], $forum);
2929         // Null check.
2930         $this->assertEmpty($neighbours['prev']);
2931         // D1 check.
2932         $this->assertEquals($discussions[1]->id, $neighbours['next']->id);
2934         // CHECK 2.
2935         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[1], $forum);
2936         // D2 check.
2937         $this->assertEquals($discussions[2]->id, $neighbours['prev']->id);
2938         // D3 check.
2939         $this->assertEquals($discussions[3]->id, $neighbours['next']->id);
2941         // CHECK 3.
2942         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[3], $forum);
2943         // D1 check.
2944         $this->assertEquals($discussions[1]->id, $neighbours['prev']->id);
2945         // D0 check.
2946         $this->assertEquals($discussions[0]->id, $neighbours['next']->id);
2948         // CHECK 4.
2949         $neighbours = forum_get_discussion_neighbours($coursemodule, $discussions[0], $forum);
2950         // D3 check.
2951         $this->assertEquals($discussions[3]->id, $neighbours['prev']->id);
2952         // Null check.
2953         $this->assertEmpty($neighbours['next']);
2954     }
2956     /**
2957      * @dataProvider forum_get_unmailed_posts_provider
2958      */
2959     public function test_forum_get_unmailed_posts($discussiondata, $enabletimedposts, $expectedcount, $expectedreplycount) {
2960         global $CFG, $DB;
2962         $this->resetAfterTest();
2964         // Configure timed posts.
2965         $CFG->forum_enabletimedposts = $enabletimedposts;
2967         $course = $this->getDataGenerator()->create_course();
2968         $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
2969         $user = $this->getDataGenerator()->create_user();
2970         $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum');
2972         // Keep track of the start time of the test. Do not use time() after this point to prevent random failures.
2973         $time = time();
2975         $record = new stdClass();
2976         $record->course = $course->id;
2977         $record->userid = $user->id;
2978         $record->forum = $forum->id;
2979         if (isset($discussiondata['timecreated'])) {
2980             $record->timemodified = $time + $discussiondata['timecreated'];
2981         }
2982         if (isset($discussiondata['timestart'])) {
2983             $record->timestart = $time + $discussiondata['timestart'];
2984         }
2985         if (isset($discussiondata['timeend'])) {
2986             $record->timeend = $time + $discussiondata['timeend'];
2987         }
2988         if (isset($discussiondata['mailed'])) {
2989             $record->mailed = $discussiondata['mailed'];
2990         }
2992         $discussion = $forumgen->create_discussion($record);
2994         // Fetch the unmailed posts.
2995         $timenow   = $time;
2996         $endtime   = $timenow - $CFG->maxeditingtime;
2997         $starttime = $endtime - 2 * DAYSECS;
2999         $unmailed = forum_get_unmailed_posts($starttime, $endtime, $timenow);
3000         $this->assertCount($expectedcount, $unmailed);
3002         // Add a reply just outside the maxeditingtime.
3003         $replyto = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
3004         $reply = new stdClass();
3005         $reply->userid = $user->id;
3006         $reply->discussion = $discussion->id;
3007         $reply->parent = $replyto->id;
3008         $reply->created = max($replyto->created, $endtime - 1);
3009         $forumgen->create_post($reply);
3011         $unmailed = forum_get_unmailed_posts($starttime, $endtime, $timenow);
3012         $this->assertCount($expectedreplycount, $unmailed);
3013     }
3015     /**
3016      * Test for forum_is_author_hidden.
3017      */
3018     public function test_forum_is_author_hidden() {
3019         // First post, different forum type.
3020         $post = (object) ['parent' => 0];
3021         $forum = (object) ['type' => 'standard'];
3022         $this->assertFalse(forum_is_author_hidden($post, $forum));
3024         // Child post, different forum type.
3025         $post->parent = 1;
3026         $this->assertFalse(forum_is_author_hidden($post, $forum));
3028         // First post, single simple discussion forum type.
3029         $post->parent = 0;
3030         $forum->type = 'single';
3031         $this->assertTrue(forum_is_author_hidden($post, $forum));
3033         // Child post, single simple discussion forum type.
3034         $post->parent = 1;
3035         $this->assertFalse(forum_is_author_hidden($post, $forum));
3037         // Incorrect parameters: $post.
3038         $this->expectException('coding_exception');
3039         $this->expectExceptionMessage('$post->parent must be set.');
3040         unset($post->parent);
3041         forum_is_author_hidden($post, $forum);
3043         // Incorrect parameters: $forum.
3044         $this->expectException('coding_exception');
3045         $this->expectExceptionMessage('$forum->type must be set.');
3046         unset($forum->type);
3047         forum_is_author_hidden($post, $forum);
3048     }
3050     public function forum_get_unmailed_posts_provider() {
3051         return [
3052             'Untimed discussion; Single post; maxeditingtime not expired' => [
3053                 'discussion'        => [
3054                 ],
3055                 'timedposts'        => false,
3056                 'postcount'         => 0,
3057                 'replycount'        => 0,
3058             ],
3059             'Untimed discussion; Single post; maxeditingtime expired' => [
3060                 'discussion'        => [
3061                     'timecreated'   => - DAYSECS,
3062                 ],
3063                 'timedposts'        => false,
3064                 'postcount'         => 1,
3065                 'replycount'        => 2,
3066             ],
3067             'Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime not expired' => [
3068                 'discussion'        => [
3069                     'timecreated'   => - WEEKSECS,
3070                     'timestart'     => 0,
3071                 ],
3072                 'timedposts'        => true,
3073                 'postcount'         => 0,
3074                 'replycount'        => 0,
3075             ],
3076             'Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired' => [
3077                 'discussion'        => [
3078                     'timecreated'   => - WEEKSECS,
3079                     'timestart'     => - DAYSECS,
3080                 ],
3081                 'timedposts'        => true,
3082                 'postcount'         => 1,
3083                 'replycount'        => 2,
3084             ],
3085             'Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired; timeend not reached' => [
3086                 'discussion'        => [
3087                     'timecreated'   => - WEEKSECS,
3088                     'timestart'     => - DAYSECS,
3089                     'timeend'       => + DAYSECS
3090                 ],
3091                 'timedposts'        => true,
3092                 'postcount'         => 1,
3093                 'replycount'        => 2,
3094             ],
3095             'Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired; timeend passed' => [
3096                 'discussion'        => [
3097                     'timecreated'   => - WEEKSECS,
3098                     'timestart'     => - DAYSECS,
3099                     'timeend'       => - HOURSECS,
3100                 ],
3101                 'timedposts'        => true,
3102                 'postcount'         => 0,
3103                 'replycount'        => 0,
3104             ],
3105             'Timed discussion; Single post; Posted 1 week ago; timeend not reached' => [
3106                 'discussion'        => [
3107                     'timecreated'   => - WEEKSECS,
3108                     'timeend'       => + DAYSECS
3109                 ],
3110                 'timedposts'        => true,
3111                 'postcount'         => 0,
3112                 'replycount'        => 1,
3113             ],
3114             'Timed discussion; Single post; Posted 1 week ago; timeend passed' => [
3115                 'discussion'        => [
3116                     'timecreated'   => - WEEKSECS,
3117                     'timeend'       => - DAYSECS,
3118                 ],
3119                 'timedposts'        => true,
3120                 'postcount'         => 0,
3121                 'replycount'        => 0,
3122             ],
3124             'Previously mailed; Untimed discussion; Single post; maxeditingtime not expired' => [
3125                 'discussion'        => [
3126                     'mailed'        => 1,
3127                 ],
3128                 'timedposts'        => false,
3129                 'postcount'         => 0,
3130                 'replycount'        => 0,
3131             ],
3133             'Previously mailed; Untimed discussion; Single post; maxeditingtime expired' => [
3134                 'discussion'        => [
3135                     'timecreated'   => - DAYSECS,
3136                     'mailed'        => 1,
3137                 ],
3138                 'timedposts'        => false,
3139                 'postcount'         => 0,
3140                 'replycount'        => 1,
3141             ],
3142             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime not expired' => [
3143                 'discussion'        => [
3144                     'timecreated'   => - WEEKSECS,
3145                     'timestart'     => 0,
3146                     'mailed'        => 1,
3147                 ],
3148                 'timedposts'        => true,
3149                 'postcount'         => 0,
3150                 'replycount'        => 0,
3151             ],
3152             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired' => [
3153                 'discussion'        => [
3154                     'timecreated'   => - WEEKSECS,
3155                     'timestart'     => - DAYSECS,
3156                     'mailed'        => 1,
3157                 ],
3158                 'timedposts'        => true,
3159                 'postcount'         => 0,
3160                 'replycount'        => 1,
3161             ],
3162             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired; timeend not reached' => [
3163                 'discussion'        => [
3164                     'timecreated'   => - WEEKSECS,
3165                     'timestart'     => - DAYSECS,
3166                     'timeend'       => + DAYSECS,
3167                     'mailed'        => 1,
3168                 ],
3169                 'timedposts'        => true,
3170                 'postcount'         => 0,
3171                 'replycount'        => 1,
3172             ],
3173             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timestart maxeditingtime expired; timeend passed' => [
3174                 'discussion'        => [
3175                     'timecreated'   => - WEEKSECS,
3176                     'timestart'     => - DAYSECS,
3177                     'timeend'       => - HOURSECS,
3178                     'mailed'        => 1,
3179                 ],
3180                 'timedposts'        => true,
3181                 'postcount'         => 0,
3182                 'replycount'        => 0,
3183             ],
3184             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timeend not reached' => [
3185                 'discussion'        => [
3186                     'timecreated'   => - WEEKSECS,
3187                     'timeend'       => + DAYSECS,
3188                     'mailed'        => 1,
3189                 ],
3190                 'timedposts'        => true,
3191                 'postcount'         => 0,
3192                 'replycount'        => 1,
3193             ],
3194             'Previously mailed; Timed discussion; Single post; Posted 1 week ago; timeend passed' => [
3195                 'discussion'        => [
3196                     'timecreated'   => - WEEKSECS,
3197                     'timeend'       => - DAYSECS,
3198                     'mailed'        => 1,
3199                 ],
3200                 'timedposts'        => true,
3201                 'postcount'         => 0,
3202                 'replycount'        => 0,
3203             ],
3204         ];
3205     }
3207     /**
3208      * Test the forum_discussion_is_locked function.
3209      *
3210      * @dataProvider forum_discussion_is_locked_provider
3211      * @param   stdClass    $forum
3212      * @param   stdClass    $discussion
3213      * @param   bool        $expect
3214      */
3215     public function test_forum_discussion_is_locked($forum, $discussion, $expect) {
3216         $this->assertEquals($expect, forum_discussion_is_locked($forum, $discussion));
3217     }
3219     /**
3220      * Dataprovider for forum_discussion_is_locked tests.
3221      *
3222      * @return  array
3223      */
3224     public function forum_discussion_is_locked_provider() {
3225         return [
3226             'Unlocked: lockdiscussionafter is unset' => [
3227                 (object) [],
3228                 (object) [],
3229                 false
3230             ],
3231             'Unlocked: lockdiscussionafter is false' => [
3232                 (object) ['lockdiscussionafter' => false],
3233                 (object) [],
3234                 false
3235             ],
3236             'Unlocked: lockdiscussionafter is null' => [
3237                 (object) ['lockdiscussionafter' => null],
3238                 (object) [],
3239                 false
3240             ],
3241             'Unlocked: lockdiscussionafter is set; forum is of type single; post is recent' => [
3242                 (object) ['lockdiscussionafter' => DAYSECS, 'type' => 'single'],
3243                 (object) ['timemodified' => time()],
3244                 false
3245             ],
3246             'Unlocked: lockdiscussionafter is set; forum is of type single; post is old' => [
3247                 (object) ['lockdiscussionafter' => MINSECS, 'type' => 'single'],
3248                 (object) ['timemodified' => time() - DAYSECS],
3249                 false
3250             ],
3251             'Unlocked: lockdiscussionafter is set; forum is of type eachuser; post is recent' => [
3252                 (object) ['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'],
3253                 (object) ['timemodified' => time()],
3254                 false
3255             ],
3256             'Locked: lockdiscussionafter is set; forum is of type eachuser; post is old' => [
3257                 (object) ['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'],
3258                 (object) ['timemodified' => time() - DAYSECS],
3259                 true
3260             ],
3261         ];
3262     }