MDL-57407 mod_forum: Return ratings in external functions
[moodle.git] / mod / forum / tests / externallib_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 external functions unit tests
19  *
20  * @package    mod_forum
21  * @category   external
22  * @copyright  2012 Mark Nelson <markn@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
30 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
31 require_once($CFG->dirroot . '/mod/forum/lib.php');
33 class mod_forum_external_testcase extends externallib_advanced_testcase {
35     /**
36      * Tests set up
37      */
38     protected function setUp() {
39         global $CFG;
41         // We must clear the subscription caches. This has to be done both before each test, and after in case of other
42         // tests using these functions.
43         \mod_forum\subscriptions::reset_forum_cache();
45         require_once($CFG->dirroot . '/mod/forum/externallib.php');
46     }
48     public function tearDown() {
49         // We must clear the subscription caches. This has to be done both before each test, and after in case of other
50         // tests using these functions.
51         \mod_forum\subscriptions::reset_forum_cache();
52     }
54     /**
55      * Test get forums
56      */
57     public function test_mod_forum_get_forums_by_courses() {
58         global $USER, $CFG, $DB;
60         $this->resetAfterTest(true);
62         // Create a user.
63         $user = self::getDataGenerator()->create_user(array('trackforums' => 1));
65         // Set to the user.
66         self::setUser($user);
68         // Create courses to add the modules.
69         $course1 = self::getDataGenerator()->create_course();
70         $course2 = self::getDataGenerator()->create_course();
72         // First forum.
73         $record = new stdClass();
74         $record->introformat = FORMAT_HTML;
75         $record->course = $course1->id;
76         $record->trackingtype = FORUM_TRACKING_FORCED;
77         $forum1 = self::getDataGenerator()->create_module('forum', $record);
79         // Second forum.
80         $record = new stdClass();
81         $record->introformat = FORMAT_HTML;
82         $record->course = $course2->id;
83         $record->trackingtype = FORUM_TRACKING_OFF;
84         $forum2 = self::getDataGenerator()->create_module('forum', $record);
85         $forum2->introfiles = [];
87         // Add discussions to the forums.
88         $record = new stdClass();
89         $record->course = $course1->id;
90         $record->userid = $user->id;
91         $record->forum = $forum1->id;
92         $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
93         // Expect one discussion.
94         $forum1->numdiscussions = 1;
95         $forum1->cancreatediscussions = true;
96         $forum1->istracked = true;
97         $forum1->unreadpostscount = 0;
98         $forum1->introfiles = [];
100         $record = new stdClass();
101         $record->course = $course2->id;
102         $record->userid = $user->id;
103         $record->forum = $forum2->id;
104         $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
105         $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
106         // Expect two discussions.
107         $forum2->numdiscussions = 2;
108         // Default limited role, no create discussion capability enabled.
109         $forum2->cancreatediscussions = false;
110         $forum2->istracked = false;
112         // Check the forum was correctly created.
113         $this->assertEquals(2, $DB->count_records_select('forum', 'id = :forum1 OR id = :forum2',
114                 array('forum1' => $forum1->id, 'forum2' => $forum2->id)));
116         // Enrol the user in two courses.
117         // DataGenerator->enrol_user automatically sets a role for the user with the permission mod/form:viewdiscussion.
118         $this->getDataGenerator()->enrol_user($user->id, $course1->id, null, 'manual');
119         // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
120         $enrol = enrol_get_plugin('manual');
121         $enrolinstances = enrol_get_instances($course2->id, true);
122         foreach ($enrolinstances as $courseenrolinstance) {
123             if ($courseenrolinstance->enrol == "manual") {
124                 $instance2 = $courseenrolinstance;
125                 break;
126             }
127         }
128         $enrol->enrol_user($instance2, $user->id);
130         // Assign capabilities to view forums for forum 2.
131         $cm2 = get_coursemodule_from_id('forum', $forum2->cmid, 0, false, MUST_EXIST);
132         $context2 = context_module::instance($cm2->id);
133         $newrole = create_role('Role 2', 'role2', 'Role 2 description');
134         $roleid2 = $this->assignUserCapability('mod/forum:viewdiscussion', $context2->id, $newrole);
136         // Create what we expect to be returned when querying the two courses.
137         unset($forum1->displaywordcount);
138         unset($forum2->displaywordcount);
140         $expectedforums = array();
141         $expectedforums[$forum1->id] = (array) $forum1;
142         $expectedforums[$forum2->id] = (array) $forum2;
144         // Call the external function passing course ids.
145         $forums = mod_forum_external::get_forums_by_courses(array($course1->id, $course2->id));
146         $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
147         $this->assertCount(2, $forums);
148         foreach ($forums as $forum) {
149             $this->assertEquals($expectedforums[$forum['id']], $forum);
150         }
152         // Call the external function without passing course id.
153         $forums = mod_forum_external::get_forums_by_courses();
154         $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
155         $this->assertCount(2, $forums);
156         foreach ($forums as $forum) {
157             $this->assertEquals($expectedforums[$forum['id']], $forum);
158         }
160         // Unenrol user from second course and alter expected forums.
161         $enrol->unenrol_user($instance2, $user->id);
162         unset($expectedforums[$forum2->id]);
164         // Call the external function without passing course id.
165         $forums = mod_forum_external::get_forums_by_courses();
166         $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
167         $this->assertCount(1, $forums);
168         $this->assertEquals($expectedforums[$forum1->id], $forums[0]);
169         $this->assertTrue($forums[0]['cancreatediscussions']);
171         // Change the type of the forum, the user shouldn't be able to add discussions.
172         $DB->set_field('forum', 'type', 'news', array('id' => $forum1->id));
173         $forums = mod_forum_external::get_forums_by_courses();
174         $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
175         $this->assertFalse($forums[0]['cancreatediscussions']);
177         // Call for the second course we unenrolled the user from.
178         $forums = mod_forum_external::get_forums_by_courses(array($course2->id));
179         $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
180         $this->assertCount(0, $forums);
181     }
183     /**
184      * Test get forum posts
185      */
186     public function test_mod_forum_get_forum_discussion_posts() {
187         global $CFG, $PAGE;
189         $this->resetAfterTest(true);
191         // Set the CFG variable to allow track forums.
192         $CFG->forum_trackreadposts = true;
194         // Create a user who can track forums.
195         $record = new stdClass();
196         $record->trackforums = true;
197         $user1 = self::getDataGenerator()->create_user($record);
198         // Create a bunch of other users to post.
199         $user2 = self::getDataGenerator()->create_user();
200         $user3 = self::getDataGenerator()->create_user();
202         // Set the first created user to the test user.
203         self::setUser($user1);
205         // Create course to add the module.
206         $course1 = self::getDataGenerator()->create_course();
208         // Forum with tracking off.
209         $record = new stdClass();
210         $record->course = $course1->id;
211         $record->trackingtype = FORUM_TRACKING_OFF;
212         $forum1 = self::getDataGenerator()->create_module('forum', $record);
213         $forum1context = context_module::instance($forum1->cmid);
215         // Forum with tracking enabled.
216         $record = new stdClass();
217         $record->course = $course1->id;
218         $forum2 = self::getDataGenerator()->create_module('forum', $record);
219         $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
220         $forum2context = context_module::instance($forum2->cmid);
222         // Add discussions to the forums.
223         $record = new stdClass();
224         $record->course = $course1->id;
225         $record->userid = $user1->id;
226         $record->forum = $forum1->id;
227         $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
229         $record = new stdClass();
230         $record->course = $course1->id;
231         $record->userid = $user2->id;
232         $record->forum = $forum1->id;
233         $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
235         $record = new stdClass();
236         $record->course = $course1->id;
237         $record->userid = $user2->id;
238         $record->forum = $forum2->id;
239         $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
241         // Add 2 replies to the discussion 1 from different users.
242         $record = new stdClass();
243         $record->discussion = $discussion1->id;
244         $record->parent = $discussion1->firstpost;
245         $record->userid = $user2->id;
246         $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
247         $filename = 'shouldbeanimage.jpg';
248         // Add a fake inline image to the post.
249         $filerecordinline = array(
250             'contextid' => $forum1context->id,
251             'component' => 'mod_forum',
252             'filearea'  => 'post',
253             'itemid'    => $discussion1reply1->id,
254             'filepath'  => '/',
255             'filename'  => $filename,
256         );
257         $fs = get_file_storage();
258         $timepost = time();
259         $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
261         $record->parent = $discussion1reply1->id;
262         $record->userid = $user3->id;
263         $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
265         // Enrol the user in the  course.
266         $enrol = enrol_get_plugin('manual');
267         // Following line enrol and assign default role id to the user.
268         // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
269         $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
270         $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
272         // Delete one user, to test that we still receive posts by this user.
273         delete_user($user3);
275         // Create what we expect to be returned when querying the discussion.
276         $expectedposts = array(
277             'posts' => array(),
278             'ratinginfo' => array(
279                 'contextid' => $forum1context->id,
280                 'component' => 'mod_forum',
281                 'ratingarea' => 'post',
282                 'canviewall' => null,
283                 'canviewany' => null,
284                 'scales' => array(),
285                 'ratings' => array(),
286             ),
287             'warnings' => array(),
288         );
290         // User pictures are initially empty, we should get the links once the external function is called.
291         $expectedposts['posts'][] = array(
292             'id' => $discussion1reply2->id,
293             'discussion' => $discussion1reply2->discussion,
294             'parent' => $discussion1reply2->parent,
295             'userid' => (int) $discussion1reply2->userid,
296             'created' => $discussion1reply2->created,
297             'modified' => $discussion1reply2->modified,
298             'mailed' => $discussion1reply2->mailed,
299             'subject' => $discussion1reply2->subject,
300             'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
301                     $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
302             'messageformat' => 1,   // This value is usually changed by external_format_text() function.
303             'messagetrust' => $discussion1reply2->messagetrust,
304             'attachment' => $discussion1reply2->attachment,
305             'totalscore' => $discussion1reply2->totalscore,
306             'mailnow' => $discussion1reply2->mailnow,
307             'children' => array(),
308             'canreply' => true,
309             'postread' => false,
310             'userfullname' => fullname($user3),
311             'userpictureurl' => ''
312         );
314         $expectedposts['posts'][] = array(
315             'id' => $discussion1reply1->id,
316             'discussion' => $discussion1reply1->discussion,
317             'parent' => $discussion1reply1->parent,
318             'userid' => (int) $discussion1reply1->userid,
319             'created' => $discussion1reply1->created,
320             'modified' => $discussion1reply1->modified,
321             'mailed' => $discussion1reply1->mailed,
322             'subject' => $discussion1reply1->subject,
323             'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
324                     $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
325             'messageformat' => 1,   // This value is usually changed by external_format_text() function.
326             'messagetrust' => $discussion1reply1->messagetrust,
327             'attachment' => $discussion1reply1->attachment,
328             'messageinlinefiles' => array(
329                 array(
330                     'filename' => $filename,
331                     'filepath' => '/',
332                     'filesize' => '27',
333                     'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_forum', 'post',
334                                     $discussion1reply1->id, '/', $filename),
335                     'timemodified' => $timepost,
336                     'mimetype' => 'image/jpeg',
337                     'isexternalfile' => false,
338                 )
339             ),
340             'totalscore' => $discussion1reply1->totalscore,
341             'mailnow' => $discussion1reply1->mailnow,
342             'children' => array($discussion1reply2->id),
343             'canreply' => true,
344             'postread' => false,
345             'userfullname' => fullname($user2),
346             'userpictureurl' => ''
347         );
349         // Test a discussion with two additional posts (total 3 posts).
350         $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
351         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
352         $this->assertEquals(3, count($posts['posts']));
354         // Generate here the pictures because we need to wait to the external function to init the theme.
355         $userpicture = new user_picture($user3);
356         $userpicture->size = 1; // Size f1.
357         $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
359         $userpicture = new user_picture($user2);
360         $userpicture->size = 1; // Size f1.
361         $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
363         // Unset the initial discussion post.
364         array_pop($posts['posts']);
365         $this->assertEquals($expectedposts, $posts);
367         // Check we receive the unread count correctly on tracked forum.
368         forum_tp_count_forum_unread_posts($forum2cm, $course1, true);    // Reset static cache.
369         $result = mod_forum_external::get_forums_by_courses(array($course1->id));
370         $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
371         foreach ($result as $f) {
372             if ($f['id'] == $forum2->id) {
373                 $this->assertEquals(1, $f['unreadpostscount']);
374             }
375         }
377         // Test discussion without additional posts. There should be only one post (the one created by the discussion).
378         $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
379         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
380         $this->assertEquals(1, count($posts['posts']));
382         // Test discussion tracking on not tracked forum.
383         $result = mod_forum_external::view_forum_discussion($discussion1->id);
384         $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
385         $this->assertTrue($result['status']);
386         $this->assertEmpty($result['warnings']);
388         // Test posts have not been marked as read.
389         $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
390         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
391         foreach ($posts['posts'] as $post) {
392             $this->assertFalse($post['postread']);
393         }
395         // Test discussion tracking on tracked forum.
396         $result = mod_forum_external::view_forum_discussion($discussion3->id);
397         $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
398         $this->assertTrue($result['status']);
399         $this->assertEmpty($result['warnings']);
401         // Test posts have been marked as read.
402         $posts = mod_forum_external::get_forum_discussion_posts($discussion3->id, 'modified', 'DESC');
403         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
404         foreach ($posts['posts'] as $post) {
405             $this->assertTrue($post['postread']);
406         }
408         // Check we receive 0 unread posts.
409         forum_tp_count_forum_unread_posts($forum2cm, $course1, true);    // Reset static cache.
410         $result = mod_forum_external::get_forums_by_courses(array($course1->id));
411         $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
412         foreach ($result as $f) {
413             if ($f['id'] == $forum2->id) {
414                 $this->assertEquals(0, $f['unreadpostscount']);
415             }
416         }
417     }
419     /**
420      * Test get forum posts (qanda forum)
421      */
422     public function test_mod_forum_get_forum_discussion_posts_qanda() {
423         global $CFG, $DB;
425         $this->resetAfterTest(true);
427         $record = new stdClass();
428         $user1 = self::getDataGenerator()->create_user($record);
429         $user2 = self::getDataGenerator()->create_user();
431         // Set the first created user to the test user.
432         self::setUser($user1);
434         // Create course to add the module.
435         $course1 = self::getDataGenerator()->create_course();
436         $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
437         $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
439         // Forum with tracking off.
440         $record = new stdClass();
441         $record->course = $course1->id;
442         $record->type = 'qanda';
443         $forum1 = self::getDataGenerator()->create_module('forum', $record);
444         $forum1context = context_module::instance($forum1->cmid);
446         // Add discussions to the forums.
447         $record = new stdClass();
448         $record->course = $course1->id;
449         $record->userid = $user2->id;
450         $record->forum = $forum1->id;
451         $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
453         // Add 1 reply (not the actual user).
454         $record = new stdClass();
455         $record->discussion = $discussion1->id;
456         $record->parent = $discussion1->firstpost;
457         $record->userid = $user2->id;
458         $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
460         // We still see only the original post.
461         $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
462         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
463         $this->assertEquals(1, count($posts['posts']));
465         // Add a new reply, the user is going to be able to see only the original post and their new post.
466         $record = new stdClass();
467         $record->discussion = $discussion1->id;
468         $record->parent = $discussion1->firstpost;
469         $record->userid = $user1->id;
470         $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
472         $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
473         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
474         $this->assertEquals(2, count($posts['posts']));
476         // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
477         $discussion1reply2->created -= $CFG->maxeditingtime * 2;
478         $DB->update_record('forum_posts', $discussion1reply2);
480         $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
481         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
482         $this->assertEquals(3, count($posts['posts']));
483     }
485     /**
486      * Test get forum discussions paginated
487      */
488     public function test_mod_forum_get_forum_discussions_paginated() {
489         global $USER, $CFG, $DB, $PAGE;
491         $this->resetAfterTest(true);
493         // Set the CFG variable to allow track forums.
494         $CFG->forum_trackreadposts = true;
496         // Create a user who can track forums.
497         $record = new stdClass();
498         $record->trackforums = true;
499         $user1 = self::getDataGenerator()->create_user($record);
500         // Create a bunch of other users to post.
501         $user2 = self::getDataGenerator()->create_user();
502         $user3 = self::getDataGenerator()->create_user();
503         $user4 = self::getDataGenerator()->create_user();
505         // Set the first created user to the test user.
506         self::setUser($user1);
508         // Create courses to add the modules.
509         $course1 = self::getDataGenerator()->create_course();
511         // First forum with tracking off.
512         $record = new stdClass();
513         $record->course = $course1->id;
514         $record->trackingtype = FORUM_TRACKING_OFF;
515         $forum1 = self::getDataGenerator()->create_module('forum', $record);
517         // Add discussions to the forums.
518         $record = new stdClass();
519         $record->course = $course1->id;
520         $record->userid = $user1->id;
521         $record->forum = $forum1->id;
522         $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
524         // Add three replies to the discussion 1 from different users.
525         $record = new stdClass();
526         $record->discussion = $discussion1->id;
527         $record->parent = $discussion1->firstpost;
528         $record->userid = $user2->id;
529         $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
531         $record->parent = $discussion1reply1->id;
532         $record->userid = $user3->id;
533         $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
535         $record->userid = $user4->id;
536         $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
538         // Enrol the user in the first course.
539         $enrol = enrol_get_plugin('manual');
541         // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
542         $enrolinstances = enrol_get_instances($course1->id, true);
543         foreach ($enrolinstances as $courseenrolinstance) {
544             if ($courseenrolinstance->enrol == "manual") {
545                 $instance1 = $courseenrolinstance;
546                 break;
547             }
548         }
549         $enrol->enrol_user($instance1, $user1->id);
551         // Delete one user.
552         delete_user($user4);
554         // Assign capabilities to view discussions for forum 1.
555         $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
556         $context = context_module::instance($cm->id);
557         $newrole = create_role('Role 2', 'role2', 'Role 2 description');
558         $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
560         // Create what we expect to be returned when querying the forums.
562         $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
564         // User pictures are initially empty, we should get the links once the external function is called.
565         $expecteddiscussions = array(
566                 'id' => $discussion1->firstpost,
567                 'name' => $discussion1->name,
568                 'groupid' => $discussion1->groupid,
569                 'timemodified' => $discussion1reply3->created,
570                 'usermodified' => $discussion1reply3->userid,
571                 'timestart' => $discussion1->timestart,
572                 'timeend' => $discussion1->timeend,
573                 'discussion' => $discussion1->id,
574                 'parent' => 0,
575                 'userid' => $discussion1->userid,
576                 'created' => $post1->created,
577                 'modified' => $post1->modified,
578                 'mailed' => $post1->mailed,
579                 'subject' => $post1->subject,
580                 'message' => $post1->message,
581                 'messageformat' => $post1->messageformat,
582                 'messagetrust' => $post1->messagetrust,
583                 'attachment' => $post1->attachment,
584                 'totalscore' => $post1->totalscore,
585                 'mailnow' => $post1->mailnow,
586                 'userfullname' => fullname($user1),
587                 'usermodifiedfullname' => fullname($user4),
588                 'userpictureurl' => '',
589                 'usermodifiedpictureurl' => '',
590                 'numreplies' => 3,
591                 'numunread' => 0,
592                 'pinned' => FORUM_DISCUSSION_UNPINNED,
593                 'locked' => false,
594                 'canreply' => false,
595             );
597         // Call the external function passing forum id.
598         $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
599         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
600         $expectedreturn = array(
601             'discussions' => array($expecteddiscussions),
602             'warnings' => array()
603         );
605         // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
606         $userpicture = new user_picture($user1);
607         $userpicture->size = 1; // Size f1.
608         $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
610         $userpicture = new user_picture($user4);
611         $userpicture->size = 1; // Size f1.
612         $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
614         $this->assertEquals($expectedreturn, $discussions);
616         // Call without required view discussion capability.
617         $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
618         try {
619             mod_forum_external::get_forum_discussions_paginated($forum1->id);
620             $this->fail('Exception expected due to missing capability.');
621         } catch (moodle_exception $e) {
622             $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
623         }
625         // Unenrol user from second course.
626         $enrol->unenrol_user($instance1, $user1->id);
628         // Call for the second course we unenrolled the user from, make sure exception thrown.
629         try {
630             mod_forum_external::get_forum_discussions_paginated($forum1->id);
631             $this->fail('Exception expected due to being unenrolled from the course.');
632         } catch (moodle_exception $e) {
633             $this->assertEquals('requireloginerror', $e->errorcode);
634         }
635     }
637     /**
638      * Test get forum discussions paginated (qanda forums)
639      */
640     public function test_mod_forum_get_forum_discussions_paginated_qanda() {
642         $this->resetAfterTest(true);
644         // Create courses to add the modules.
645         $course = self::getDataGenerator()->create_course();
647         $user1 = self::getDataGenerator()->create_user();
648         $user2 = self::getDataGenerator()->create_user();
650         // First forum with tracking off.
651         $record = new stdClass();
652         $record->course = $course->id;
653         $record->type = 'qanda';
654         $forum = self::getDataGenerator()->create_module('forum', $record);
656         // Add discussions to the forums.
657         $discussionrecord = new stdClass();
658         $discussionrecord->course = $course->id;
659         $discussionrecord->userid = $user2->id;
660         $discussionrecord->forum = $forum->id;
661         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
663         self::setAdminUser();
664         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
665         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
667         $this->assertCount(1, $discussions['discussions']);
668         $this->assertCount(0, $discussions['warnings']);
670         self::setUser($user1);
671         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
673         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
674         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
676         $this->assertCount(1, $discussions['discussions']);
677         $this->assertCount(0, $discussions['warnings']);
679     }
681     /**
682      * Test add_discussion_post
683      */
684     public function test_add_discussion_post() {
685         global $CFG;
687         $this->resetAfterTest(true);
689         $user = self::getDataGenerator()->create_user();
690         $otheruser = self::getDataGenerator()->create_user();
692         self::setAdminUser();
694         // Create course to add the module.
695         $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
697         // Forum with tracking off.
698         $record = new stdClass();
699         $record->course = $course->id;
700         $forum = self::getDataGenerator()->create_module('forum', $record);
701         $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
702         $forumcontext = context_module::instance($forum->cmid);
704         // Add discussions to the forums.
705         $record = new stdClass();
706         $record->course = $course->id;
707         $record->userid = $user->id;
708         $record->forum = $forum->id;
709         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
711         // Try to post (user not enrolled).
712         self::setUser($user);
713         try {
714             mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
715             $this->fail('Exception expected due to being unenrolled from the course.');
716         } catch (moodle_exception $e) {
717             $this->assertEquals('requireloginerror', $e->errorcode);
718         }
720         $this->getDataGenerator()->enrol_user($user->id, $course->id);
721         $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
723         $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
724         $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
726         $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
727         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
728         // We receive the discussion and the post.
729         $this->assertEquals(2, count($posts['posts']));
731         $tested = false;
732         foreach ($posts['posts'] as $thispost) {
733             if ($createdpost['postid'] == $thispost['id']) {
734                 $this->assertEquals('some subject', $thispost['subject']);
735                 $this->assertEquals('some text here...', $thispost['message']);
736                 $tested = true;
737             }
738         }
739         $this->assertTrue($tested);
741         // Test inline and regular attachment in post
742         // Create a file in a draft area for inline attachments.
743         $draftidinlineattach = file_get_unused_draft_itemid();
744         $draftidattach = file_get_unused_draft_itemid();
745         self::setUser($user);
746         $usercontext = context_user::instance($user->id);
747         $filepath = '/';
748         $filearea = 'draft';
749         $component = 'user';
750         $filenameimg = 'shouldbeanimage.txt';
751         $filerecordinline = array(
752             'contextid' => $usercontext->id,
753             'component' => $component,
754             'filearea'  => $filearea,
755             'itemid'    => $draftidinlineattach,
756             'filepath'  => $filepath,
757             'filename'  => $filenameimg,
758         );
759         $fs = get_file_storage();
761         // Create a file in a draft area for regular attachments.
762         $filerecordattach = $filerecordinline;
763         $attachfilename = 'attachment.txt';
764         $filerecordattach['filename'] = $attachfilename;
765         $filerecordattach['itemid'] = $draftidattach;
766         $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
767         $fs->create_file_from_string($filerecordattach, 'simple text attachment');
769         $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
770                          array('name' => 'attachmentsid', 'value' => $draftidattach));
771         $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot
772                      . "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}"
773                      . '" alt="inlineimage">.';
774         $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'new post inline attachment',
775                                                                $dummytext, $options);
776         $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
778         $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
779         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
780         // We receive the discussion and the post.
781         // Can't guarantee order of posts during tests.
782         $postfound = false;
783         foreach ($posts['posts'] as $thispost) {
784             if ($createdpost['postid'] == $thispost['id']) {
785                 $this->assertEquals($createdpost['postid'], $thispost['id']);
786                 $this->assertEquals($thispost['attachment'], 1, "There should be a non-inline attachment");
787                 $this->assertCount(1, $thispost['attachments'], "There should be 1 attachment");
788                 $this->assertEquals($thispost['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
789                 $this->assertContains('pluginfile.php', $thispost['message']);
790                 $postfound = true;
791                 break;
792             }
793         }
795         $this->assertTrue($postfound);
797         // Check not posting in groups the user is not member of.
798         $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
799         groups_add_member($group->id, $otheruser->id);
801         $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
802         $record->forum = $forum->id;
803         $record->userid = $otheruser->id;
804         $record->groupid = $group->id;
805         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
807         try {
808             mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
809             $this->fail('Exception expected due to invalid permissions for posting.');
810         } catch (moodle_exception $e) {
811             $this->assertEquals('nopostforum', $e->errorcode);
812         }
814     }
816     /*
817      * Test add_discussion. A basic test since all the API functions are already covered by unit tests.
818      */
819     public function test_add_discussion() {
820         global $CFG, $USER;
821         $this->resetAfterTest(true);
823         // Create courses to add the modules.
824         $course = self::getDataGenerator()->create_course();
826         $user1 = self::getDataGenerator()->create_user();
827         $user2 = self::getDataGenerator()->create_user();
829         // First forum with tracking off.
830         $record = new stdClass();
831         $record->course = $course->id;
832         $record->type = 'news';
833         $forum = self::getDataGenerator()->create_module('forum', $record);
835         self::setUser($user1);
836         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
838         try {
839             mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
840             $this->fail('Exception expected due to invalid permissions.');
841         } catch (moodle_exception $e) {
842             $this->assertEquals('cannotcreatediscussion', $e->errorcode);
843         }
845         self::setAdminUser();
846         $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
847         $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
849         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
850         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
852         $this->assertCount(1, $discussions['discussions']);
853         $this->assertCount(0, $discussions['warnings']);
855         $this->assertEquals($createddiscussion['discussionid'], $discussions['discussions'][0]['discussion']);
856         $this->assertEquals(-1, $discussions['discussions'][0]['groupid']);
857         $this->assertEquals('the subject', $discussions['discussions'][0]['subject']);
858         $this->assertEquals('some text here...', $discussions['discussions'][0]['message']);
860         $discussion2pinned = mod_forum_external::add_discussion($forum->id, 'the pinned subject', 'some 2 text here...', -1,
861                                                                 array('options' => array('name' => 'discussionpinned',
862                                                                                          'value' => true)));
863         $discussion3 = mod_forum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...');
864         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
865         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
866         $this->assertCount(3, $discussions['discussions']);
867         $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']);
869         // Test inline and regular attachment in new discussion
870         // Create a file in a draft area for inline attachments.
872         $fs = get_file_storage();
874         $draftidinlineattach = file_get_unused_draft_itemid();
875         $draftidattach = file_get_unused_draft_itemid();
877         $usercontext = context_user::instance($USER->id);
878         $filepath = '/';
879         $filearea = 'draft';
880         $component = 'user';
881         $filenameimg = 'shouldbeanimage.txt';
882         $filerecord = array(
883             'contextid' => $usercontext->id,
884             'component' => $component,
885             'filearea'  => $filearea,
886             'itemid'    => $draftidinlineattach,
887             'filepath'  => $filepath,
888             'filename'  => $filenameimg,
889         );
891         // Create a file in a draft area for regular attachments.
892         $filerecordattach = $filerecord;
893         $attachfilename = 'attachment.txt';
894         $filerecordattach['filename'] = $attachfilename;
895         $filerecordattach['itemid'] = $draftidattach;
896         $fs->create_file_from_string($filerecord, 'image contents (not really)');
897         $fs->create_file_from_string($filerecordattach, 'simple text attachment');
899         $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot .
900                     "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}" .
901                     '" alt="inlineimage">.';
903         $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
904                          array('name' => 'attachmentsid', 'value' => $draftidattach));
905         $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the inline attachment subject',
906                                                                 $dummytext, -1, $options);
907         $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
909         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
910         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
912         $this->assertCount(4, $discussions['discussions']);
913         $this->assertCount(0, $createddiscussion['warnings']);
914         // Can't guarantee order of posts during tests.
915         $postfound = false;
916         foreach ($discussions['discussions'] as $thisdiscussion) {
917             if ($createddiscussion['discussionid'] == $thisdiscussion['discussion']) {
918                 $this->assertEquals($thisdiscussion['attachment'], 1, "There should be a non-inline attachment");
919                 $this->assertCount(1, $thisdiscussion['attachments'], "There should be 1 attachment");
920                 $this->assertEquals($thisdiscussion['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
921                 $this->assertNotContains('draftfile.php', $thisdiscussion['message']);
922                 $this->assertContains('pluginfile.php', $thisdiscussion['message']);
923                 $postfound = true;
924                 break;
925             }
926         }
928         $this->assertTrue($postfound);
929     }
931     /**
932      * Test adding discussions in a course with gorups
933      */
934     public function test_add_discussion_in_course_with_groups() {
935         global $CFG;
937         $this->resetAfterTest(true);
939         // Create course to add the module.
940         $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
941         $user = self::getDataGenerator()->create_user();
942         $this->getDataGenerator()->enrol_user($user->id, $course->id);
944         // Forum forcing separate gropus.
945         $record = new stdClass();
946         $record->course = $course->id;
947         $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
949         // Try to post (user not enrolled).
950         self::setUser($user);
952         // The user is not enroled in any group, try to post in a forum with separate groups.
953         try {
954             mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
955             $this->fail('Exception expected due to invalid group permissions.');
956         } catch (moodle_exception $e) {
957             $this->assertEquals('cannotcreatediscussion', $e->errorcode);
958         }
960         try {
961             mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0);
962             $this->fail('Exception expected due to invalid group permissions.');
963         } catch (moodle_exception $e) {
964             $this->assertEquals('cannotcreatediscussion', $e->errorcode);
965         }
967         // Create a group.
968         $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
970         // Try to post in a group the user is not enrolled.
971         try {
972             mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
973             $this->fail('Exception expected due to invalid group permissions.');
974         } catch (moodle_exception $e) {
975             $this->assertEquals('cannotcreatediscussion', $e->errorcode);
976         }
978         // Add the user to a group.
979         groups_add_member($group->id, $user->id);
981         // Try to post in a group the user is not enrolled.
982         try {
983             mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1);
984             $this->fail('Exception expected due to invalid group.');
985         } catch (moodle_exception $e) {
986             $this->assertEquals('cannotcreatediscussion', $e->errorcode);
987         }
989         // Nost add the discussion using a valid group.
990         $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
991         $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
993         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
994         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
996         $this->assertCount(1, $discussions['discussions']);
997         $this->assertCount(0, $discussions['warnings']);
998         $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']);
999         $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1001         // Now add a discussions without indicating a group. The function should guess the correct group.
1002         $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1003         $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1005         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1006         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1008         $this->assertCount(2, $discussions['discussions']);
1009         $this->assertCount(0, $discussions['warnings']);
1010         $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1011         $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1013         // Enrol the same user in other group.
1014         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1015         groups_add_member($group2->id, $user->id);
1017         // Now add a discussions without indicating a group. The function should guess the correct group (the first one).
1018         $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1019         $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1021         $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1022         $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1024         $this->assertCount(3, $discussions['discussions']);
1025         $this->assertCount(0, $discussions['warnings']);
1026         $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1027         $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1028         $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']);
1030     }
1032     /*
1033      * Test can_add_discussion. A basic test since all the API functions are already covered by unit tests.
1034      */
1035     public function test_can_add_discussion() {
1036         global $DB;
1037         $this->resetAfterTest(true);
1039         // Create courses to add the modules.
1040         $course = self::getDataGenerator()->create_course();
1042         $user = self::getDataGenerator()->create_user();
1044         // First forum with tracking off.
1045         $record = new stdClass();
1046         $record->course = $course->id;
1047         $record->type = 'news';
1048         $forum = self::getDataGenerator()->create_module('forum', $record);
1050         // User with no permissions to add in a news forum.
1051         self::setUser($user);
1052         $this->getDataGenerator()->enrol_user($user->id, $course->id);
1054         $result = mod_forum_external::can_add_discussion($forum->id);
1055         $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1056         $this->assertFalse($result['status']);
1057         $this->assertFalse($result['canpindiscussions']);
1058         $this->assertTrue($result['cancreateattachment']);
1060         // Disable attachments.
1061         $DB->set_field('forum', 'maxattachments', 0, array('id' => $forum->id));
1062         $result = mod_forum_external::can_add_discussion($forum->id);
1063         $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1064         $this->assertFalse($result['status']);
1065         $this->assertFalse($result['canpindiscussions']);
1066         $this->assertFalse($result['cancreateattachment']);
1067         $DB->set_field('forum', 'maxattachments', 1, array('id' => $forum->id));    // Enable attachments again.
1069         self::setAdminUser();
1070         $result = mod_forum_external::can_add_discussion($forum->id);
1071         $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1072         $this->assertTrue($result['status']);
1073         $this->assertTrue($result['canpindiscussions']);
1074         $this->assertTrue($result['cancreateattachment']);
1076     }
1078     /**
1079      * Test get forum posts discussions including rating information.
1080      */
1081     public function test_mod_forum_get_forum_discussion_rating_information() {
1082         global $DB, $CFG;
1083         require_once($CFG->dirroot . '/rating/lib.php');
1085         $this->resetAfterTest(true);
1087         $user1 = self::getDataGenerator()->create_user();
1088         $user2 = self::getDataGenerator()->create_user();
1089         $user3 = self::getDataGenerator()->create_user();
1090         $teacher = self::getDataGenerator()->create_user();
1092         // Create course to add the module.
1093         $course = self::getDataGenerator()->create_course();
1095         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1096         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
1097         $this->getDataGenerator()->enrol_user($user1->id, $course->id, $studentrole->id, 'manual');
1098         $this->getDataGenerator()->enrol_user($user2->id, $course->id, $studentrole->id, 'manual');
1099         $this->getDataGenerator()->enrol_user($user3->id, $course->id, $studentrole->id, 'manual');
1100         $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
1102         // Create the forum.
1103         $record = new stdClass();
1104         $record->course = $course->id;
1105         // Set Aggregate type = Average of ratings.
1106         $record->assessed = RATING_AGGREGATE_AVERAGE;
1107         $record->scale = 100;
1108         $forum = self::getDataGenerator()->create_module('forum', $record);
1109         $context = context_module::instance($forum->cmid);
1111         // Add discussion to the forum.
1112         $record = new stdClass();
1113         $record->course = $course->id;
1114         $record->userid = $user1->id;
1115         $record->forum = $forum->id;
1116         $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1118         // Retrieve the first post.
1119         $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1121         // Rate the discussion as user2.
1122         $rating1 = new stdClass();
1123         $rating1->contextid = $context->id;
1124         $rating1->component = 'mod_forum';
1125         $rating1->ratingarea = 'post';
1126         $rating1->itemid = $post->id;
1127         $rating1->rating = 50;
1128         $rating1->scaleid = 100;
1129         $rating1->userid = $user2->id;
1130         $rating1->timecreated = time();
1131         $rating1->timemodified = time();
1132         $rating1->id = $DB->insert_record('rating', $rating1);
1134         // Rate the discussion as user3.
1135         $rating2 = new stdClass();
1136         $rating2->contextid = $context->id;
1137         $rating2->component = 'mod_forum';
1138         $rating2->ratingarea = 'post';
1139         $rating2->itemid = $post->id;
1140         $rating2->rating = 100;
1141         $rating2->scaleid = 100;
1142         $rating2->userid = $user3->id;
1143         $rating2->timecreated = time() + 1;
1144         $rating2->timemodified = time() + 1;
1145         $rating2->id = $DB->insert_record('rating', $rating2);
1147         // Retrieve the rating for the post as student.
1148         $this->setUser($user1);
1149         $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1150         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1151         $this->assertCount(1, $posts['ratinginfo']['ratings']);
1152         $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
1153         $this->assertFalse($posts['ratinginfo']['canviewall']);
1154         $this->assertFalse($posts['ratinginfo']['ratings'][0]['canrate']);
1155         $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
1156         $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
1158         // Retrieve the rating for the post as teacher.
1159         $this->setUser($teacher);
1160         $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1161         $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1162         $this->assertCount(1, $posts['ratinginfo']['ratings']);
1163         $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
1164         $this->assertTrue($posts['ratinginfo']['canviewall']);
1165         $this->assertTrue($posts['ratinginfo']['ratings'][0]['canrate']);
1166         $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
1167         $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
1168     }