MDL-65071 forum: Fix webservice unit tests
[moodle.git] / mod / forum / tests / externallib_test.php
CommitLineData
2b9fe87d 1<?php
2b9fe87d
MN
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
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 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29
30require_once($CFG->dirroot . '/webservice/tests/helpers.php');
4669389d 31require_once($CFG->dirroot . '/mod/forum/lib.php');
2b9fe87d
MN
32
33class mod_forum_external_testcase extends externallib_advanced_testcase {
34
35 /**
36 * Tests set up
37 */
38 protected function setUp() {
39 global $CFG;
40
59075a43
AN
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();
44
2b9fe87d
MN
45 require_once($CFG->dirroot . '/mod/forum/externallib.php');
46 }
47
59075a43
AN
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 }
53
2b9fe87d
MN
54 /**
55 * Test get forums
56 */
57 public function test_mod_forum_get_forums_by_courses() {
58 global $USER, $CFG, $DB;
59
60 $this->resetAfterTest(true);
61
62 // Create a user.
4669389d 63 $user = self::getDataGenerator()->create_user(array('trackforums' => 1));
2b9fe87d
MN
64
65 // Set to the user.
66 self::setUser($user);
67
68 // Create courses to add the modules.
69 $course1 = self::getDataGenerator()->create_course();
70 $course2 = self::getDataGenerator()->create_course();
71
72 // First forum.
73 $record = new stdClass();
74 $record->introformat = FORMAT_HTML;
75 $record->course = $course1->id;
4669389d 76 $record->trackingtype = FORUM_TRACKING_FORCED;
2b9fe87d
MN
77 $forum1 = self::getDataGenerator()->create_module('forum', $record);
78
79 // Second forum.
80 $record = new stdClass();
81 $record->introformat = FORMAT_HTML;
82 $record->course = $course2->id;
4669389d 83 $record->trackingtype = FORUM_TRACKING_OFF;
2b9fe87d 84 $forum2 = self::getDataGenerator()->create_module('forum', $record);
7ef49bd3 85 $forum2->introfiles = [];
2b9fe87d 86
7ea6ada3
JL
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;
ea5b910b 95 $forum1->cancreatediscussions = true;
4669389d 96 $forum1->istracked = true;
2256bb74 97 $forum1->unreadpostscount = 0;
7ef49bd3 98 $forum1->introfiles = [];
7ea6ada3
JL
99
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;
ea5b910b
JL
108 // Default limited role, no create discussion capability enabled.
109 $forum2->cancreatediscussions = false;
4669389d 110 $forum2->istracked = false;
7ea6ada3 111
2b9fe87d
MN
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)));
115
116 // Enrol the user in two courses.
909f27ac
JM
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.
2b9fe87d 120 $enrol = enrol_get_plugin('manual');
2b9fe87d
MN
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);
129
2b9fe87d 130 // Assign capabilities to view forums for forum 2.
74b63eae 131 $cm2 = get_coursemodule_from_id('forum', $forum2->cmid, 0, false, MUST_EXIST);
2b9fe87d
MN
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);
135
136 // Create what we expect to be returned when querying the two courses.
c8f1d8a0
JL
137 unset($forum1->displaywordcount);
138 unset($forum2->displaywordcount);
139
2b9fe87d
MN
140 $expectedforums = array();
141 $expectedforums[$forum1->id] = (array) $forum1;
142 $expectedforums[$forum2->id] = (array) $forum2;
143
144 // Call the external function passing course ids.
145 $forums = mod_forum_external::get_forums_by_courses(array($course1->id, $course2->id));
c8f1d8a0
JL
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 }
2b9fe87d
MN
151
152 // Call the external function without passing course id.
153 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
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 }
2b9fe87d
MN
159
160 // Unenrol user from second course and alter expected forums.
161 $enrol->unenrol_user($instance2, $user->id);
162 unset($expectedforums[$forum2->id]);
163
164 // Call the external function without passing course id.
165 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
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]);
ea5b910b
JL
169 $this->assertTrue($forums[0]['cancreatediscussions']);
170
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']);
c8f1d8a0
JL
176
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);
a9a0cb69
MN
181 }
182
897ac0de
P
183 /**
184 * Test the toggle favourite state
185 */
186 public function test_mod_forum_toggle_favourite_state() {
187 global $USER, $CFG, $DB;
188
189 $this->resetAfterTest(true);
190
191 // Create a user.
192 $user = self::getDataGenerator()->create_user(array('trackforums' => 1));
193
194 // Set to the user.
195 self::setUser($user);
196
197 // Create courses to add the modules.
198 $course1 = self::getDataGenerator()->create_course();
199 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
200
201 $record = new stdClass();
202 $record->introformat = FORMAT_HTML;
203 $record->course = $course1->id;
204 $record->trackingtype = FORUM_TRACKING_OFF;
205 $forum1 = self::getDataGenerator()->create_module('forum', $record);
206 $forum1->introfiles = [];
207
208 // Add discussions to the forums.
209 $record = new stdClass();
210 $record->course = $course1->id;
211 $record->userid = $user->id;
212 $record->forum = $forum1->id;
213 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
214
13cd05ac 215 $response = mod_forum_external::toggle_favourite_state($discussion1->id, 1);
897ac0de
P
216 $response = external_api::clean_returnvalue(mod_forum_external::toggle_favourite_state_returns(), $response);
217 $this->assertTrue($response['userstate']['favourited']);
218
13cd05ac 219 $response = mod_forum_external::toggle_favourite_state($discussion1->id, 0);
897ac0de
P
220 $response = external_api::clean_returnvalue(mod_forum_external::toggle_favourite_state_returns(), $response);
221 $this->assertFalse($response['userstate']['favourited']);
d3cac88d
P
222
223 $this->setUser(0);
224 try {
225 $response = mod_forum_external::toggle_favourite_state($discussion1->id, 0);
226 } catch (moodle_exception $e) {
227 $this->assertEquals('requireloginerror', $e->errorcode);
228 }
897ac0de
P
229 }
230
309e8698
P
231 /**
232 * Test the toggle pin state
233 */
234 public function test_mod_forum_set_pin_state() {
235 $this->resetAfterTest(true);
236
237 // Create a user.
238 $user = self::getDataGenerator()->create_user(array('trackforums' => 1));
239
240 // Set to the user.
241 self::setUser($user);
242
243 // Create courses to add the modules.
244 $course1 = self::getDataGenerator()->create_course();
245 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
246
247 $record = new stdClass();
248 $record->introformat = FORMAT_HTML;
249 $record->course = $course1->id;
250 $record->trackingtype = FORUM_TRACKING_OFF;
251 $forum1 = self::getDataGenerator()->create_module('forum', $record);
252 $forum1->introfiles = [];
253
254 // Add discussions to the forums.
255 $record = new stdClass();
256 $record->course = $course1->id;
257 $record->userid = $user->id;
258 $record->forum = $forum1->id;
259 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
260
261 try {
13cd05ac 262 $response = mod_forum_external::set_pin_state($discussion1->id, 1);
309e8698
P
263 } catch (Exception $e) {
264 $this->assertEquals('cannotpindiscussions', $e->errorcode);
265 }
266
267 self::setAdminUser();
13cd05ac 268 $response = mod_forum_external::set_pin_state($discussion1->id, 1);
309e8698
P
269 $response = external_api::clean_returnvalue(mod_forum_external::set_pin_state_returns(), $response);
270 $this->assertTrue($response['pinned']);
271
13cd05ac 272 $response = mod_forum_external::set_pin_state($discussion1->id, 0);
309e8698
P
273 $response = external_api::clean_returnvalue(mod_forum_external::set_pin_state_returns(), $response);
274 $this->assertFalse($response['pinned']);
275 }
276
e2ede426
JL
277 /**
278 * Test get forum posts
279 */
280 public function test_mod_forum_get_forum_discussion_posts() {
d85bedf7 281 global $CFG, $PAGE;
e2ede426
JL
282
283 $this->resetAfterTest(true);
284
285 // Set the CFG variable to allow track forums.
286 $CFG->forum_trackreadposts = true;
287
288 // Create a user who can track forums.
289 $record = new stdClass();
290 $record->trackforums = true;
291 $user1 = self::getDataGenerator()->create_user($record);
292 // Create a bunch of other users to post.
293 $user2 = self::getDataGenerator()->create_user();
294 $user3 = self::getDataGenerator()->create_user();
295
296 // Set the first created user to the test user.
297 self::setUser($user1);
298
299 // Create course to add the module.
300 $course1 = self::getDataGenerator()->create_course();
301
302 // Forum with tracking off.
303 $record = new stdClass();
304 $record->course = $course1->id;
305 $record->trackingtype = FORUM_TRACKING_OFF;
306 $forum1 = self::getDataGenerator()->create_module('forum', $record);
307 $forum1context = context_module::instance($forum1->cmid);
308
db3c9ff8
PFO
309 // Forum with tracking enabled.
310 $record = new stdClass();
311 $record->course = $course1->id;
312 $forum2 = self::getDataGenerator()->create_module('forum', $record);
2256bb74 313 $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
db3c9ff8
PFO
314 $forum2context = context_module::instance($forum2->cmid);
315
e2ede426
JL
316 // Add discussions to the forums.
317 $record = new stdClass();
318 $record->course = $course1->id;
319 $record->userid = $user1->id;
320 $record->forum = $forum1->id;
321 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
322
323 $record = new stdClass();
324 $record->course = $course1->id;
325 $record->userid = $user2->id;
326 $record->forum = $forum1->id;
327 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
328
db3c9ff8
PFO
329 $record = new stdClass();
330 $record->course = $course1->id;
331 $record->userid = $user2->id;
332 $record->forum = $forum2->id;
333 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
334
e2ede426
JL
335 // Add 2 replies to the discussion 1 from different users.
336 $record = new stdClass();
337 $record->discussion = $discussion1->id;
338 $record->parent = $discussion1->firstpost;
339 $record->userid = $user2->id;
340 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
c8743f62
JL
341 $filename = 'shouldbeanimage.jpg';
342 // Add a fake inline image to the post.
343 $filerecordinline = array(
344 'contextid' => $forum1context->id,
345 'component' => 'mod_forum',
346 'filearea' => 'post',
347 'itemid' => $discussion1reply1->id,
348 'filepath' => '/',
349 'filename' => $filename,
350 );
351 $fs = get_file_storage();
352 $timepost = time();
353 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
e2ede426
JL
354
355 $record->parent = $discussion1reply1->id;
356 $record->userid = $user3->id;
6c344ff2 357 $record->tags = array('Cats', 'Dogs');
e2ede426
JL
358 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
359
360 // Enrol the user in the course.
361 $enrol = enrol_get_plugin('manual');
362 // Following line enrol and assign default role id to the user.
363 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
364 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
365 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
81f810dc
JL
366
367 // Delete one user, to test that we still receive posts by this user.
368 delete_user($user3);
e2ede426
JL
369
370 // Create what we expect to be returned when querying the discussion.
371 $expectedposts = array(
372 'posts' => array(),
b7ce46df
JL
373 'ratinginfo' => array(
374 'contextid' => $forum1context->id,
375 'component' => 'mod_forum',
376 'ratingarea' => 'post',
377 'canviewall' => null,
378 'canviewany' => null,
379 'scales' => array(),
380 'ratings' => array(),
381 ),
e2ede426
JL
382 'warnings' => array(),
383 );
694bf0c7 384
d85bedf7 385 // User pictures are initially empty, we should get the links once the external function is called.
e2ede426
JL
386 $expectedposts['posts'][] = array(
387 'id' => $discussion1reply2->id,
388 'discussion' => $discussion1reply2->discussion,
389 'parent' => $discussion1reply2->parent,
48fb0250 390 'userid' => (int) $discussion1reply2->userid,
e2ede426
JL
391 'created' => $discussion1reply2->created,
392 'modified' => $discussion1reply2->modified,
393 'mailed' => $discussion1reply2->mailed,
394 'subject' => $discussion1reply2->subject,
395 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
396 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
48fb0250 397 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
398 'messagetrust' => $discussion1reply2->messagetrust,
399 'attachment' => $discussion1reply2->attachment,
400 'totalscore' => $discussion1reply2->totalscore,
401 'mailnow' => $discussion1reply2->mailnow,
402 'children' => array(),
403 'canreply' => true,
404 'postread' => false,
694bf0c7 405 'userfullname' => fullname($user3),
3e95e09b
AN
406 'userpictureurl' => '',
407 'deleted' => false,
bc4c7337 408 'isprivatereply' => false,
6c344ff2 409 'tags' => \core_tag\external\util::get_item_tags('mod_forum', 'forum_posts', $discussion1reply2->id),
e2ede426 410 );
6c344ff2
JL
411 // Cast to expected.
412 $this->assertCount(2, $expectedposts['posts'][0]['tags']);
413 $expectedposts['posts'][0]['tags'][0]['isstandard'] = (bool) $expectedposts['posts'][0]['tags'][0]['isstandard'];
414 $expectedposts['posts'][0]['tags'][1]['isstandard'] = (bool) $expectedposts['posts'][0]['tags'][1]['isstandard'];
694bf0c7 415
e2ede426
JL
416 $expectedposts['posts'][] = array(
417 'id' => $discussion1reply1->id,
418 'discussion' => $discussion1reply1->discussion,
419 'parent' => $discussion1reply1->parent,
48fb0250 420 'userid' => (int) $discussion1reply1->userid,
e2ede426
JL
421 'created' => $discussion1reply1->created,
422 'modified' => $discussion1reply1->modified,
423 'mailed' => $discussion1reply1->mailed,
424 'subject' => $discussion1reply1->subject,
425 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
426 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
48fb0250 427 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
428 'messagetrust' => $discussion1reply1->messagetrust,
429 'attachment' => $discussion1reply1->attachment,
c8743f62
JL
430 'messageinlinefiles' => array(
431 array(
432 'filename' => $filename,
433 'filepath' => '/',
434 'filesize' => '27',
435 'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_forum', 'post',
436 $discussion1reply1->id, '/', $filename),
437 'timemodified' => $timepost,
438 'mimetype' => 'image/jpeg',
1104a9fa 439 'isexternalfile' => false,
c8743f62
JL
440 )
441 ),
e2ede426
JL
442 'totalscore' => $discussion1reply1->totalscore,
443 'mailnow' => $discussion1reply1->mailnow,
d2c58b95 444 'children' => array($discussion1reply2->id),
e2ede426
JL
445 'canreply' => true,
446 'postread' => false,
694bf0c7 447 'userfullname' => fullname($user2),
3e95e09b
AN
448 'userpictureurl' => '',
449 'deleted' => false,
bc4c7337 450 'isprivatereply' => false,
6c344ff2 451 'tags' => array(),
e2ede426
JL
452 );
453
454 // Test a discussion with two additional posts (total 3 posts).
455 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
456 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
457 $this->assertEquals(3, count($posts['posts']));
458
d85bedf7
JL
459 // Generate here the pictures because we need to wait to the external function to init the theme.
460 $userpicture = new user_picture($user3);
461 $userpicture->size = 1; // Size f1.
462 $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
463
464 $userpicture = new user_picture($user2);
465 $userpicture->size = 1; // Size f1.
466 $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
467
e2ede426
JL
468 // Unset the initial discussion post.
469 array_pop($posts['posts']);
470 $this->assertEquals($expectedposts, $posts);
471
2256bb74
JL
472 // Check we receive the unread count correctly on tracked forum.
473 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
474 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
475 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
476 foreach ($result as $f) {
477 if ($f['id'] == $forum2->id) {
478 $this->assertEquals(1, $f['unreadpostscount']);
479 }
480 }
481
e2ede426
JL
482 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
483 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
484 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
485 $this->assertEquals(1, count($posts['posts']));
486
db3c9ff8
PFO
487 // Test discussion tracking on not tracked forum.
488 $result = mod_forum_external::view_forum_discussion($discussion1->id);
489 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
490 $this->assertTrue($result['status']);
491 $this->assertEmpty($result['warnings']);
492
493 // Test posts have not been marked as read.
494 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
495 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
496 foreach ($posts['posts'] as $post) {
497 $this->assertFalse($post['postread']);
498 }
499
500 // Test discussion tracking on tracked forum.
501 $result = mod_forum_external::view_forum_discussion($discussion3->id);
502 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
503 $this->assertTrue($result['status']);
504 $this->assertEmpty($result['warnings']);
505
506 // Test posts have been marked as read.
507 $posts = mod_forum_external::get_forum_discussion_posts($discussion3->id, 'modified', 'DESC');
508 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
509 foreach ($posts['posts'] as $post) {
510 $this->assertTrue($post['postread']);
511 }
2256bb74
JL
512
513 // Check we receive 0 unread posts.
514 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
515 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
516 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
517 foreach ($result as $f) {
518 if ($f['id'] == $forum2->id) {
519 $this->assertEquals(0, $f['unreadpostscount']);
520 }
521 }
e2ede426 522 }
c2586672 523
8245daba
P
524 /**
525 * Test get forum posts
526 *
527 * Tests is similar to the get_forum_discussion_posts only utilizing the new return structure and entities
528 */
529 public function test_mod_forum_get_discussion_posts() {
530 global $CFG, $PAGE;
531
532 $this->resetAfterTest(true);
533
534 // Set the CFG variable to allow track forums.
535 $CFG->forum_trackreadposts = true;
536
537 $urlfactory = mod_forum\local\container::get_url_factory();
538 $legacyfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
539 $entityfactory = mod_forum\local\container::get_entity_factory();
540
541 // Create a user who can track forums.
542 $record = new stdClass();
543 $record->trackforums = true;
544 $user1 = self::getDataGenerator()->create_user($record);
545 // Create a bunch of other users to post.
546 $user2 = self::getDataGenerator()->create_user();
547 $user2entity = $entityfactory->get_author_from_stdclass($user2);
548 $exporteduser2 = [
549 'id' => (int) $user2->id,
550 'fullname' => fullname($user2),
551 'groups' => [],
552 'urls' => [
553 'profile' => $urlfactory->get_author_profile_url($user2entity),
554 'profileimage' => $urlfactory->get_author_profile_image_url($user2entity),
555 ]
556 ];
557 $user2->fullname = $exporteduser2['fullname'];
558
559 $user3 = self::getDataGenerator()->create_user(['fullname' => "Mr Pants 1"]);
560 $user3entity = $entityfactory->get_author_from_stdclass($user3);
561 $exporteduser3 = [
562 'id' => (int) $user3->id,
563 'fullname' => fullname($user3),
564 'groups' => [],
565 'urls' => [
566 'profile' => $urlfactory->get_author_profile_url($user3entity),
567 'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
568 ]
569 ];
570 $user3->fullname = $exporteduser3['fullname'];
571 $forumgenerator = self::getDataGenerator()->get_plugin_generator('mod_forum');
572
573 // Set the first created user to the test user.
574 self::setUser($user1);
575
576 // Create course to add the module.
577 $course1 = self::getDataGenerator()->create_course();
578
579 // Forum with tracking off.
580 $record = new stdClass();
581 $record->course = $course1->id;
582 $record->trackingtype = FORUM_TRACKING_OFF;
583 $forum1 = self::getDataGenerator()->create_module('forum', $record);
584 $forum1context = context_module::instance($forum1->cmid);
585
586 // Forum with tracking enabled.
587 $record = new stdClass();
588 $record->course = $course1->id;
589 $forum2 = self::getDataGenerator()->create_module('forum', $record);
590 $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
591 $forum2context = context_module::instance($forum2->cmid);
592
593 // Add discussions to the forums.
594 $record = new stdClass();
595 $record->course = $course1->id;
596 $record->userid = $user1->id;
597 $record->forum = $forum1->id;
598 $discussion1 = $forumgenerator->create_discussion($record);
599
600 $record = new stdClass();
601 $record->course = $course1->id;
602 $record->userid = $user2->id;
603 $record->forum = $forum1->id;
604 $discussion2 = $forumgenerator->create_discussion($record);
605
606 $record = new stdClass();
607 $record->course = $course1->id;
608 $record->userid = $user2->id;
609 $record->forum = $forum2->id;
610 $discussion3 = $forumgenerator->create_discussion($record);
611
612 // Add 2 replies to the discussion 1 from different users.
613 $record = new stdClass();
614 $record->discussion = $discussion1->id;
615 $record->parent = $discussion1->firstpost;
616 $record->userid = $user2->id;
617 $discussion1reply1 = $forumgenerator->create_post($record);
618 $filename = 'shouldbeanimage.jpg';
619 // Add a fake inline image to the post.
620 $filerecordinline = array(
621 'contextid' => $forum1context->id,
622 'component' => 'mod_forum',
623 'filearea' => 'post',
624 'itemid' => $discussion1reply1->id,
625 'filepath' => '/',
626 'filename' => $filename,
627 );
628 $fs = get_file_storage();
629 $timepost = time();
630 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
631
632 $record->parent = $discussion1reply1->id;
633 $record->userid = $user3->id;
634 $discussion1reply2 = $forumgenerator->create_post($record);
635
636 // Enrol the user in the course.
637 $enrol = enrol_get_plugin('manual');
638 // Following line enrol and assign default role id to the user.
639 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
640 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
641 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
642
643 // Delete one user, to test that we still receive posts by this user.
644 delete_user($user3);
645
646 // Create what we expect to be returned when querying the discussion.
647 $expectedposts = array(
648 'posts' => array(),
649 'ratinginfo' => array(
650 'contextid' => $forum1context->id,
651 'component' => 'mod_forum',
652 'ratingarea' => 'post',
653 'canviewall' => null,
654 'canviewany' => null,
655 'scales' => array(),
656 'ratings' => array(),
657 ),
658 'warnings' => array(),
659 );
660
661 // User pictures are initially empty, we should get the links once the external function is called.
662 $isolatedurl = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply2->discussion);
663 $isolatedurl->params(['parent' => $discussion1reply2->id]);
664 $expectedposts['posts'][] = array(
665 'id' => $discussion1reply2->id,
666 'discussionid' => $discussion1reply2->discussion,
667 'parentid' => $discussion1reply2->parent,
668 'hasparent' => true,
669 'timecreated' => $discussion1reply2->created,
670 'subject' => $discussion1reply2->subject,
671 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
672 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
673 'messageformat' => 1, // This value is usually changed by external_format_text() function.
674 'unread' => null,
675 'isdeleted' => false,
676 'isprivatereply' => false,
677 'haswordcount' => false,
678 'wordcount' => null,
679 'author'=> $exporteduser3,
680 'attachments' => [],
681 'tags' => [],
682 'html' => [
683 'rating' => null,
684 'taglist' => null,
685 'authorsubheading' => $forumgenerator->get_author_subheading_html((object)$exporteduser3, $discussion1reply2->created)
686 ],
687 'capabilities' => [
688 'view' => 1,
689 'edit' => 0,
690 'delete' => 0,
691 'split' => 0,
692 'reply' => 1,
693 'export' => 0,
694 'controlreadstatus' => 0
695 ],
696 'urls' => [
697 'view' => $urlfactory->get_view_post_url_from_post_id($discussion1reply2->discussion, $discussion1reply2->id),
698 'viewisolated' => $isolatedurl->out(false),
699 'viewparent' => $urlfactory->get_view_post_url_from_post_id($discussion1reply2->discussion, $discussion1reply2->parent),
700 'edit' => null,
701 'delete' =>null,
702 'split' => null,
703 'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
704 'reply' => $discussion1reply2->id
705 ]))->out(false),
706 'export' => null,
707 'markasread' => null,
708 'markasunread' => null,
709 'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply2->discussion),
710 ],
711 );
712
713
714 $isolatedurl = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply1->discussion);
715 $isolatedurl->params(['parent' => $discussion1reply1->id]);
716 $expectedposts['posts'][] = array(
717 'id' => $discussion1reply1->id,
718 'discussionid' => $discussion1reply1->discussion,
719 'parentid' => $discussion1reply1->parent,
720 'hasparent' => true,
721 'timecreated' => $discussion1reply1->created,
722 'subject' => $discussion1reply1->subject,
723 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
724 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
725 'messageformat' => 1, // This value is usually changed by external_format_text() function.
726 'unread' => null,
727 'isdeleted' => false,
728 'isprivatereply' => false,
729 'haswordcount' => false,
730 'wordcount' => null,
731 'author'=> $exporteduser2,
732 'attachments' => [],
733 'tags' => [],
734 'html' => [
735 'rating' => null,
736 'taglist' => null,
737 'authorsubheading' => $forumgenerator->get_author_subheading_html((object)$exporteduser2, $discussion1reply1->created)
738 ],
739 'capabilities' => [
740 'view' => 1,
741 'edit' => 0,
742 'delete' => 0,
743 'split' => 0,
744 'reply' => 1,
745 'export' => 0,
746 'controlreadstatus' => 0
747 ],
748 'urls' => [
749 'view' => $urlfactory->get_view_post_url_from_post_id($discussion1reply1->discussion, $discussion1reply1->id),
750 'viewisolated' => $isolatedurl->out(false),
751 'viewparent' => $urlfactory->get_view_post_url_from_post_id($discussion1reply1->discussion, $discussion1reply1->parent),
752 'edit' => null,
753 'delete' =>null,
754 'split' => null,
755 'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
756 'reply' => $discussion1reply1->id
757 ]))->out(false),
758 'export' => null,
759 'markasread' => null,
760 'markasunread' => null,
761 'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply1->discussion),
762 ],
763 );
764
765 // Test a discussion with two additional posts (total 3 posts).
766 $posts = mod_forum_external::get_discussion_posts($discussion1->id, 'modified', 'DESC');
767 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
768 $this->assertEquals(3, count($posts['posts']));
769
770 // Unset the initial discussion post.
771 array_pop($posts['posts']);
772 $this->assertEquals($expectedposts, $posts);
773
774 // Check we receive the unread count correctly on tracked forum.
775 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
776 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
777 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
778 foreach ($result as $f) {
779 if ($f['id'] == $forum2->id) {
780 $this->assertEquals(1, $f['unreadpostscount']);
781 }
782 }
783
784 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
785 $posts = mod_forum_external::get_discussion_posts($discussion2->id, 'modified', 'DESC');
786 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
787 $this->assertEquals(1, count($posts['posts']));
788
789 // Test discussion tracking on not tracked forum.
790 $result = mod_forum_external::view_forum_discussion($discussion1->id);
791 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
792 $this->assertTrue($result['status']);
793 $this->assertEmpty($result['warnings']);
794
795 // Test posts have not been marked as read.
796 $posts = mod_forum_external::get_discussion_posts($discussion1->id, 'modified', 'DESC');
797 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
798 foreach ($posts['posts'] as $post) {
799 $this->assertNull($post['unread']);
800 }
801
802 // Test discussion tracking on tracked forum.
803 $result = mod_forum_external::view_forum_discussion($discussion3->id);
804 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
805 $this->assertTrue($result['status']);
806 $this->assertEmpty($result['warnings']);
807
808 // Test posts have been marked as read.
809 $posts = mod_forum_external::get_discussion_posts($discussion3->id, 'modified', 'DESC');
810 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
811 foreach ($posts['posts'] as $post) {
812 $this->assertFalse($post['unread']);
813 }
814
815 // Check we receive 0 unread posts.
816 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
817 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
818 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
819 foreach ($result as $f) {
820 if ($f['id'] == $forum2->id) {
821 $this->assertEquals(0, $f['unreadpostscount']);
822 }
823 }
824 }
825
3e95e09b
AN
826 /**
827 * Test get forum posts
828 */
829 public function test_mod_forum_get_forum_discussion_posts_deleted() {
830 global $CFG, $PAGE;
831
832 $this->resetAfterTest(true);
833 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
834
835 // Create a course and enrol some users in it.
836 $course1 = self::getDataGenerator()->create_course();
837
838 // Create users.
839 $user1 = self::getDataGenerator()->create_user();
840 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
841 $user2 = self::getDataGenerator()->create_user();
842 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
843
844 // Set the first created user to the test user.
845 self::setUser($user1);
846
847 // Create test data.
848 $forum1 = self::getDataGenerator()->create_module('forum', (object) [
849 'course' => $course1->id,
850 ]);
851 $forum1context = context_module::instance($forum1->cmid);
852
853 // Add discussions to the forum.
854 $discussion = $generator->create_discussion((object) [
855 'course' => $course1->id,
856 'userid' => $user1->id,
857 'forum' => $forum1->id,
858 ]);
859
860 $discussion2 = $generator->create_discussion((object) [
861 'course' => $course1->id,
862 'userid' => $user2->id,
863 'forum' => $forum1->id,
864 ]);
865
866 // Add replies to the discussion.
867 $discussionreply1 = $generator->create_post((object) [
868 'discussion' => $discussion->id,
869 'parent' => $discussion->firstpost,
870 'userid' => $user2->id,
871 ]);
872 $discussionreply2 = $generator->create_post((object) [
873 'discussion' => $discussion->id,
874 'parent' => $discussionreply1->id,
875 'userid' => $user2->id,
876 'subject' => '',
877 'message' => '',
878 'messageformat' => FORMAT_PLAIN,
879 'deleted' => 1,
880 ]);
881 $discussionreply3 = $generator->create_post((object) [
882 'discussion' => $discussion->id,
883 'parent' => $discussion->firstpost,
884 'userid' => $user2->id,
885 ]);
886
887 // Test where some posts have been marked as deleted.
888 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id, 'modified', 'DESC');
889 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
890 $deletedsubject = get_string('privacy:request:delete:post:subject', 'mod_forum');
891 $deletedmessage = get_string('privacy:request:delete:post:message', 'mod_forum');
892
893 foreach ($posts['posts'] as $post) {
894 if ($post['id'] == $discussionreply2->id) {
895 $this->assertTrue($post['deleted']);
896 $this->assertEquals($deletedsubject, $post['subject']);
897 $this->assertEquals($deletedmessage, $post['message']);
898 } else {
899 $this->assertFalse($post['deleted']);
900 $this->assertNotEquals($deletedsubject, $post['subject']);
901 $this->assertNotEquals($deletedmessage, $post['message']);
902 }
903 }
904 }
905
b1aa7dfa
JL
906 /**
907 * Test get forum posts (qanda forum)
908 */
909 public function test_mod_forum_get_forum_discussion_posts_qanda() {
910 global $CFG, $DB;
911
912 $this->resetAfterTest(true);
913
914 $record = new stdClass();
915 $user1 = self::getDataGenerator()->create_user($record);
916 $user2 = self::getDataGenerator()->create_user();
917
918 // Set the first created user to the test user.
919 self::setUser($user1);
920
921 // Create course to add the module.
922 $course1 = self::getDataGenerator()->create_course();
923 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
924 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
925
926 // Forum with tracking off.
927 $record = new stdClass();
928 $record->course = $course1->id;
929 $record->type = 'qanda';
930 $forum1 = self::getDataGenerator()->create_module('forum', $record);
931 $forum1context = context_module::instance($forum1->cmid);
932
933 // Add discussions to the forums.
934 $record = new stdClass();
935 $record->course = $course1->id;
936 $record->userid = $user2->id;
937 $record->forum = $forum1->id;
938 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
939
940 // Add 1 reply (not the actual user).
941 $record = new stdClass();
942 $record->discussion = $discussion1->id;
943 $record->parent = $discussion1->firstpost;
944 $record->userid = $user2->id;
945 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
946
947 // We still see only the original post.
948 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
949 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
950 $this->assertEquals(1, count($posts['posts']));
951
952 // Add a new reply, the user is going to be able to see only the original post and their new post.
953 $record = new stdClass();
954 $record->discussion = $discussion1->id;
955 $record->parent = $discussion1->firstpost;
956 $record->userid = $user1->id;
957 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
958
959 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
960 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
961 $this->assertEquals(2, count($posts['posts']));
962
963 // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
964 $discussion1reply2->created -= $CFG->maxeditingtime * 2;
965 $DB->update_record('forum_posts', $discussion1reply2);
966
967 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
968 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
969 $this->assertEquals(3, count($posts['posts']));
b1aa7dfa
JL
970 }
971
c2586672
JL
972 /**
973 * Test get forum discussions paginated
974 */
975 public function test_mod_forum_get_forum_discussions_paginated() {
d85bedf7 976 global $USER, $CFG, $DB, $PAGE;
c2586672
JL
977
978 $this->resetAfterTest(true);
979
980 // Set the CFG variable to allow track forums.
981 $CFG->forum_trackreadposts = true;
982
983 // Create a user who can track forums.
984 $record = new stdClass();
985 $record->trackforums = true;
986 $user1 = self::getDataGenerator()->create_user($record);
987 // Create a bunch of other users to post.
988 $user2 = self::getDataGenerator()->create_user();
989 $user3 = self::getDataGenerator()->create_user();
990 $user4 = self::getDataGenerator()->create_user();
991
992 // Set the first created user to the test user.
993 self::setUser($user1);
994
995 // Create courses to add the modules.
996 $course1 = self::getDataGenerator()->create_course();
997
998 // First forum with tracking off.
999 $record = new stdClass();
1000 $record->course = $course1->id;
1001 $record->trackingtype = FORUM_TRACKING_OFF;
1002 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1003
1004 // Add discussions to the forums.
1005 $record = new stdClass();
1006 $record->course = $course1->id;
1007 $record->userid = $user1->id;
1008 $record->forum = $forum1->id;
1009 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1010
1011 // Add three replies to the discussion 1 from different users.
1012 $record = new stdClass();
1013 $record->discussion = $discussion1->id;
1014 $record->parent = $discussion1->firstpost;
1015 $record->userid = $user2->id;
1016 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1017
1018 $record->parent = $discussion1reply1->id;
1019 $record->userid = $user3->id;
1020 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1021
1022 $record->userid = $user4->id;
1023 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1024
1025 // Enrol the user in the first course.
1026 $enrol = enrol_get_plugin('manual');
1027
1028 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1029 $enrolinstances = enrol_get_instances($course1->id, true);
1030 foreach ($enrolinstances as $courseenrolinstance) {
1031 if ($courseenrolinstance->enrol == "manual") {
1032 $instance1 = $courseenrolinstance;
1033 break;
1034 }
1035 }
1036 $enrol->enrol_user($instance1, $user1->id);
1037
81f810dc
JL
1038 // Delete one user.
1039 delete_user($user4);
1040
c2586672
JL
1041 // Assign capabilities to view discussions for forum 1.
1042 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1043 $context = context_module::instance($cm->id);
1044 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1045 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1046
1047 // Create what we expect to be returned when querying the forums.
1048
1049 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
c2586672 1050
d85bedf7 1051 // User pictures are initially empty, we should get the links once the external function is called.
c2586672
JL
1052 $expecteddiscussions = array(
1053 'id' => $discussion1->firstpost,
1054 'name' => $discussion1->name,
28c2f2b2 1055 'groupid' => (int) $discussion1->groupid,
c2586672 1056 'timemodified' => $discussion1reply3->created,
28c2f2b2
MG
1057 'usermodified' => (int) $discussion1reply3->userid,
1058 'timestart' => (int) $discussion1->timestart,
1059 'timeend' => (int) $discussion1->timeend,
c2586672
JL
1060 'discussion' => $discussion1->id,
1061 'parent' => 0,
28c2f2b2
MG
1062 'userid' => (int) $discussion1->userid,
1063 'created' => (int) $post1->created,
1064 'modified' => (int) $post1->modified,
1065 'mailed' => (int) $post1->mailed,
c2586672
JL
1066 'subject' => $post1->subject,
1067 'message' => $post1->message,
28c2f2b2
MG
1068 'messageformat' => (int) $post1->messageformat,
1069 'messagetrust' => (int) $post1->messagetrust,
c2586672 1070 'attachment' => $post1->attachment,
28c2f2b2
MG
1071 'totalscore' => (int) $post1->totalscore,
1072 'mailnow' => (int) $post1->mailnow,
c2586672
JL
1073 'userfullname' => fullname($user1),
1074 'usermodifiedfullname' => fullname($user4),
d85bedf7
JL
1075 'userpictureurl' => '',
1076 'usermodifiedpictureurl' => '',
c2586672 1077 'numreplies' => 3,
5f219cf1 1078 'numunread' => 0,
28c2f2b2 1079 'pinned' => (bool) FORUM_DISCUSSION_UNPINNED,
0f3bbfd4
AN
1080 'locked' => false,
1081 'canreply' => false,
28c2f2b2 1082 'canlock' => false
c2586672
JL
1083 );
1084
1085 // Call the external function passing forum id.
1086 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
1087 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1088 $expectedreturn = array(
1089 'discussions' => array($expecteddiscussions),
1090 'warnings' => array()
1091 );
d85bedf7
JL
1092
1093 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
1094 $userpicture = new user_picture($user1);
1095 $userpicture->size = 1; // Size f1.
1096 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1097
1098 $userpicture = new user_picture($user4);
1099 $userpicture->size = 1; // Size f1.
1100 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1101
c2586672
JL
1102 $this->assertEquals($expectedreturn, $discussions);
1103
1104 // Call without required view discussion capability.
1105 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1106 try {
1107 mod_forum_external::get_forum_discussions_paginated($forum1->id);
1108 $this->fail('Exception expected due to missing capability.');
1109 } catch (moodle_exception $e) {
1110 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
1111 }
1112
1113 // Unenrol user from second course.
1114 $enrol->unenrol_user($instance1, $user1->id);
1115
1116 // Call for the second course we unenrolled the user from, make sure exception thrown.
1117 try {
1118 mod_forum_external::get_forum_discussions_paginated($forum1->id);
1119 $this->fail('Exception expected due to being unenrolled from the course.');
1120 } catch (moodle_exception $e) {
1121 $this->assertEquals('requireloginerror', $e->errorcode);
1122 }
565cccfa
P
1123
1124 $this->setAdminUser();
1125 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
1126 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1127 $this->assertTrue($discussions['discussions'][0]['canlock']);
c2586672 1128 }
039c81f0
JL
1129
1130 /**
1131 * Test get forum discussions paginated (qanda forums)
1132 */
1133 public function test_mod_forum_get_forum_discussions_paginated_qanda() {
1134
1135 $this->resetAfterTest(true);
1136
1137 // Create courses to add the modules.
1138 $course = self::getDataGenerator()->create_course();
1139
1140 $user1 = self::getDataGenerator()->create_user();
1141 $user2 = self::getDataGenerator()->create_user();
1142
1143 // First forum with tracking off.
1144 $record = new stdClass();
1145 $record->course = $course->id;
1146 $record->type = 'qanda';
1147 $forum = self::getDataGenerator()->create_module('forum', $record);
1148
1149 // Add discussions to the forums.
1150 $discussionrecord = new stdClass();
1151 $discussionrecord->course = $course->id;
1152 $discussionrecord->userid = $user2->id;
1153 $discussionrecord->forum = $forum->id;
1154 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
1155
1156 self::setAdminUser();
1157 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1158 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1159
1160 $this->assertCount(1, $discussions['discussions']);
1161 $this->assertCount(0, $discussions['warnings']);
1162
1163 self::setUser($user1);
1164 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1165
1166 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1167 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1168
1169 $this->assertCount(1, $discussions['discussions']);
1170 $this->assertCount(0, $discussions['warnings']);
1171
1172 }
50a20317 1173
3f9ed16e
MG
1174 /**
1175 * Test get forum discussions
1176 */
1177 public function test_mod_forum_get_forum_discussions() {
1178 global $CFG, $DB, $PAGE;
1179
1180 $this->resetAfterTest(true);
1181
1182 // Set the CFG variable to allow track forums.
1183 $CFG->forum_trackreadposts = true;
1184
1185 // Create a user who can track forums.
1186 $record = new stdClass();
1187 $record->trackforums = true;
1188 $user1 = self::getDataGenerator()->create_user($record);
1189 // Create a bunch of other users to post.
1190 $user2 = self::getDataGenerator()->create_user();
1191 $user3 = self::getDataGenerator()->create_user();
1192 $user4 = self::getDataGenerator()->create_user();
1193
1194 // Set the first created user to the test user.
1195 self::setUser($user1);
1196
1197 // Create courses to add the modules.
1198 $course1 = self::getDataGenerator()->create_course();
1199
1200 // First forum with tracking off.
1201 $record = new stdClass();
1202 $record->course = $course1->id;
1203 $record->trackingtype = FORUM_TRACKING_OFF;
1204 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1205
1206 // Add discussions to the forums.
1207 $record = new stdClass();
1208 $record->course = $course1->id;
1209 $record->userid = $user1->id;
1210 $record->forum = $forum1->id;
1211 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1212
1213 // Add three replies to the discussion 1 from different users.
1214 $record = new stdClass();
1215 $record->discussion = $discussion1->id;
1216 $record->parent = $discussion1->firstpost;
1217 $record->userid = $user2->id;
1218 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1219
1220 $record->parent = $discussion1reply1->id;
1221 $record->userid = $user3->id;
1222 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1223
1224 $record->userid = $user4->id;
1225 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1226
1227 // Enrol the user in the first course.
1228 $enrol = enrol_get_plugin('manual');
1229
1230 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1231 $enrolinstances = enrol_get_instances($course1->id, true);
1232 foreach ($enrolinstances as $courseenrolinstance) {
1233 if ($courseenrolinstance->enrol == "manual") {
1234 $instance1 = $courseenrolinstance;
1235 break;
1236 }
1237 }
1238 $enrol->enrol_user($instance1, $user1->id);
1239
1240 // Delete one user.
1241 delete_user($user4);
1242
1243 // Assign capabilities to view discussions for forum 1.
1244 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1245 $context = context_module::instance($cm->id);
1246 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1247 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1248
1249 // Create what we expect to be returned when querying the forums.
1250
1251 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
1252
1253 // User pictures are initially empty, we should get the links once the external function is called.
1254 $expecteddiscussions = array(
1255 'id' => $discussion1->firstpost,
1256 'name' => $discussion1->name,
1257 'groupid' => (int) $discussion1->groupid,
1258 'timemodified' => (int) $discussion1reply3->created,
1259 'usermodified' => (int) $discussion1reply3->userid,
1260 'timestart' => (int) $discussion1->timestart,
1261 'timeend' => (int) $discussion1->timeend,
1262 'discussion' => (int) $discussion1->id,
1263 'parent' => 0,
1264 'userid' => (int) $discussion1->userid,
1265 'created' => (int) $post1->created,
1266 'modified' => (int) $post1->modified,
1267 'mailed' => (int) $post1->mailed,
1268 'subject' => $post1->subject,
1269 'message' => $post1->message,
1270 'messageformat' => (int) $post1->messageformat,
1271 'messagetrust' => (int) $post1->messagetrust,
1272 'attachment' => $post1->attachment,
1273 'totalscore' => (int) $post1->totalscore,
1274 'mailnow' => (int) $post1->mailnow,
1275 'userfullname' => fullname($user1),
1276 'usermodifiedfullname' => fullname($user4),
1277 'userpictureurl' => '',
1278 'usermodifiedpictureurl' => '',
1279 'numreplies' => 3,
1280 'numunread' => 0,
1281 'pinned' => (bool) FORUM_DISCUSSION_UNPINNED,
1282 'locked' => false,
1283 'canreply' => false,
1284 'canlock' => false,
28c2f2b2
MG
1285 'starred' => false,
1286 'canfavourite' => true
3f9ed16e
MG
1287 );
1288
1289 // Call the external function passing forum id.
1290 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1291 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1292 $expectedreturn = array(
1293 'discussions' => array($expecteddiscussions),
1294 'warnings' => array()
1295 );
1296
1297 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
1298 $userpicture = new user_picture($user1);
1299 $userpicture->size = 2; // Size f2.
1300 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1301
1302 $userpicture = new user_picture($user4);
1303 $userpicture->size = 2; // Size f2.
1304 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1305
1306 $this->assertEquals($expectedreturn, $discussions);
1307
28c2f2b2
MG
1308 // Test the starring functionality return.
1309 $t = mod_forum_external::toggle_favourite_state($discussion1->id, 1);
1310 $expectedreturn['discussions'][0]['starred'] = true;
1311 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1312 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1313 $this->assertEquals($expectedreturn, $discussions);
1314
3f9ed16e
MG
1315 // Call without required view discussion capability.
1316 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1317 try {
1318 mod_forum_external::get_forum_discussions($forum1->id);
1319 $this->fail('Exception expected due to missing capability.');
1320 } catch (moodle_exception $e) {
1321 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
1322 }
1323
1324 // Unenrol user from second course.
1325 $enrol->unenrol_user($instance1, $user1->id);
1326
1327 // Call for the second course we unenrolled the user from, make sure exception thrown.
1328 try {
1329 mod_forum_external::get_forum_discussions($forum1->id);
1330 $this->fail('Exception expected due to being unenrolled from the course.');
1331 } catch (moodle_exception $e) {
1332 $this->assertEquals('requireloginerror', $e->errorcode);
1333 }
1334
1335 $this->setAdminUser();
1336 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1337 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1338 $this->assertTrue($discussions['discussions'][0]['canlock']);
1339 }
1340
1341 /**
1342 * Test the sorting in get forum discussions
1343 */
1344 public function test_mod_forum_get_forum_discussions_sorting() {
1345 global $CFG, $DB, $PAGE;
1346
1347 $this->resetAfterTest(true);
1348
1349 // Set the CFG variable to allow track forums.
1350 $CFG->forum_trackreadposts = true;
1351
1352 // Create a user who can track forums.
1353 $record = new stdClass();
1354 $record->trackforums = true;
1355 $user1 = self::getDataGenerator()->create_user($record);
1356 // Create a bunch of other users to post.
1357 $user2 = self::getDataGenerator()->create_user();
1358 $user3 = self::getDataGenerator()->create_user();
1359 $user4 = self::getDataGenerator()->create_user();
1360
1361 // Set the first created user to the test user.
1362 self::setUser($user1);
1363
1364 // Create courses to add the modules.
1365 $course1 = self::getDataGenerator()->create_course();
1366
1367 // Enrol the user in the first course.
1368 $enrol = enrol_get_plugin('manual');
1369
1370 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1371 $enrolinstances = enrol_get_instances($course1->id, true);
1372 foreach ($enrolinstances as $courseenrolinstance) {
1373 if ($courseenrolinstance->enrol == "manual") {
1374 $instance1 = $courseenrolinstance;
1375 break;
1376 }
1377 }
1378 $enrol->enrol_user($instance1, $user1->id);
1379
1380 // First forum with tracking off.
1381 $record = new stdClass();
1382 $record->course = $course1->id;
1383 $record->trackingtype = FORUM_TRACKING_OFF;
1384 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1385
1386 // Assign capabilities to view discussions for forum 1.
1387 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1388 $context = context_module::instance($cm->id);
1389 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1390 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1391
1392 // Add discussions to the forums.
1393 $record = new stdClass();
1394 $record->course = $course1->id;
1395 $record->userid = $user1->id;
1396 $record->forum = $forum1->id;
1397 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1398 sleep(1);
1399
1400 // Add three replies to the discussion 1 from different users.
1401 $record = new stdClass();
1402 $record->discussion = $discussion1->id;
1403 $record->parent = $discussion1->firstpost;
1404 $record->userid = $user2->id;
1405 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1406 sleep(1);
1407
1408 $record->parent = $discussion1reply1->id;
1409 $record->userid = $user3->id;
1410 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1411 sleep(1);
1412
1413 $record->userid = $user4->id;
1414 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1415 sleep(1);
1416
1417 // Create discussion2.
1418 $record2 = new stdClass();
1419 $record2->course = $course1->id;
1420 $record2->userid = $user1->id;
1421 $record2->forum = $forum1->id;
1422 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record2);
1423 sleep(1);
1424
1425 // Add one reply to the discussion 2.
1426 $record2 = new stdClass();
1427 $record2->discussion = $discussion2->id;
1428 $record2->parent = $discussion2->firstpost;
1429 $record2->userid = $user2->id;
1430 $discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record2);
1431 sleep(1);
1432
1433 // Create discussion 3.
1434 $record3 = new stdClass();
1435 $record3->course = $course1->id;
1436 $record3->userid = $user1->id;
1437 $record3->forum = $forum1->id;
1438 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record3);
1439 sleep(1);
1440
1441 // Add two replies to the discussion 3.
1442 $record3 = new stdClass();
1443 $record3->discussion = $discussion3->id;
1444 $record3->parent = $discussion3->firstpost;
1445 $record3->userid = $user2->id;
1446 $discussion3reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record3);
1447 sleep(1);
1448
1449 $record3->parent = $discussion3reply1->id;
1450 $record3->userid = $user3->id;
1451 $discussion3reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record3);
1452
1453 // Call the external function passing forum id.
1454 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1455 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1456 // Discussions should be ordered by last post date in descending order by default.
1457 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id);
1458 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1459 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1460
1461 $vaultfactory = \mod_forum\local\container::get_vault_factory();
1462 $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault();
1463
1464 // Call the external function passing forum id and sort order parameter.
1465 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_LASTPOST_ASC);
1466 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1467 // Discussions should be ordered by last post date in ascending order.
1468 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1469 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1470 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id);
1471
1472 // Call the external function passing forum id and sort order parameter.
1473 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_CREATED_DESC);
1474 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1475 // Discussions should be ordered by discussion creation date in descending order.
1476 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id);
1477 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1478 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1479
1480 // Call the external function passing forum id and sort order parameter.
1481 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_CREATED_ASC);
1482 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1483 // Discussions should be ordered by discussion creation date in ascending order.
1484 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1485 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1486 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id);
1487
1488 // Call the external function passing forum id and sort order parameter.
1489 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_DESC);
1490 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1491 // Discussions should be ordered by the number of replies in descending order.
1492 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1493 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1494 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion2->id);
1495
1496 // Call the external function passing forum id and sort order parameter.
1497 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_ASC);
1498 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1499 // Discussions should be ordered by the number of replies in ascending order.
1500 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1501 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1502 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1503
1504 // Pin discussion2.
1505 $DB->update_record('forum_discussions',
1506 (object) array('id' => $discussion2->id, 'pinned' => FORUM_DISCUSSION_PINNED));
1507
1508 // Call the external function passing forum id.
1509 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1510 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1511 // Discussions should be ordered by last post date in descending order by default.
1512 // Pinned discussions should be at the top of the list.
1513 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1514 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1515 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1516
1517 // Call the external function passing forum id and sort order parameter.
1518 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_LASTPOST_ASC);
1519 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1520 // Discussions should be ordered by last post date in ascending order.
1521 // Pinned discussions should be at the top of the list.
1522 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1523 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion1->id);
1524 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id);
1525 }
1526
50a20317
JL
1527 /**
1528 * Test add_discussion_post
1529 */
1530 public function test_add_discussion_post() {
e881c4f5 1531 global $CFG;
50a20317
JL
1532
1533 $this->resetAfterTest(true);
1534
1535 $user = self::getDataGenerator()->create_user();
1536 $otheruser = self::getDataGenerator()->create_user();
1537
1538 self::setAdminUser();
1539
1540 // Create course to add the module.
1541 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1542
1543 // Forum with tracking off.
1544 $record = new stdClass();
1545 $record->course = $course->id;
1546 $forum = self::getDataGenerator()->create_module('forum', $record);
1547 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
1548 $forumcontext = context_module::instance($forum->cmid);
1549
1550 // Add discussions to the forums.
1551 $record = new stdClass();
1552 $record->course = $course->id;
1553 $record->userid = $user->id;
1554 $record->forum = $forum->id;
1555 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1556
1557 // Try to post (user not enrolled).
1558 self::setUser($user);
1559 try {
1560 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1561 $this->fail('Exception expected due to being unenrolled from the course.');
1562 } catch (moodle_exception $e) {
1563 $this->assertEquals('requireloginerror', $e->errorcode);
1564 }
1565
1566 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1567 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
1568
41182118
BK
1569 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1570 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
50a20317
JL
1571
1572 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1573 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1574 // We receive the discussion and the post.
1575 $this->assertEquals(2, count($posts['posts']));
43ff833d
JL
1576
1577 $tested = false;
41182118
BK
1578 foreach ($posts['posts'] as $thispost) {
1579 if ($createdpost['postid'] == $thispost['id']) {
1580 $this->assertEquals('some subject', $thispost['subject']);
1581 $this->assertEquals('some text here...', $thispost['message']);
43ff833d
JL
1582 $tested = true;
1583 }
1584 }
1585 $this->assertTrue($tested);
50a20317 1586
e881c4f5 1587 // Test inline and regular attachment in post
41182118
BK
1588 // Create a file in a draft area for inline attachments.
1589 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5 1590 $draftidattach = file_get_unused_draft_itemid();
41182118
BK
1591 self::setUser($user);
1592 $usercontext = context_user::instance($user->id);
1593 $filepath = '/';
1594 $filearea = 'draft';
1595 $component = 'user';
1596 $filenameimg = 'shouldbeanimage.txt';
e881c4f5 1597 $filerecordinline = array(
41182118
BK
1598 'contextid' => $usercontext->id,
1599 'component' => $component,
1600 'filearea' => $filearea,
1601 'itemid' => $draftidinlineattach,
1602 'filepath' => $filepath,
1603 'filename' => $filenameimg,
1604 );
1605 $fs = get_file_storage();
41182118 1606
e881c4f5
BK
1607 // Create a file in a draft area for regular attachments.
1608 $filerecordattach = $filerecordinline;
1609 $attachfilename = 'attachment.txt';
1610 $filerecordattach['filename'] = $attachfilename;
1611 $filerecordattach['itemid'] = $draftidattach;
1612 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1613 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1614
48143990 1615 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 1616 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
1617 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot
1618 . "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}"
1619 . '" alt="inlineimage">.';
1620 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'new post inline attachment',
1621 $dummytext, $options);
1622 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
1623
1624 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1625 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1626 // We receive the discussion and the post.
1627 // Can't guarantee order of posts during tests.
1628 $postfound = false;
1629 foreach ($posts['posts'] as $thispost) {
1630 if ($createdpost['postid'] == $thispost['id']) {
1631 $this->assertEquals($createdpost['postid'], $thispost['id']);
e881c4f5
BK
1632 $this->assertEquals($thispost['attachment'], 1, "There should be a non-inline attachment");
1633 $this->assertCount(1, $thispost['attachments'], "There should be 1 attachment");
1634 $this->assertEquals($thispost['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
1635 $this->assertContains('pluginfile.php', $thispost['message']);
1636 $postfound = true;
e881c4f5 1637 break;
41182118
BK
1638 }
1639 }
1640
1641 $this->assertTrue($postfound);
1642
50a20317
JL
1643 // Check not posting in groups the user is not member of.
1644 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1645 groups_add_member($group->id, $otheruser->id);
1646
1647 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
1648 $record->forum = $forum->id;
1649 $record->userid = $otheruser->id;
1650 $record->groupid = $group->id;
1651 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1652
1653 try {
1654 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1655 $this->fail('Exception expected due to invalid permissions for posting.');
1656 } catch (moodle_exception $e) {
50a20317
JL
1657 $this->assertEquals('nopostforum', $e->errorcode);
1658 }
1659
1660 }
7ab43ac8
JL
1661
1662 /*
1663 * Test add_discussion. A basic test since all the API functions are already covered by unit tests.
1664 */
1665 public function test_add_discussion() {
41182118 1666 global $CFG, $USER;
7ab43ac8
JL
1667 $this->resetAfterTest(true);
1668
1669 // Create courses to add the modules.
1670 $course = self::getDataGenerator()->create_course();
1671
1672 $user1 = self::getDataGenerator()->create_user();
1673 $user2 = self::getDataGenerator()->create_user();
1674
1675 // First forum with tracking off.
1676 $record = new stdClass();
1677 $record->course = $course->id;
1678 $record->type = 'news';
1679 $forum = self::getDataGenerator()->create_module('forum', $record);
1680
1681 self::setUser($user1);
1682 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1683
1684 try {
1685 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1686 $this->fail('Exception expected due to invalid permissions.');
1687 } catch (moodle_exception $e) {
1688 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1689 }
1690
1691 self::setAdminUser();
41182118
BK
1692 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1693 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
7ab43ac8
JL
1694
1695 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1696 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1697
1698 $this->assertCount(1, $discussions['discussions']);
1699 $this->assertCount(0, $discussions['warnings']);
1700
41182118 1701 $this->assertEquals($createddiscussion['discussionid'], $discussions['discussions'][0]['discussion']);
7ab43ac8
JL
1702 $this->assertEquals(-1, $discussions['discussions'][0]['groupid']);
1703 $this->assertEquals('the subject', $discussions['discussions'][0]['subject']);
1704 $this->assertEquals('some text here...', $discussions['discussions'][0]['message']);
1705
5f219cf1
BK
1706 $discussion2pinned = mod_forum_external::add_discussion($forum->id, 'the pinned subject', 'some 2 text here...', -1,
1707 array('options' => array('name' => 'discussionpinned',
1708 'value' => true)));
1709 $discussion3 = mod_forum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...');
1710 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1711 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1712 $this->assertCount(3, $discussions['discussions']);
1713 $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']);
41182118 1714
e881c4f5 1715 // Test inline and regular attachment in new discussion
41182118 1716 // Create a file in a draft area for inline attachments.
e881c4f5
BK
1717
1718 $fs = get_file_storage();
1719
41182118 1720 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5
BK
1721 $draftidattach = file_get_unused_draft_itemid();
1722
41182118
BK
1723 $usercontext = context_user::instance($USER->id);
1724 $filepath = '/';
1725 $filearea = 'draft';
1726 $component = 'user';
1727 $filenameimg = 'shouldbeanimage.txt';
1728 $filerecord = array(
1729 'contextid' => $usercontext->id,
1730 'component' => $component,
1731 'filearea' => $filearea,
1732 'itemid' => $draftidinlineattach,
1733 'filepath' => $filepath,
1734 'filename' => $filenameimg,
1735 );
e881c4f5 1736
e881c4f5
BK
1737 // Create a file in a draft area for regular attachments.
1738 $filerecordattach = $filerecord;
1739 $attachfilename = 'attachment.txt';
1740 $filerecordattach['filename'] = $attachfilename;
1741 $filerecordattach['itemid'] = $draftidattach;
41182118 1742 $fs->create_file_from_string($filerecord, 'image contents (not really)');
e881c4f5
BK
1743 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1744
41182118
BK
1745 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot .
1746 "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}" .
1747 '" alt="inlineimage">.';
1748
48143990 1749 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 1750 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
1751 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the inline attachment subject',
1752 $dummytext, -1, $options);
1753 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
1754
1755 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1756 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1757
1758 $this->assertCount(4, $discussions['discussions']);
1759 $this->assertCount(0, $createddiscussion['warnings']);
1760 // Can't guarantee order of posts during tests.
1761 $postfound = false;
1762 foreach ($discussions['discussions'] as $thisdiscussion) {
1763 if ($createddiscussion['discussionid'] == $thisdiscussion['discussion']) {
e881c4f5
BK
1764 $this->assertEquals($thisdiscussion['attachment'], 1, "There should be a non-inline attachment");
1765 $this->assertCount(1, $thisdiscussion['attachments'], "There should be 1 attachment");
1766 $this->assertEquals($thisdiscussion['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
1767 $this->assertNotContains('draftfile.php', $thisdiscussion['message']);
1768 $this->assertContains('pluginfile.php', $thisdiscussion['message']);
1769 $postfound = true;
e881c4f5 1770 break;
41182118
BK
1771 }
1772 }
1773
1774 $this->assertTrue($postfound);
7ab43ac8
JL
1775 }
1776
1777 /**
1778 * Test adding discussions in a course with gorups
1779 */
1780 public function test_add_discussion_in_course_with_groups() {
1781 global $CFG;
1782
1783 $this->resetAfterTest(true);
1784
1785 // Create course to add the module.
1786 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1787 $user = self::getDataGenerator()->create_user();
1788 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1789
1790 // Forum forcing separate gropus.
1791 $record = new stdClass();
1792 $record->course = $course->id;
1793 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
1794
1795 // Try to post (user not enrolled).
1796 self::setUser($user);
1797
1798 // The user is not enroled in any group, try to post in a forum with separate groups.
1799 try {
1800 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1801 $this->fail('Exception expected due to invalid group permissions.');
1802 } catch (moodle_exception $e) {
1803 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1804 }
1805
1806 try {
1807 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0);
1808 $this->fail('Exception expected due to invalid group permissions.');
1809 } catch (moodle_exception $e) {
1810 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1811 }
1812
1813 // Create a group.
1814 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1815
1816 // Try to post in a group the user is not enrolled.
1817 try {
1818 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1819 $this->fail('Exception expected due to invalid group permissions.');
1820 } catch (moodle_exception $e) {
1821 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1822 }
1823
1824 // Add the user to a group.
1825 groups_add_member($group->id, $user->id);
1826
1827 // Try to post in a group the user is not enrolled.
1828 try {
1829 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1);
1830 $this->fail('Exception expected due to invalid group.');
1831 } catch (moodle_exception $e) {
1832 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1833 }
1834
1835 // Nost add the discussion using a valid group.
1836 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1837 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1838
1839 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1840 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1841
1842 $this->assertCount(1, $discussions['discussions']);
1843 $this->assertCount(0, $discussions['warnings']);
1844 $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']);
1845 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1846
1847 // Now add a discussions without indicating a group. The function should guess the correct group.
1848 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1849 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1850
1851 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1852 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1853
1854 $this->assertCount(2, $discussions['discussions']);
1855 $this->assertCount(0, $discussions['warnings']);
1856 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1857 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1858
1859 // Enrol the same user in other group.
1860 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1861 groups_add_member($group2->id, $user->id);
1862
1863 // Now add a discussions without indicating a group. The function should guess the correct group (the first one).
1864 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1865 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1866
1867 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1868 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1869
1870 $this->assertCount(3, $discussions['discussions']);
1871 $this->assertCount(0, $discussions['warnings']);
1872 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1873 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1874 $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']);
1875
1876 }
1877
f5b4320e
P
1878 /*
1879 * Test set_lock_state.
1880 */
1881 public function test_set_lock_state() {
1882 global $DB;
1883 $this->resetAfterTest(true);
1884
1885 // Create courses to add the modules.
1886 $course = self::getDataGenerator()->create_course();
1887 $user = self::getDataGenerator()->create_user();
1888 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1889
1890 // First forum with tracking off.
1891 $record = new stdClass();
1892 $record->course = $course->id;
1893 $record->type = 'news';
1894 $forum = self::getDataGenerator()->create_module('forum', $record);
1895
1896 $record = new stdClass();
1897 $record->course = $course->id;
1898 $record->userid = $user->id;
1899 $record->forum = $forum->id;
1900 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1901
1902 // User who is a student.
1903 self::setUser($user);
1904 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
1905
1906 // Only a teacher should be able to lock a discussion.
bdb4a87d
P
1907 try {
1908 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, 0);
1909 $this->fail('Exception expected due to missing capability.');
1910 } catch (moodle_exception $e) {
1911 $this->assertEquals('errorcannotlock', $e->errorcode);
1912 }
f5b4320e
P
1913
1914 // Set the lock.
1915 self::setAdminUser();
1916 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, 0);
1917 $result = external_api::clean_returnvalue(mod_forum_external::set_lock_state_returns(), $result);
bdb4a87d 1918 $this->assertTrue($result['locked']);
f5b4320e
P
1919 $this->assertNotEquals(0, $result['times']['locked']);
1920
1921 // Unset the lock.
1922 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, time());
1923 $result = external_api::clean_returnvalue(mod_forum_external::set_lock_state_returns(), $result);
bdb4a87d 1924 $this->assertFalse($result['locked']);
f5b4320e
P
1925 $this->assertEquals('0', $result['times']['locked']);
1926 }
1927
04cd8ae3
JL
1928 /*
1929 * Test can_add_discussion. A basic test since all the API functions are already covered by unit tests.
1930 */
1931 public function test_can_add_discussion() {
581e75bf 1932 global $DB;
04cd8ae3
JL
1933 $this->resetAfterTest(true);
1934
1935 // Create courses to add the modules.
1936 $course = self::getDataGenerator()->create_course();
1937
1938 $user = self::getDataGenerator()->create_user();
1939
1940 // First forum with tracking off.
1941 $record = new stdClass();
1942 $record->course = $course->id;
1943 $record->type = 'news';
1944 $forum = self::getDataGenerator()->create_module('forum', $record);
1945
1946 // User with no permissions to add in a news forum.
1947 self::setUser($user);
1948 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1949
1950 $result = mod_forum_external::can_add_discussion($forum->id);
1951 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1952 $this->assertFalse($result['status']);
581e75bf
JL
1953 $this->assertFalse($result['canpindiscussions']);
1954 $this->assertTrue($result['cancreateattachment']);
1955
1956 // Disable attachments.
1957 $DB->set_field('forum', 'maxattachments', 0, array('id' => $forum->id));
1958 $result = mod_forum_external::can_add_discussion($forum->id);
1959 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1960 $this->assertFalse($result['status']);
1961 $this->assertFalse($result['canpindiscussions']);
1962 $this->assertFalse($result['cancreateattachment']);
1963 $DB->set_field('forum', 'maxattachments', 1, array('id' => $forum->id)); // Enable attachments again.
04cd8ae3
JL
1964
1965 self::setAdminUser();
1966 $result = mod_forum_external::can_add_discussion($forum->id);
1967 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1968 $this->assertTrue($result['status']);
581e75bf
JL
1969 $this->assertTrue($result['canpindiscussions']);
1970 $this->assertTrue($result['cancreateattachment']);
cbf63d8e
SR
1971 }
1972
1973 /*
1974 * A basic test to make sure users cannot post to forum after the cutoff date.
1975 */
1976 public function test_can_add_discussion_after_cutoff() {
1977 $this->resetAfterTest(true);
1978
1979 // Create courses to add the modules.
1980 $course = self::getDataGenerator()->create_course();
1981
1982 $user = self::getDataGenerator()->create_user();
1983
1984 // Create a forum with cutoff date set to a past date.
1985 $forum = self::getDataGenerator()->create_module('forum', ['course' => $course->id, 'cutoffdate' => time() - 1]);
04cd8ae3 1986
cbf63d8e
SR
1987 // User with no mod/forum:canoverridecutoff capability.
1988 self::setUser($user);
1989 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1990
1991 $result = mod_forum_external::can_add_discussion($forum->id);
1992 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1993 $this->assertFalse($result['status']);
1994
1995 self::setAdminUser();
1996 $result = mod_forum_external::can_add_discussion($forum->id);
1997 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1998 $this->assertTrue($result['status']);
04cd8ae3
JL
1999 }
2000
b7ce46df
JL
2001 /**
2002 * Test get forum posts discussions including rating information.
2003 */
2004 public function test_mod_forum_get_forum_discussion_rating_information() {
2005 global $DB, $CFG;
2006 require_once($CFG->dirroot . '/rating/lib.php');
2007
2008 $this->resetAfterTest(true);
2009
2010 $user1 = self::getDataGenerator()->create_user();
2011 $user2 = self::getDataGenerator()->create_user();
2012 $user3 = self::getDataGenerator()->create_user();
2013 $teacher = self::getDataGenerator()->create_user();
2014
2015 // Create course to add the module.
2016 $course = self::getDataGenerator()->create_course();
2017
2018 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2019 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2020 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $studentrole->id, 'manual');
2021 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $studentrole->id, 'manual');
2022 $this->getDataGenerator()->enrol_user($user3->id, $course->id, $studentrole->id, 'manual');
2023 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
2024
2025 // Create the forum.
2026 $record = new stdClass();
2027 $record->course = $course->id;
2028 // Set Aggregate type = Average of ratings.
2029 $record->assessed = RATING_AGGREGATE_AVERAGE;
2030 $record->scale = 100;
2031 $forum = self::getDataGenerator()->create_module('forum', $record);
2032 $context = context_module::instance($forum->cmid);
2033
2034 // Add discussion to the forum.
2035 $record = new stdClass();
2036 $record->course = $course->id;
2037 $record->userid = $user1->id;
2038 $record->forum = $forum->id;
2039 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2040
2041 // Retrieve the first post.
2042 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
2043
2044 // Rate the discussion as user2.
2045 $rating1 = new stdClass();
2046 $rating1->contextid = $context->id;
2047 $rating1->component = 'mod_forum';
2048 $rating1->ratingarea = 'post';
2049 $rating1->itemid = $post->id;
2050 $rating1->rating = 50;
2051 $rating1->scaleid = 100;
2052 $rating1->userid = $user2->id;
2053 $rating1->timecreated = time();
2054 $rating1->timemodified = time();
2055 $rating1->id = $DB->insert_record('rating', $rating1);
2056
2057 // Rate the discussion as user3.
2058 $rating2 = new stdClass();
2059 $rating2->contextid = $context->id;
2060 $rating2->component = 'mod_forum';
2061 $rating2->ratingarea = 'post';
2062 $rating2->itemid = $post->id;
2063 $rating2->rating = 100;
2064 $rating2->scaleid = 100;
2065 $rating2->userid = $user3->id;
2066 $rating2->timecreated = time() + 1;
2067 $rating2->timemodified = time() + 1;
2068 $rating2->id = $DB->insert_record('rating', $rating2);
2069
2070 // Retrieve the rating for the post as student.
2071 $this->setUser($user1);
2072 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2073 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2074 $this->assertCount(1, $posts['ratinginfo']['ratings']);
2075 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
2076 $this->assertFalse($posts['ratinginfo']['canviewall']);
2077 $this->assertFalse($posts['ratinginfo']['ratings'][0]['canrate']);
2078 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
2079 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
2080
2081 // Retrieve the rating for the post as teacher.
2082 $this->setUser($teacher);
2083 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2084 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2085 $this->assertCount(1, $posts['ratinginfo']['ratings']);
2086 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
2087 $this->assertTrue($posts['ratinginfo']['canviewall']);
2088 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canrate']);
2089 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
2090 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
2091 }
4daa0d08
JL
2092
2093 /**
2094 * Test mod_forum_get_forum_access_information.
2095 */
2096 public function test_mod_forum_get_forum_access_information() {
2097 global $DB;
2098
2099 $this->resetAfterTest(true);
2100
2101 $student = self::getDataGenerator()->create_user();
2102 $course = self::getDataGenerator()->create_course();
2103 // Create the forum.
2104 $record = new stdClass();
2105 $record->course = $course->id;
2106 $forum = self::getDataGenerator()->create_module('forum', $record);
2107
2108 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2109 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
2110
2111 self::setUser($student);
2112 $result = mod_forum_external::get_forum_access_information($forum->id);
2113 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
2114
2115 // Check default values for capabilities.
2116 $enabledcaps = array('canviewdiscussion', 'canstartdiscussion', 'canreplypost', 'canviewrating', 'cancreateattachment',
13cd05ac 2117 'canexportownpost', 'cancantogglefavourite', 'candeleteownpost', 'canallowforcesubscribe');
4daa0d08
JL
2118
2119 unset($result['warnings']);
2120 foreach ($result as $capname => $capvalue) {
2121 if (in_array($capname, $enabledcaps)) {
2122 $this->assertTrue($capvalue);
2123 } else {
2124 $this->assertFalse($capvalue);
2125 }
2126 }
2127 // Now, unassign some capabilities.
2128 unassign_capability('mod/forum:deleteownpost', $studentrole->id);
2129 unassign_capability('mod/forum:allowforcesubscribe', $studentrole->id);
2130 array_pop($enabledcaps);
2131 array_pop($enabledcaps);
2132 accesslib_clear_all_caches_for_unit_testing();
2133
2134 $result = mod_forum_external::get_forum_access_information($forum->id);
2135 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
2136 unset($result['warnings']);
2137 foreach ($result as $capname => $capvalue) {
2138 if (in_array($capname, $enabledcaps)) {
2139 $this->assertTrue($capvalue);
2140 } else {
2141 $this->assertFalse($capvalue);
2142 }
2143 }
2144 }
bc4c7337
AN
2145
2146 /**
2147 * Test add_discussion_post
2148 */
2149 public function test_add_discussion_post_private() {
2150 global $DB;
2151
2152 $this->resetAfterTest(true);
2153
2154 self::setAdminUser();
2155
2156 // Create course to add the module.
2157 $course = self::getDataGenerator()->create_course();
2158
2159 // Standard forum.
2160 $record = new stdClass();
2161 $record->course = $course->id;
2162 $forum = self::getDataGenerator()->create_module('forum', $record);
2163 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
2164 $forumcontext = context_module::instance($forum->cmid);
2165 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
2166
2167 // Create an enrol users.
2168 $student1 = self::getDataGenerator()->create_user();
2169 $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
2170 $student2 = self::getDataGenerator()->create_user();
2171 $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
2172 $teacher1 = self::getDataGenerator()->create_user();
2173 $this->getDataGenerator()->enrol_user($teacher1->id, $course->id, 'editingteacher');
2174 $teacher2 = self::getDataGenerator()->create_user();
2175 $this->getDataGenerator()->enrol_user($teacher2->id, $course->id, 'editingteacher');
2176
2177 // Add a new discussion to the forum.
2178 self::setUser($student1);
2179 $record = new stdClass();
2180 $record->course = $course->id;
2181 $record->userid = $student1->id;
2182 $record->forum = $forum->id;
2183 $discussion = $generator->create_discussion($record);
2184
2185 // Have the teacher reply privately.
2186 self::setUser($teacher1);
2187 $post = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...', [
2188 [
2189 'name' => 'private',
2190 'value' => true,
2191 ],
2192 ]);
2193 $post = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $post);
2194 $privatereply = $DB->get_record('forum_posts', array('id' => $post['postid']));
2195 $this->assertEquals($student1->id, $privatereply->privatereplyto);
2196 // Bump the time of the private reply to ensure order.
2197 $privatereply->created++;
2198 $privatereply->modified = $privatereply->created;
2199 $DB->update_record('forum_posts', $privatereply);
2200
2201 // The teacher will receive their private reply.
2202 self::setUser($teacher1);
2203 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2204 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2205 $this->assertEquals(2, count($posts['posts']));
2206 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2207
2208 // Another teacher on the course will also receive the private reply.
2209 self::setUser($teacher2);
2210 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2211 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2212 $this->assertEquals(2, count($posts['posts']));
2213 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2214
2215 // The student will receive the private reply.
2216 self::setUser($student1);
2217 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2218 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2219 $this->assertEquals(2, count($posts['posts']));
2220 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2221
2222 // Another student will not receive the private reply.
2223 self::setUser($student2);
2224 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2225 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2226 $this->assertEquals(1, count($posts['posts']));
2227 $this->assertFalse($posts['posts'][0]['isprivatereply']);
2228
2229 // A user cannot reply to a private reply.
2230 self::setUser($teacher2);
2231 $this->expectException('coding_exception');
2232 $post = mod_forum_external::add_discussion_post($privatereply->id, 'some subject', 'some text here...', [
2233 'options' => [
2234 'name' => 'private',
2235 'value' => false,
2236 ],
2237 ]);
2238 }
2b9fe87d 2239}