Merge branch 'MDL-65017-master' of git://github.com/jleyva/moodle
[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),
23e0ceca 551 'isdeleted' => false,
8245daba
P
552 'groups' => [],
553 'urls' => [
554 'profile' => $urlfactory->get_author_profile_url($user2entity),
555 'profileimage' => $urlfactory->get_author_profile_image_url($user2entity),
556 ]
557 ];
558 $user2->fullname = $exporteduser2['fullname'];
559
560 $user3 = self::getDataGenerator()->create_user(['fullname' => "Mr Pants 1"]);
561 $user3entity = $entityfactory->get_author_from_stdclass($user3);
562 $exporteduser3 = [
563 'id' => (int) $user3->id,
564 'fullname' => fullname($user3),
565 'groups' => [],
23e0ceca 566 'isdeleted' => false,
8245daba
P
567 'urls' => [
568 'profile' => $urlfactory->get_author_profile_url($user3entity),
569 'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
570 ]
571 ];
572 $user3->fullname = $exporteduser3['fullname'];
573 $forumgenerator = self::getDataGenerator()->get_plugin_generator('mod_forum');
574
575 // Set the first created user to the test user.
576 self::setUser($user1);
577
578 // Create course to add the module.
579 $course1 = self::getDataGenerator()->create_course();
580
581 // Forum with tracking off.
582 $record = new stdClass();
583 $record->course = $course1->id;
584 $record->trackingtype = FORUM_TRACKING_OFF;
10cc5e81
JP
585 // Display word count. Otherwise, word and char counts will be set to null by the forum post exporter.
586 $record->displaywordcount = true;
8245daba
P
587 $forum1 = self::getDataGenerator()->create_module('forum', $record);
588 $forum1context = context_module::instance($forum1->cmid);
589
590 // Forum with tracking enabled.
591 $record = new stdClass();
592 $record->course = $course1->id;
593 $forum2 = self::getDataGenerator()->create_module('forum', $record);
594 $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
595 $forum2context = context_module::instance($forum2->cmid);
596
597 // Add discussions to the forums.
598 $record = new stdClass();
599 $record->course = $course1->id;
600 $record->userid = $user1->id;
601 $record->forum = $forum1->id;
602 $discussion1 = $forumgenerator->create_discussion($record);
603
604 $record = new stdClass();
605 $record->course = $course1->id;
606 $record->userid = $user2->id;
607 $record->forum = $forum1->id;
608 $discussion2 = $forumgenerator->create_discussion($record);
609
610 $record = new stdClass();
611 $record->course = $course1->id;
612 $record->userid = $user2->id;
613 $record->forum = $forum2->id;
614 $discussion3 = $forumgenerator->create_discussion($record);
615
616 // Add 2 replies to the discussion 1 from different users.
617 $record = new stdClass();
618 $record->discussion = $discussion1->id;
619 $record->parent = $discussion1->firstpost;
620 $record->userid = $user2->id;
621 $discussion1reply1 = $forumgenerator->create_post($record);
622 $filename = 'shouldbeanimage.jpg';
623 // Add a fake inline image to the post.
624 $filerecordinline = array(
625 'contextid' => $forum1context->id,
626 'component' => 'mod_forum',
627 'filearea' => 'post',
628 'itemid' => $discussion1reply1->id,
629 'filepath' => '/',
630 'filename' => $filename,
631 );
632 $fs = get_file_storage();
633 $timepost = time();
634 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
635
636 $record->parent = $discussion1reply1->id;
637 $record->userid = $user3->id;
638 $discussion1reply2 = $forumgenerator->create_post($record);
639
640 // Enrol the user in the course.
641 $enrol = enrol_get_plugin('manual');
642 // Following line enrol and assign default role id to the user.
643 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
644 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
645 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
646
647 // Delete one user, to test that we still receive posts by this user.
648 delete_user($user3);
23e0ceca
RW
649 $exporteduser3 = [
650 'id' => (int) $user3->id,
651 'fullname' => get_string('deleteduser', 'mod_forum'),
652 'groups' => [],
653 'isdeleted' => true,
654 'urls' => [
655 'profile' => $urlfactory->get_author_profile_url($user3entity),
656 'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
657 ]
658 ];
8245daba
P
659
660 // Create what we expect to be returned when querying the discussion.
661 $expectedposts = array(
662 'posts' => array(),
663 'ratinginfo' => array(
664 'contextid' => $forum1context->id,
665 'component' => 'mod_forum',
666 'ratingarea' => 'post',
667 'canviewall' => null,
668 'canviewany' => null,
669 'scales' => array(),
670 'ratings' => array(),
671 ),
672 'warnings' => array(),
673 );
674
675 // User pictures are initially empty, we should get the links once the external function is called.
676 $isolatedurl = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply2->discussion);
677 $isolatedurl->params(['parent' => $discussion1reply2->id]);
10cc5e81
JP
678 $message = file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
679 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id);
8245daba
P
680 $expectedposts['posts'][] = array(
681 'id' => $discussion1reply2->id,
682 'discussionid' => $discussion1reply2->discussion,
683 'parentid' => $discussion1reply2->parent,
684 'hasparent' => true,
685 'timecreated' => $discussion1reply2->created,
686 'subject' => $discussion1reply2->subject,
9ba09a12 687 'replysubject' => get_string('re', 'mod_forum') . " {$discussion1reply2->subject}",
10cc5e81 688 'message' => $message,
8245daba
P
689 'messageformat' => 1, // This value is usually changed by external_format_text() function.
690 'unread' => null,
691 'isdeleted' => false,
692 'isprivatereply' => false,
10cc5e81
JP
693 'haswordcount' => true,
694 'wordcount' => count_words($message),
695 'charcount' => count_letters($message),
8245daba
P
696 'author'=> $exporteduser3,
697 'attachments' => [],
698 'tags' => [],
699 'html' => [
700 'rating' => null,
701 'taglist' => null,
702 'authorsubheading' => $forumgenerator->get_author_subheading_html((object)$exporteduser3, $discussion1reply2->created)
703 ],
704 'capabilities' => [
705 'view' => 1,
706 'edit' => 0,
707 'delete' => 0,
708 'split' => 0,
709 'reply' => 1,
710 'export' => 0,
cee572aa 711 'controlreadstatus' => 0,
92ba55ee
P
712 'canreplyprivately' => 0,
713 'selfenrol' => 0
8245daba
P
714 ],
715 'urls' => [
716 'view' => $urlfactory->get_view_post_url_from_post_id($discussion1reply2->discussion, $discussion1reply2->id),
717 'viewisolated' => $isolatedurl->out(false),
718 'viewparent' => $urlfactory->get_view_post_url_from_post_id($discussion1reply2->discussion, $discussion1reply2->parent),
719 'edit' => null,
720 'delete' =>null,
721 'split' => null,
722 'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
723 'reply' => $discussion1reply2->id
724 ]))->out(false),
725 'export' => null,
726 'markasread' => null,
727 'markasunread' => null,
728 'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply2->discussion),
729 ],
730 );
731
732
733 $isolatedurl = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply1->discussion);
734 $isolatedurl->params(['parent' => $discussion1reply1->id]);
10cc5e81
JP
735 $message = file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
736 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id);
8245daba
P
737 $expectedposts['posts'][] = array(
738 'id' => $discussion1reply1->id,
739 'discussionid' => $discussion1reply1->discussion,
740 'parentid' => $discussion1reply1->parent,
741 'hasparent' => true,
742 'timecreated' => $discussion1reply1->created,
743 'subject' => $discussion1reply1->subject,
9ba09a12 744 'replysubject' => get_string('re', 'mod_forum') . " {$discussion1reply1->subject}",
10cc5e81 745 'message' => $message,
8245daba
P
746 'messageformat' => 1, // This value is usually changed by external_format_text() function.
747 'unread' => null,
748 'isdeleted' => false,
749 'isprivatereply' => false,
10cc5e81
JP
750 'haswordcount' => true,
751 'wordcount' => count_words($message),
752 'charcount' => count_letters($message),
8245daba
P
753 'author'=> $exporteduser2,
754 'attachments' => [],
755 'tags' => [],
756 'html' => [
757 'rating' => null,
758 'taglist' => null,
759 'authorsubheading' => $forumgenerator->get_author_subheading_html((object)$exporteduser2, $discussion1reply1->created)
760 ],
761 'capabilities' => [
762 'view' => 1,
763 'edit' => 0,
764 'delete' => 0,
765 'split' => 0,
766 'reply' => 1,
767 'export' => 0,
cee572aa 768 'controlreadstatus' => 0,
92ba55ee
P
769 'canreplyprivately' => 0,
770 'selfenrol' => 0
8245daba
P
771 ],
772 'urls' => [
773 'view' => $urlfactory->get_view_post_url_from_post_id($discussion1reply1->discussion, $discussion1reply1->id),
774 'viewisolated' => $isolatedurl->out(false),
775 'viewparent' => $urlfactory->get_view_post_url_from_post_id($discussion1reply1->discussion, $discussion1reply1->parent),
776 'edit' => null,
777 'delete' =>null,
778 'split' => null,
779 'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
780 'reply' => $discussion1reply1->id
781 ]))->out(false),
782 'export' => null,
783 'markasread' => null,
784 'markasunread' => null,
785 'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply1->discussion),
786 ],
787 );
788
789 // Test a discussion with two additional posts (total 3 posts).
790 $posts = mod_forum_external::get_discussion_posts($discussion1->id, 'modified', 'DESC');
791 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
792 $this->assertEquals(3, count($posts['posts']));
793
794 // Unset the initial discussion post.
795 array_pop($posts['posts']);
796 $this->assertEquals($expectedposts, $posts);
797
798 // Check we receive the unread count correctly on tracked forum.
799 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
800 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
801 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
802 foreach ($result as $f) {
803 if ($f['id'] == $forum2->id) {
804 $this->assertEquals(1, $f['unreadpostscount']);
805 }
806 }
807
808 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
809 $posts = mod_forum_external::get_discussion_posts($discussion2->id, 'modified', 'DESC');
810 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
811 $this->assertEquals(1, count($posts['posts']));
812
813 // Test discussion tracking on not tracked forum.
814 $result = mod_forum_external::view_forum_discussion($discussion1->id);
815 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
816 $this->assertTrue($result['status']);
817 $this->assertEmpty($result['warnings']);
818
819 // Test posts have not been marked as read.
820 $posts = mod_forum_external::get_discussion_posts($discussion1->id, 'modified', 'DESC');
821 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
822 foreach ($posts['posts'] as $post) {
823 $this->assertNull($post['unread']);
824 }
825
826 // Test discussion tracking on tracked forum.
827 $result = mod_forum_external::view_forum_discussion($discussion3->id);
828 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
829 $this->assertTrue($result['status']);
830 $this->assertEmpty($result['warnings']);
831
832 // Test posts have been marked as read.
833 $posts = mod_forum_external::get_discussion_posts($discussion3->id, 'modified', 'DESC');
834 $posts = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_returns(), $posts);
835 foreach ($posts['posts'] as $post) {
836 $this->assertFalse($post['unread']);
837 }
838
839 // Check we receive 0 unread posts.
840 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
841 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
842 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
843 foreach ($result as $f) {
844 if ($f['id'] == $forum2->id) {
845 $this->assertEquals(0, $f['unreadpostscount']);
846 }
847 }
848 }
849
3e95e09b
AN
850 /**
851 * Test get forum posts
852 */
853 public function test_mod_forum_get_forum_discussion_posts_deleted() {
854 global $CFG, $PAGE;
855
856 $this->resetAfterTest(true);
857 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
858
859 // Create a course and enrol some users in it.
860 $course1 = self::getDataGenerator()->create_course();
861
862 // Create users.
863 $user1 = self::getDataGenerator()->create_user();
864 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
865 $user2 = self::getDataGenerator()->create_user();
866 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
867
868 // Set the first created user to the test user.
869 self::setUser($user1);
870
871 // Create test data.
872 $forum1 = self::getDataGenerator()->create_module('forum', (object) [
873 'course' => $course1->id,
874 ]);
875 $forum1context = context_module::instance($forum1->cmid);
876
877 // Add discussions to the forum.
878 $discussion = $generator->create_discussion((object) [
879 'course' => $course1->id,
880 'userid' => $user1->id,
881 'forum' => $forum1->id,
882 ]);
883
884 $discussion2 = $generator->create_discussion((object) [
885 'course' => $course1->id,
886 'userid' => $user2->id,
887 'forum' => $forum1->id,
888 ]);
889
890 // Add replies to the discussion.
891 $discussionreply1 = $generator->create_post((object) [
892 'discussion' => $discussion->id,
893 'parent' => $discussion->firstpost,
894 'userid' => $user2->id,
895 ]);
896 $discussionreply2 = $generator->create_post((object) [
897 'discussion' => $discussion->id,
898 'parent' => $discussionreply1->id,
899 'userid' => $user2->id,
900 'subject' => '',
901 'message' => '',
902 'messageformat' => FORMAT_PLAIN,
903 'deleted' => 1,
904 ]);
905 $discussionreply3 = $generator->create_post((object) [
906 'discussion' => $discussion->id,
907 'parent' => $discussion->firstpost,
908 'userid' => $user2->id,
909 ]);
910
911 // Test where some posts have been marked as deleted.
912 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id, 'modified', 'DESC');
913 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
914 $deletedsubject = get_string('privacy:request:delete:post:subject', 'mod_forum');
915 $deletedmessage = get_string('privacy:request:delete:post:message', 'mod_forum');
916
917 foreach ($posts['posts'] as $post) {
918 if ($post['id'] == $discussionreply2->id) {
919 $this->assertTrue($post['deleted']);
920 $this->assertEquals($deletedsubject, $post['subject']);
921 $this->assertEquals($deletedmessage, $post['message']);
922 } else {
923 $this->assertFalse($post['deleted']);
924 $this->assertNotEquals($deletedsubject, $post['subject']);
925 $this->assertNotEquals($deletedmessage, $post['message']);
926 }
927 }
928 }
929
b1aa7dfa
JL
930 /**
931 * Test get forum posts (qanda forum)
932 */
933 public function test_mod_forum_get_forum_discussion_posts_qanda() {
934 global $CFG, $DB;
935
936 $this->resetAfterTest(true);
937
938 $record = new stdClass();
939 $user1 = self::getDataGenerator()->create_user($record);
940 $user2 = self::getDataGenerator()->create_user();
941
942 // Set the first created user to the test user.
943 self::setUser($user1);
944
945 // Create course to add the module.
946 $course1 = self::getDataGenerator()->create_course();
947 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
948 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
949
950 // Forum with tracking off.
951 $record = new stdClass();
952 $record->course = $course1->id;
953 $record->type = 'qanda';
954 $forum1 = self::getDataGenerator()->create_module('forum', $record);
955 $forum1context = context_module::instance($forum1->cmid);
956
957 // Add discussions to the forums.
958 $record = new stdClass();
959 $record->course = $course1->id;
960 $record->userid = $user2->id;
961 $record->forum = $forum1->id;
962 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
963
964 // Add 1 reply (not the actual user).
965 $record = new stdClass();
966 $record->discussion = $discussion1->id;
967 $record->parent = $discussion1->firstpost;
968 $record->userid = $user2->id;
969 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
970
971 // We still see only the original post.
972 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
973 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
974 $this->assertEquals(1, count($posts['posts']));
975
976 // Add a new reply, the user is going to be able to see only the original post and their new post.
977 $record = new stdClass();
978 $record->discussion = $discussion1->id;
979 $record->parent = $discussion1->firstpost;
980 $record->userid = $user1->id;
981 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
982
983 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
984 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
985 $this->assertEquals(2, count($posts['posts']));
986
987 // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
988 $discussion1reply2->created -= $CFG->maxeditingtime * 2;
989 $DB->update_record('forum_posts', $discussion1reply2);
990
991 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
992 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
993 $this->assertEquals(3, count($posts['posts']));
b1aa7dfa
JL
994 }
995
c2586672
JL
996 /**
997 * Test get forum discussions paginated
998 */
999 public function test_mod_forum_get_forum_discussions_paginated() {
d85bedf7 1000 global $USER, $CFG, $DB, $PAGE;
c2586672
JL
1001
1002 $this->resetAfterTest(true);
1003
1004 // Set the CFG variable to allow track forums.
1005 $CFG->forum_trackreadposts = true;
1006
1007 // Create a user who can track forums.
1008 $record = new stdClass();
1009 $record->trackforums = true;
1010 $user1 = self::getDataGenerator()->create_user($record);
1011 // Create a bunch of other users to post.
1012 $user2 = self::getDataGenerator()->create_user();
1013 $user3 = self::getDataGenerator()->create_user();
1014 $user4 = self::getDataGenerator()->create_user();
1015
1016 // Set the first created user to the test user.
1017 self::setUser($user1);
1018
1019 // Create courses to add the modules.
1020 $course1 = self::getDataGenerator()->create_course();
1021
1022 // First forum with tracking off.
1023 $record = new stdClass();
1024 $record->course = $course1->id;
1025 $record->trackingtype = FORUM_TRACKING_OFF;
1026 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1027
1028 // Add discussions to the forums.
1029 $record = new stdClass();
1030 $record->course = $course1->id;
1031 $record->userid = $user1->id;
1032 $record->forum = $forum1->id;
1033 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1034
1035 // Add three replies to the discussion 1 from different users.
1036 $record = new stdClass();
1037 $record->discussion = $discussion1->id;
1038 $record->parent = $discussion1->firstpost;
1039 $record->userid = $user2->id;
1040 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1041
1042 $record->parent = $discussion1reply1->id;
1043 $record->userid = $user3->id;
1044 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1045
1046 $record->userid = $user4->id;
1047 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1048
1049 // Enrol the user in the first course.
1050 $enrol = enrol_get_plugin('manual');
1051
1052 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1053 $enrolinstances = enrol_get_instances($course1->id, true);
1054 foreach ($enrolinstances as $courseenrolinstance) {
1055 if ($courseenrolinstance->enrol == "manual") {
1056 $instance1 = $courseenrolinstance;
1057 break;
1058 }
1059 }
1060 $enrol->enrol_user($instance1, $user1->id);
1061
81f810dc
JL
1062 // Delete one user.
1063 delete_user($user4);
1064
c2586672
JL
1065 // Assign capabilities to view discussions for forum 1.
1066 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1067 $context = context_module::instance($cm->id);
1068 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1069 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1070
1071 // Create what we expect to be returned when querying the forums.
1072
1073 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
c2586672 1074
d85bedf7 1075 // User pictures are initially empty, we should get the links once the external function is called.
c2586672
JL
1076 $expecteddiscussions = array(
1077 'id' => $discussion1->firstpost,
1078 'name' => $discussion1->name,
28c2f2b2 1079 'groupid' => (int) $discussion1->groupid,
c2586672 1080 'timemodified' => $discussion1reply3->created,
28c2f2b2
MG
1081 'usermodified' => (int) $discussion1reply3->userid,
1082 'timestart' => (int) $discussion1->timestart,
1083 'timeend' => (int) $discussion1->timeend,
c2586672
JL
1084 'discussion' => $discussion1->id,
1085 'parent' => 0,
28c2f2b2
MG
1086 'userid' => (int) $discussion1->userid,
1087 'created' => (int) $post1->created,
1088 'modified' => (int) $post1->modified,
1089 'mailed' => (int) $post1->mailed,
c2586672
JL
1090 'subject' => $post1->subject,
1091 'message' => $post1->message,
28c2f2b2
MG
1092 'messageformat' => (int) $post1->messageformat,
1093 'messagetrust' => (int) $post1->messagetrust,
c2586672 1094 'attachment' => $post1->attachment,
28c2f2b2
MG
1095 'totalscore' => (int) $post1->totalscore,
1096 'mailnow' => (int) $post1->mailnow,
c2586672
JL
1097 'userfullname' => fullname($user1),
1098 'usermodifiedfullname' => fullname($user4),
d85bedf7
JL
1099 'userpictureurl' => '',
1100 'usermodifiedpictureurl' => '',
c2586672 1101 'numreplies' => 3,
5f219cf1 1102 'numunread' => 0,
28c2f2b2 1103 'pinned' => (bool) FORUM_DISCUSSION_UNPINNED,
0f3bbfd4
AN
1104 'locked' => false,
1105 'canreply' => false,
28c2f2b2 1106 'canlock' => false
c2586672
JL
1107 );
1108
1109 // Call the external function passing forum id.
1110 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
1111 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1112 $expectedreturn = array(
1113 'discussions' => array($expecteddiscussions),
1114 'warnings' => array()
1115 );
d85bedf7
JL
1116
1117 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
1118 $userpicture = new user_picture($user1);
1119 $userpicture->size = 1; // Size f1.
1120 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1121
1122 $userpicture = new user_picture($user4);
1123 $userpicture->size = 1; // Size f1.
1124 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1125
c2586672
JL
1126 $this->assertEquals($expectedreturn, $discussions);
1127
1128 // Call without required view discussion capability.
1129 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1130 try {
1131 mod_forum_external::get_forum_discussions_paginated($forum1->id);
1132 $this->fail('Exception expected due to missing capability.');
1133 } catch (moodle_exception $e) {
1134 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
1135 }
1136
1137 // Unenrol user from second course.
1138 $enrol->unenrol_user($instance1, $user1->id);
1139
1140 // Call for the second course we unenrolled the user from, make sure exception thrown.
1141 try {
1142 mod_forum_external::get_forum_discussions_paginated($forum1->id);
1143 $this->fail('Exception expected due to being unenrolled from the course.');
1144 } catch (moodle_exception $e) {
1145 $this->assertEquals('requireloginerror', $e->errorcode);
1146 }
565cccfa
P
1147
1148 $this->setAdminUser();
1149 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
1150 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1151 $this->assertTrue($discussions['discussions'][0]['canlock']);
c2586672 1152 }
039c81f0
JL
1153
1154 /**
1155 * Test get forum discussions paginated (qanda forums)
1156 */
1157 public function test_mod_forum_get_forum_discussions_paginated_qanda() {
1158
1159 $this->resetAfterTest(true);
1160
1161 // Create courses to add the modules.
1162 $course = self::getDataGenerator()->create_course();
1163
1164 $user1 = self::getDataGenerator()->create_user();
1165 $user2 = self::getDataGenerator()->create_user();
1166
1167 // First forum with tracking off.
1168 $record = new stdClass();
1169 $record->course = $course->id;
1170 $record->type = 'qanda';
1171 $forum = self::getDataGenerator()->create_module('forum', $record);
1172
1173 // Add discussions to the forums.
1174 $discussionrecord = new stdClass();
1175 $discussionrecord->course = $course->id;
1176 $discussionrecord->userid = $user2->id;
1177 $discussionrecord->forum = $forum->id;
1178 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
1179
1180 self::setAdminUser();
1181 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1182 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1183
1184 $this->assertCount(1, $discussions['discussions']);
1185 $this->assertCount(0, $discussions['warnings']);
1186
1187 self::setUser($user1);
1188 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1189
1190 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1191 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1192
1193 $this->assertCount(1, $discussions['discussions']);
1194 $this->assertCount(0, $discussions['warnings']);
1195
1196 }
50a20317 1197
3f9ed16e
MG
1198 /**
1199 * Test get forum discussions
1200 */
1201 public function test_mod_forum_get_forum_discussions() {
1202 global $CFG, $DB, $PAGE;
1203
1204 $this->resetAfterTest(true);
1205
1206 // Set the CFG variable to allow track forums.
1207 $CFG->forum_trackreadposts = true;
1208
1209 // Create a user who can track forums.
1210 $record = new stdClass();
1211 $record->trackforums = true;
1212 $user1 = self::getDataGenerator()->create_user($record);
1213 // Create a bunch of other users to post.
1214 $user2 = self::getDataGenerator()->create_user();
1215 $user3 = self::getDataGenerator()->create_user();
1216 $user4 = self::getDataGenerator()->create_user();
1217
1218 // Set the first created user to the test user.
1219 self::setUser($user1);
1220
1221 // Create courses to add the modules.
1222 $course1 = self::getDataGenerator()->create_course();
1223
1224 // First forum with tracking off.
1225 $record = new stdClass();
1226 $record->course = $course1->id;
1227 $record->trackingtype = FORUM_TRACKING_OFF;
1228 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1229
1230 // Add discussions to the forums.
1231 $record = new stdClass();
1232 $record->course = $course1->id;
1233 $record->userid = $user1->id;
1234 $record->forum = $forum1->id;
1235 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1236
1237 // Add three replies to the discussion 1 from different users.
1238 $record = new stdClass();
1239 $record->discussion = $discussion1->id;
1240 $record->parent = $discussion1->firstpost;
1241 $record->userid = $user2->id;
1242 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1243
1244 $record->parent = $discussion1reply1->id;
1245 $record->userid = $user3->id;
1246 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1247
1248 $record->userid = $user4->id;
1249 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1250
1251 // Enrol the user in the first course.
1252 $enrol = enrol_get_plugin('manual');
1253
1254 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1255 $enrolinstances = enrol_get_instances($course1->id, true);
1256 foreach ($enrolinstances as $courseenrolinstance) {
1257 if ($courseenrolinstance->enrol == "manual") {
1258 $instance1 = $courseenrolinstance;
1259 break;
1260 }
1261 }
1262 $enrol->enrol_user($instance1, $user1->id);
1263
1264 // Delete one user.
1265 delete_user($user4);
1266
1267 // Assign capabilities to view discussions for forum 1.
1268 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1269 $context = context_module::instance($cm->id);
1270 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1271 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1272
1273 // Create what we expect to be returned when querying the forums.
1274
1275 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
1276
1277 // User pictures are initially empty, we should get the links once the external function is called.
1278 $expecteddiscussions = array(
1279 'id' => $discussion1->firstpost,
1280 'name' => $discussion1->name,
1281 'groupid' => (int) $discussion1->groupid,
1282 'timemodified' => (int) $discussion1reply3->created,
1283 'usermodified' => (int) $discussion1reply3->userid,
1284 'timestart' => (int) $discussion1->timestart,
1285 'timeend' => (int) $discussion1->timeend,
1286 'discussion' => (int) $discussion1->id,
1287 'parent' => 0,
1288 'userid' => (int) $discussion1->userid,
1289 'created' => (int) $post1->created,
1290 'modified' => (int) $post1->modified,
1291 'mailed' => (int) $post1->mailed,
1292 'subject' => $post1->subject,
1293 'message' => $post1->message,
1294 'messageformat' => (int) $post1->messageformat,
1295 'messagetrust' => (int) $post1->messagetrust,
1296 'attachment' => $post1->attachment,
1297 'totalscore' => (int) $post1->totalscore,
1298 'mailnow' => (int) $post1->mailnow,
1299 'userfullname' => fullname($user1),
1300 'usermodifiedfullname' => fullname($user4),
1301 'userpictureurl' => '',
1302 'usermodifiedpictureurl' => '',
1303 'numreplies' => 3,
1304 'numunread' => 0,
1305 'pinned' => (bool) FORUM_DISCUSSION_UNPINNED,
1306 'locked' => false,
1307 'canreply' => false,
1308 'canlock' => false,
28c2f2b2
MG
1309 'starred' => false,
1310 'canfavourite' => true
3f9ed16e
MG
1311 );
1312
1313 // Call the external function passing forum id.
1314 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1315 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1316 $expectedreturn = array(
1317 'discussions' => array($expecteddiscussions),
1318 'warnings' => array()
1319 );
1320
1321 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
1322 $userpicture = new user_picture($user1);
1323 $userpicture->size = 2; // Size f2.
1324 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1325
1326 $userpicture = new user_picture($user4);
1327 $userpicture->size = 2; // Size f2.
1328 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
1329
1330 $this->assertEquals($expectedreturn, $discussions);
1331
28c2f2b2
MG
1332 // Test the starring functionality return.
1333 $t = mod_forum_external::toggle_favourite_state($discussion1->id, 1);
1334 $expectedreturn['discussions'][0]['starred'] = true;
1335 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1336 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1337 $this->assertEquals($expectedreturn, $discussions);
1338
3f9ed16e
MG
1339 // Call without required view discussion capability.
1340 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1341 try {
1342 mod_forum_external::get_forum_discussions($forum1->id);
1343 $this->fail('Exception expected due to missing capability.');
1344 } catch (moodle_exception $e) {
1345 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
1346 }
1347
1348 // Unenrol user from second course.
1349 $enrol->unenrol_user($instance1, $user1->id);
1350
1351 // Call for the second course we unenrolled the user from, make sure exception thrown.
1352 try {
1353 mod_forum_external::get_forum_discussions($forum1->id);
1354 $this->fail('Exception expected due to being unenrolled from the course.');
1355 } catch (moodle_exception $e) {
1356 $this->assertEquals('requireloginerror', $e->errorcode);
1357 }
1358
1359 $this->setAdminUser();
1360 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1361 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1362 $this->assertTrue($discussions['discussions'][0]['canlock']);
1363 }
1364
1365 /**
1366 * Test the sorting in get forum discussions
1367 */
1368 public function test_mod_forum_get_forum_discussions_sorting() {
1369 global $CFG, $DB, $PAGE;
1370
1371 $this->resetAfterTest(true);
1372
1373 // Set the CFG variable to allow track forums.
1374 $CFG->forum_trackreadposts = true;
1375
1376 // Create a user who can track forums.
1377 $record = new stdClass();
1378 $record->trackforums = true;
1379 $user1 = self::getDataGenerator()->create_user($record);
1380 // Create a bunch of other users to post.
1381 $user2 = self::getDataGenerator()->create_user();
1382 $user3 = self::getDataGenerator()->create_user();
1383 $user4 = self::getDataGenerator()->create_user();
1384
1385 // Set the first created user to the test user.
1386 self::setUser($user1);
1387
1388 // Create courses to add the modules.
1389 $course1 = self::getDataGenerator()->create_course();
1390
1391 // Enrol the user in the first course.
1392 $enrol = enrol_get_plugin('manual');
1393
1394 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
1395 $enrolinstances = enrol_get_instances($course1->id, true);
1396 foreach ($enrolinstances as $courseenrolinstance) {
1397 if ($courseenrolinstance->enrol == "manual") {
1398 $instance1 = $courseenrolinstance;
1399 break;
1400 }
1401 }
1402 $enrol->enrol_user($instance1, $user1->id);
1403
1404 // First forum with tracking off.
1405 $record = new stdClass();
1406 $record->course = $course1->id;
1407 $record->trackingtype = FORUM_TRACKING_OFF;
1408 $forum1 = self::getDataGenerator()->create_module('forum', $record);
1409
1410 // Assign capabilities to view discussions for forum 1.
1411 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
1412 $context = context_module::instance($cm->id);
1413 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
1414 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
1415
1416 // Add discussions to the forums.
1417 $record = new stdClass();
1418 $record->course = $course1->id;
1419 $record->userid = $user1->id;
1420 $record->forum = $forum1->id;
1421 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1422 sleep(1);
1423
1424 // Add three replies to the discussion 1 from different users.
1425 $record = new stdClass();
1426 $record->discussion = $discussion1->id;
1427 $record->parent = $discussion1->firstpost;
1428 $record->userid = $user2->id;
1429 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1430 sleep(1);
1431
1432 $record->parent = $discussion1reply1->id;
1433 $record->userid = $user3->id;
1434 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1435 sleep(1);
1436
1437 $record->userid = $user4->id;
1438 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
1439 sleep(1);
1440
1441 // Create discussion2.
1442 $record2 = new stdClass();
1443 $record2->course = $course1->id;
1444 $record2->userid = $user1->id;
1445 $record2->forum = $forum1->id;
1446 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record2);
1447 sleep(1);
1448
1449 // Add one reply to the discussion 2.
1450 $record2 = new stdClass();
1451 $record2->discussion = $discussion2->id;
1452 $record2->parent = $discussion2->firstpost;
1453 $record2->userid = $user2->id;
1454 $discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record2);
1455 sleep(1);
1456
1457 // Create discussion 3.
1458 $record3 = new stdClass();
1459 $record3->course = $course1->id;
1460 $record3->userid = $user1->id;
1461 $record3->forum = $forum1->id;
1462 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record3);
1463 sleep(1);
1464
1465 // Add two replies to the discussion 3.
1466 $record3 = new stdClass();
1467 $record3->discussion = $discussion3->id;
1468 $record3->parent = $discussion3->firstpost;
1469 $record3->userid = $user2->id;
1470 $discussion3reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record3);
1471 sleep(1);
1472
1473 $record3->parent = $discussion3reply1->id;
1474 $record3->userid = $user3->id;
1475 $discussion3reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record3);
1476
1477 // Call the external function passing forum id.
1478 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1479 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1480 // Discussions should be ordered by last post date in descending order by default.
1481 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id);
1482 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1483 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1484
1485 $vaultfactory = \mod_forum\local\container::get_vault_factory();
1486 $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault();
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_LASTPOST_ASC);
1490 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1491 // Discussions should be ordered by last post date in ascending order.
1492 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1493 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1494 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->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_CREATED_DESC);
1498 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1499 // Discussions should be ordered by discussion creation date in descending order.
1500 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id);
1501 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1502 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1503
1504 // Call the external function passing forum id and sort order parameter.
1505 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_CREATED_ASC);
1506 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1507 // Discussions should be ordered by discussion creation date in ascending order.
1508 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1509 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id);
1510 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id);
1511
1512 // Call the external function passing forum id and sort order parameter.
1513 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_DESC);
1514 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1515 // Discussions should be ordered by the number of replies in descending order.
1516 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id);
1517 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1518 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion2->id);
1519
1520 // Call the external function passing forum id and sort order parameter.
1521 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_ASC);
1522 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1523 // Discussions should be ordered by the number of replies in ascending order.
1524 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1525 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1526 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1527
1528 // Pin discussion2.
1529 $DB->update_record('forum_discussions',
1530 (object) array('id' => $discussion2->id, 'pinned' => FORUM_DISCUSSION_PINNED));
1531
1532 // Call the external function passing forum id.
1533 $discussions = mod_forum_external::get_forum_discussions($forum1->id);
1534 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1535 // Discussions should be ordered by last post date in descending order by default.
1536 // Pinned discussions should be at the top of the list.
1537 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1538 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id);
1539 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id);
1540
1541 // Call the external function passing forum id and sort order parameter.
1542 $discussions = mod_forum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_LASTPOST_ASC);
1543 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
1544 // Discussions should be ordered by last post date in ascending order.
1545 // Pinned discussions should be at the top of the list.
1546 $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id);
1547 $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion1->id);
1548 $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id);
1549 }
1550
50a20317
JL
1551 /**
1552 * Test add_discussion_post
1553 */
1554 public function test_add_discussion_post() {
e881c4f5 1555 global $CFG;
50a20317
JL
1556
1557 $this->resetAfterTest(true);
1558
1559 $user = self::getDataGenerator()->create_user();
1560 $otheruser = self::getDataGenerator()->create_user();
1561
1562 self::setAdminUser();
1563
1564 // Create course to add the module.
1565 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1566
1567 // Forum with tracking off.
1568 $record = new stdClass();
1569 $record->course = $course->id;
1570 $forum = self::getDataGenerator()->create_module('forum', $record);
1571 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
1572 $forumcontext = context_module::instance($forum->cmid);
1573
1574 // Add discussions to the forums.
1575 $record = new stdClass();
1576 $record->course = $course->id;
1577 $record->userid = $user->id;
1578 $record->forum = $forum->id;
1579 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1580
1581 // Try to post (user not enrolled).
1582 self::setUser($user);
1583 try {
1584 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1585 $this->fail('Exception expected due to being unenrolled from the course.');
1586 } catch (moodle_exception $e) {
1587 $this->assertEquals('requireloginerror', $e->errorcode);
1588 }
1589
1590 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1591 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
1592
41182118
BK
1593 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1594 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
50a20317
JL
1595
1596 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1597 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1598 // We receive the discussion and the post.
1599 $this->assertEquals(2, count($posts['posts']));
43ff833d
JL
1600
1601 $tested = false;
41182118
BK
1602 foreach ($posts['posts'] as $thispost) {
1603 if ($createdpost['postid'] == $thispost['id']) {
1604 $this->assertEquals('some subject', $thispost['subject']);
1605 $this->assertEquals('some text here...', $thispost['message']);
f2eb04c1 1606 $this->assertEquals(FORMAT_HTML, $thispost['messageformat']); // This is the default if format was not specified.
43ff833d
JL
1607 $tested = true;
1608 }
1609 }
1610 $this->assertTrue($tested);
50a20317 1611
f2eb04c1
EL
1612 // Let's simulate a call with any other format, it should be stored that way.
1613 global $DB; // Yes, we are going to use DB facilities too, because cannot rely on other functions for checking
1614 // the format. They eat it completely (going back to FORMAT_HTML. So we only can trust DB for further
1615 // processing.
1616 $formats = [FORMAT_PLAIN, FORMAT_MOODLE, FORMAT_MARKDOWN, FORMAT_HTML];
1617 $options = [];
1618 foreach ($formats as $format) {
1619 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost,
1620 'with some format', 'some formatted here...', $options, $format);
1621 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
1622 $dbformat = $DB->get_field('forum_posts', 'messageformat', ['id' => $createdpost['postid']]);
1623 $this->assertEquals($format, $dbformat);
1624 }
1625
117e4bd4
EL
1626 // Now let's try the 'topreferredformat' option. That should end with the content
1627 // transformed and the format being FORMAT_HTML (when, like in this case, user preferred
1628 // format is HTML, inferred from editor in preferences).
1629 $options = [['name' => 'topreferredformat', 'value' => true]];
f2eb04c1
EL
1630 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost,
1631 'interesting subject', 'with some https://example.com link', $options, FORMAT_MOODLE);
1632 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
1633 $dbpost = $DB->get_record('forum_posts', ['id' => $createdpost['postid']]);
1634 // Format HTML and content converted, we should get.
1635 $this->assertEquals(FORMAT_HTML, $dbpost->messageformat);
1636 $this->assertEquals('<div class="text_to_html">with some https://example.com link</div>', $dbpost->message);
1637
e881c4f5 1638 // Test inline and regular attachment in post
41182118
BK
1639 // Create a file in a draft area for inline attachments.
1640 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5 1641 $draftidattach = file_get_unused_draft_itemid();
41182118
BK
1642 self::setUser($user);
1643 $usercontext = context_user::instance($user->id);
1644 $filepath = '/';
1645 $filearea = 'draft';
1646 $component = 'user';
1647 $filenameimg = 'shouldbeanimage.txt';
e881c4f5 1648 $filerecordinline = array(
41182118
BK
1649 'contextid' => $usercontext->id,
1650 'component' => $component,
1651 'filearea' => $filearea,
1652 'itemid' => $draftidinlineattach,
1653 'filepath' => $filepath,
1654 'filename' => $filenameimg,
1655 );
1656 $fs = get_file_storage();
41182118 1657
e881c4f5
BK
1658 // Create a file in a draft area for regular attachments.
1659 $filerecordattach = $filerecordinline;
1660 $attachfilename = 'attachment.txt';
1661 $filerecordattach['filename'] = $attachfilename;
1662 $filerecordattach['itemid'] = $draftidattach;
1663 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1664 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1665
48143990 1666 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 1667 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
1668 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot
1669 . "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}"
1670 . '" alt="inlineimage">.';
1671 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'new post inline attachment',
1672 $dummytext, $options);
1673 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
1674
1675 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1676 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1677 // We receive the discussion and the post.
1678 // Can't guarantee order of posts during tests.
1679 $postfound = false;
1680 foreach ($posts['posts'] as $thispost) {
1681 if ($createdpost['postid'] == $thispost['id']) {
1682 $this->assertEquals($createdpost['postid'], $thispost['id']);
e881c4f5
BK
1683 $this->assertEquals($thispost['attachment'], 1, "There should be a non-inline attachment");
1684 $this->assertCount(1, $thispost['attachments'], "There should be 1 attachment");
1685 $this->assertEquals($thispost['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
1686 $this->assertContains('pluginfile.php', $thispost['message']);
1687 $postfound = true;
e881c4f5 1688 break;
41182118
BK
1689 }
1690 }
1691
1692 $this->assertTrue($postfound);
1693
50a20317
JL
1694 // Check not posting in groups the user is not member of.
1695 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1696 groups_add_member($group->id, $otheruser->id);
1697
1698 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
1699 $record->forum = $forum->id;
1700 $record->userid = $otheruser->id;
1701 $record->groupid = $group->id;
1702 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1703
1704 try {
1705 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
1706 $this->fail('Exception expected due to invalid permissions for posting.');
1707 } catch (moodle_exception $e) {
50a20317
JL
1708 $this->assertEquals('nopostforum', $e->errorcode);
1709 }
50a20317 1710 }
7ab43ac8 1711
e321b282
MG
1712 /**
1713 * Test add_discussion_post and auto subscription to a discussion.
1714 */
1715 public function test_add_discussion_post_subscribe_discussion() {
1716 global $USER;
1717
1718 $this->resetAfterTest(true);
1719
1720 self::setAdminUser();
1721
1722 $user = self::getDataGenerator()->create_user();
1723 $admin = get_admin();
1724 // Create course to add the module.
1725 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1726
1727 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1728
1729 // Forum with tracking off.
1730 $record = new stdClass();
1731 $record->course = $course->id;
1732 $forum = self::getDataGenerator()->create_module('forum', $record);
1733 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
1734
1735 // Add discussions to the forums.
1736 $record = new stdClass();
1737 $record->course = $course->id;
1738 $record->userid = $admin->id;
1739 $record->forum = $forum->id;
1740 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1741 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1742
1743 // Try to post as user.
1744 self::setUser($user);
1745 // Enable auto subscribe discussion.
1746 $USER->autosubscribe = true;
1747 // Add a discussion post in a forum discussion where the user is not subscribed (auto-subscribe preference enabled).
1748 mod_forum_external::add_discussion_post($discussion1->firstpost, 'some subject', 'some text here...');
1749
1750 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id);
1751 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1752 // We receive the discussion and the post.
1753 $this->assertEquals(2, count($posts['posts']));
1754 // The user should be subscribed to the discussion after adding a discussion post.
1755 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion1->id, $cm));
1756
1757 // Disable auto subscribe discussion.
1758 $USER->autosubscribe = false;
1759 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion1->id, $cm));
1760 // Add a discussion post in a forum discussion where the user is subscribed (auto-subscribe preference disabled).
1761 mod_forum_external::add_discussion_post($discussion1->firstpost, 'some subject 1', 'some text here 1...');
1762
1763 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id);
1764 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1765 // We receive the discussion and the post.
1766 $this->assertEquals(3, count($posts['posts']));
1767 // The user should still be subscribed to the discussion after adding a discussion post.
1768 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion1->id, $cm));
1769
1770 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion2->id, $cm));
1771 // Add a discussion post in a forum discussion where the user is not subscribed (auto-subscribe preference disabled).
1772 mod_forum_external::add_discussion_post($discussion2->firstpost, 'some subject 2', 'some text here 2...');
1773
1774 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id);
1775 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1776 // We receive the discussion and the post.
1777 $this->assertEquals(2, count($posts['posts']));
1778 // The user should still not be subscribed to the discussion after adding a discussion post.
1779 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion2->id, $cm));
1780
1781 // Passing a value for the discussionsubscribe option parameter.
1782 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion2->id, $cm));
1783 // Add a discussion post in a forum discussion where the user is not subscribed (auto-subscribe preference disabled),
1784 // and the option parameter 'discussionsubscribe' => true in the webservice.
1785 $option = array('name' => 'discussionsubscribe', 'value' => true);
1786 $options[] = $option;
1787 mod_forum_external::add_discussion_post($discussion2->firstpost, 'some subject 2', 'some text here 2...',
1788 $options);
1789
1790 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id);
1791 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1792 // We receive the discussion and the post.
1793 $this->assertEquals(3, count($posts['posts']));
1794 // The user should now be subscribed to the discussion after adding a discussion post.
1795 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion2->id, $cm));
1796 }
1797
7ab43ac8
JL
1798 /*
1799 * Test add_discussion. A basic test since all the API functions are already covered by unit tests.
1800 */
1801 public function test_add_discussion() {
41182118 1802 global $CFG, $USER;
7ab43ac8
JL
1803 $this->resetAfterTest(true);
1804
1805 // Create courses to add the modules.
1806 $course = self::getDataGenerator()->create_course();
1807
1808 $user1 = self::getDataGenerator()->create_user();
1809 $user2 = self::getDataGenerator()->create_user();
1810
1811 // First forum with tracking off.
1812 $record = new stdClass();
1813 $record->course = $course->id;
1814 $record->type = 'news';
1815 $forum = self::getDataGenerator()->create_module('forum', $record);
1816
1817 self::setUser($user1);
1818 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
1819
1820 try {
1821 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1822 $this->fail('Exception expected due to invalid permissions.');
1823 } catch (moodle_exception $e) {
1824 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1825 }
1826
1827 self::setAdminUser();
41182118
BK
1828 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1829 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
7ab43ac8
JL
1830
1831 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1832 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1833
1834 $this->assertCount(1, $discussions['discussions']);
1835 $this->assertCount(0, $discussions['warnings']);
1836
41182118 1837 $this->assertEquals($createddiscussion['discussionid'], $discussions['discussions'][0]['discussion']);
7ab43ac8
JL
1838 $this->assertEquals(-1, $discussions['discussions'][0]['groupid']);
1839 $this->assertEquals('the subject', $discussions['discussions'][0]['subject']);
1840 $this->assertEquals('some text here...', $discussions['discussions'][0]['message']);
1841
5f219cf1
BK
1842 $discussion2pinned = mod_forum_external::add_discussion($forum->id, 'the pinned subject', 'some 2 text here...', -1,
1843 array('options' => array('name' => 'discussionpinned',
1844 'value' => true)));
1845 $discussion3 = mod_forum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...');
1846 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1847 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1848 $this->assertCount(3, $discussions['discussions']);
1849 $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']);
41182118 1850
e881c4f5 1851 // Test inline and regular attachment in new discussion
41182118 1852 // Create a file in a draft area for inline attachments.
e881c4f5
BK
1853
1854 $fs = get_file_storage();
1855
41182118 1856 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5
BK
1857 $draftidattach = file_get_unused_draft_itemid();
1858
41182118
BK
1859 $usercontext = context_user::instance($USER->id);
1860 $filepath = '/';
1861 $filearea = 'draft';
1862 $component = 'user';
1863 $filenameimg = 'shouldbeanimage.txt';
1864 $filerecord = array(
1865 'contextid' => $usercontext->id,
1866 'component' => $component,
1867 'filearea' => $filearea,
1868 'itemid' => $draftidinlineattach,
1869 'filepath' => $filepath,
1870 'filename' => $filenameimg,
1871 );
e881c4f5 1872
e881c4f5
BK
1873 // Create a file in a draft area for regular attachments.
1874 $filerecordattach = $filerecord;
1875 $attachfilename = 'attachment.txt';
1876 $filerecordattach['filename'] = $attachfilename;
1877 $filerecordattach['itemid'] = $draftidattach;
41182118 1878 $fs->create_file_from_string($filerecord, 'image contents (not really)');
e881c4f5
BK
1879 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1880
41182118
BK
1881 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot .
1882 "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}" .
1883 '" alt="inlineimage">.';
1884
48143990 1885 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 1886 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
1887 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the inline attachment subject',
1888 $dummytext, -1, $options);
1889 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
1890
1891 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1892 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1893
1894 $this->assertCount(4, $discussions['discussions']);
1895 $this->assertCount(0, $createddiscussion['warnings']);
1896 // Can't guarantee order of posts during tests.
1897 $postfound = false;
1898 foreach ($discussions['discussions'] as $thisdiscussion) {
1899 if ($createddiscussion['discussionid'] == $thisdiscussion['discussion']) {
e881c4f5
BK
1900 $this->assertEquals($thisdiscussion['attachment'], 1, "There should be a non-inline attachment");
1901 $this->assertCount(1, $thisdiscussion['attachments'], "There should be 1 attachment");
1902 $this->assertEquals($thisdiscussion['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
1903 $this->assertNotContains('draftfile.php', $thisdiscussion['message']);
1904 $this->assertContains('pluginfile.php', $thisdiscussion['message']);
1905 $postfound = true;
e881c4f5 1906 break;
41182118
BK
1907 }
1908 }
1909
1910 $this->assertTrue($postfound);
7ab43ac8
JL
1911 }
1912
1913 /**
1914 * Test adding discussions in a course with gorups
1915 */
1916 public function test_add_discussion_in_course_with_groups() {
1917 global $CFG;
1918
1919 $this->resetAfterTest(true);
1920
1921 // Create course to add the module.
1922 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1923 $user = self::getDataGenerator()->create_user();
1924 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1925
1926 // Forum forcing separate gropus.
1927 $record = new stdClass();
1928 $record->course = $course->id;
1929 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
1930
1931 // Try to post (user not enrolled).
1932 self::setUser($user);
1933
1934 // The user is not enroled in any group, try to post in a forum with separate groups.
1935 try {
1936 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1937 $this->fail('Exception expected due to invalid group permissions.');
1938 } catch (moodle_exception $e) {
1939 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1940 }
1941
1942 try {
1943 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0);
1944 $this->fail('Exception expected due to invalid group permissions.');
1945 } catch (moodle_exception $e) {
1946 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1947 }
1948
1949 // Create a group.
1950 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1951
1952 // Try to post in a group the user is not enrolled.
1953 try {
1954 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1955 $this->fail('Exception expected due to invalid group permissions.');
1956 } catch (moodle_exception $e) {
1957 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1958 }
1959
1960 // Add the user to a group.
1961 groups_add_member($group->id, $user->id);
1962
1963 // Try to post in a group the user is not enrolled.
1964 try {
1965 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1);
1966 $this->fail('Exception expected due to invalid group.');
1967 } catch (moodle_exception $e) {
1968 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1969 }
1970
1971 // Nost add the discussion using a valid group.
1972 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1973 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1974
1975 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1976 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1977
1978 $this->assertCount(1, $discussions['discussions']);
1979 $this->assertCount(0, $discussions['warnings']);
1980 $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']);
1981 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1982
1983 // Now add a discussions without indicating a group. The function should guess the correct group.
1984 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1985 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1986
1987 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1988 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1989
1990 $this->assertCount(2, $discussions['discussions']);
1991 $this->assertCount(0, $discussions['warnings']);
1992 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1993 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1994
1995 // Enrol the same user in other group.
1996 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1997 groups_add_member($group2->id, $user->id);
1998
1999 // Now add a discussions without indicating a group. The function should guess the correct group (the first one).
2000 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
2001 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
2002
2003 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
2004 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
2005
2006 $this->assertCount(3, $discussions['discussions']);
2007 $this->assertCount(0, $discussions['warnings']);
2008 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
2009 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
2010 $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']);
2011
2012 }
2013
f5b4320e
P
2014 /*
2015 * Test set_lock_state.
2016 */
2017 public function test_set_lock_state() {
2018 global $DB;
2019 $this->resetAfterTest(true);
2020
2021 // Create courses to add the modules.
2022 $course = self::getDataGenerator()->create_course();
2023 $user = self::getDataGenerator()->create_user();
2024 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2025
2026 // First forum with tracking off.
2027 $record = new stdClass();
2028 $record->course = $course->id;
2029 $record->type = 'news';
2030 $forum = self::getDataGenerator()->create_module('forum', $record);
2031
2032 $record = new stdClass();
2033 $record->course = $course->id;
2034 $record->userid = $user->id;
2035 $record->forum = $forum->id;
2036 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2037
2038 // User who is a student.
2039 self::setUser($user);
2040 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
2041
2042 // Only a teacher should be able to lock a discussion.
bdb4a87d
P
2043 try {
2044 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, 0);
2045 $this->fail('Exception expected due to missing capability.');
2046 } catch (moodle_exception $e) {
2047 $this->assertEquals('errorcannotlock', $e->errorcode);
2048 }
f5b4320e
P
2049
2050 // Set the lock.
2051 self::setAdminUser();
2052 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, 0);
2053 $result = external_api::clean_returnvalue(mod_forum_external::set_lock_state_returns(), $result);
bdb4a87d 2054 $this->assertTrue($result['locked']);
f5b4320e
P
2055 $this->assertNotEquals(0, $result['times']['locked']);
2056
2057 // Unset the lock.
2058 $result = mod_forum_external::set_lock_state($forum->id, $discussion->id, time());
2059 $result = external_api::clean_returnvalue(mod_forum_external::set_lock_state_returns(), $result);
bdb4a87d 2060 $this->assertFalse($result['locked']);
f5b4320e
P
2061 $this->assertEquals('0', $result['times']['locked']);
2062 }
2063
04cd8ae3
JL
2064 /*
2065 * Test can_add_discussion. A basic test since all the API functions are already covered by unit tests.
2066 */
2067 public function test_can_add_discussion() {
581e75bf 2068 global $DB;
04cd8ae3
JL
2069 $this->resetAfterTest(true);
2070
2071 // Create courses to add the modules.
2072 $course = self::getDataGenerator()->create_course();
2073
2074 $user = self::getDataGenerator()->create_user();
2075
2076 // First forum with tracking off.
2077 $record = new stdClass();
2078 $record->course = $course->id;
2079 $record->type = 'news';
2080 $forum = self::getDataGenerator()->create_module('forum', $record);
2081
2082 // User with no permissions to add in a news forum.
2083 self::setUser($user);
2084 $this->getDataGenerator()->enrol_user($user->id, $course->id);
2085
2086 $result = mod_forum_external::can_add_discussion($forum->id);
2087 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
2088 $this->assertFalse($result['status']);
581e75bf
JL
2089 $this->assertFalse($result['canpindiscussions']);
2090 $this->assertTrue($result['cancreateattachment']);
2091
2092 // Disable attachments.
2093 $DB->set_field('forum', 'maxattachments', 0, array('id' => $forum->id));
2094 $result = mod_forum_external::can_add_discussion($forum->id);
2095 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
2096 $this->assertFalse($result['status']);
2097 $this->assertFalse($result['canpindiscussions']);
2098 $this->assertFalse($result['cancreateattachment']);
2099 $DB->set_field('forum', 'maxattachments', 1, array('id' => $forum->id)); // Enable attachments again.
04cd8ae3
JL
2100
2101 self::setAdminUser();
2102 $result = mod_forum_external::can_add_discussion($forum->id);
2103 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
2104 $this->assertTrue($result['status']);
581e75bf
JL
2105 $this->assertTrue($result['canpindiscussions']);
2106 $this->assertTrue($result['cancreateattachment']);
cbf63d8e
SR
2107 }
2108
2109 /*
2110 * A basic test to make sure users cannot post to forum after the cutoff date.
2111 */
2112 public function test_can_add_discussion_after_cutoff() {
2113 $this->resetAfterTest(true);
2114
2115 // Create courses to add the modules.
2116 $course = self::getDataGenerator()->create_course();
2117
2118 $user = self::getDataGenerator()->create_user();
2119
2120 // Create a forum with cutoff date set to a past date.
2121 $forum = self::getDataGenerator()->create_module('forum', ['course' => $course->id, 'cutoffdate' => time() - 1]);
04cd8ae3 2122
cbf63d8e
SR
2123 // User with no mod/forum:canoverridecutoff capability.
2124 self::setUser($user);
2125 $this->getDataGenerator()->enrol_user($user->id, $course->id);
2126
2127 $result = mod_forum_external::can_add_discussion($forum->id);
2128 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
2129 $this->assertFalse($result['status']);
2130
2131 self::setAdminUser();
2132 $result = mod_forum_external::can_add_discussion($forum->id);
2133 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
2134 $this->assertTrue($result['status']);
04cd8ae3
JL
2135 }
2136
b7ce46df
JL
2137 /**
2138 * Test get forum posts discussions including rating information.
2139 */
2140 public function test_mod_forum_get_forum_discussion_rating_information() {
2141 global $DB, $CFG;
2142 require_once($CFG->dirroot . '/rating/lib.php');
2143
2144 $this->resetAfterTest(true);
2145
2146 $user1 = self::getDataGenerator()->create_user();
2147 $user2 = self::getDataGenerator()->create_user();
2148 $user3 = self::getDataGenerator()->create_user();
2149 $teacher = self::getDataGenerator()->create_user();
2150
2151 // Create course to add the module.
2152 $course = self::getDataGenerator()->create_course();
2153
2154 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2155 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2156 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $studentrole->id, 'manual');
2157 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $studentrole->id, 'manual');
2158 $this->getDataGenerator()->enrol_user($user3->id, $course->id, $studentrole->id, 'manual');
2159 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
2160
2161 // Create the forum.
2162 $record = new stdClass();
2163 $record->course = $course->id;
2164 // Set Aggregate type = Average of ratings.
2165 $record->assessed = RATING_AGGREGATE_AVERAGE;
2166 $record->scale = 100;
2167 $forum = self::getDataGenerator()->create_module('forum', $record);
2168 $context = context_module::instance($forum->cmid);
2169
2170 // Add discussion to the forum.
2171 $record = new stdClass();
2172 $record->course = $course->id;
2173 $record->userid = $user1->id;
2174 $record->forum = $forum->id;
2175 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2176
2177 // Retrieve the first post.
2178 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
2179
2180 // Rate the discussion as user2.
2181 $rating1 = new stdClass();
2182 $rating1->contextid = $context->id;
2183 $rating1->component = 'mod_forum';
2184 $rating1->ratingarea = 'post';
2185 $rating1->itemid = $post->id;
2186 $rating1->rating = 50;
2187 $rating1->scaleid = 100;
2188 $rating1->userid = $user2->id;
2189 $rating1->timecreated = time();
2190 $rating1->timemodified = time();
2191 $rating1->id = $DB->insert_record('rating', $rating1);
2192
2193 // Rate the discussion as user3.
2194 $rating2 = new stdClass();
2195 $rating2->contextid = $context->id;
2196 $rating2->component = 'mod_forum';
2197 $rating2->ratingarea = 'post';
2198 $rating2->itemid = $post->id;
2199 $rating2->rating = 100;
2200 $rating2->scaleid = 100;
2201 $rating2->userid = $user3->id;
2202 $rating2->timecreated = time() + 1;
2203 $rating2->timemodified = time() + 1;
2204 $rating2->id = $DB->insert_record('rating', $rating2);
2205
2206 // Retrieve the rating for the post as student.
2207 $this->setUser($user1);
2208 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2209 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2210 $this->assertCount(1, $posts['ratinginfo']['ratings']);
2211 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
2212 $this->assertFalse($posts['ratinginfo']['canviewall']);
2213 $this->assertFalse($posts['ratinginfo']['ratings'][0]['canrate']);
2214 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
2215 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
2216
2217 // Retrieve the rating for the post as teacher.
2218 $this->setUser($teacher);
2219 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2220 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2221 $this->assertCount(1, $posts['ratinginfo']['ratings']);
2222 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
2223 $this->assertTrue($posts['ratinginfo']['canviewall']);
2224 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canrate']);
2225 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
2226 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
2227 }
4daa0d08
JL
2228
2229 /**
2230 * Test mod_forum_get_forum_access_information.
2231 */
2232 public function test_mod_forum_get_forum_access_information() {
2233 global $DB;
2234
2235 $this->resetAfterTest(true);
2236
2237 $student = self::getDataGenerator()->create_user();
2238 $course = self::getDataGenerator()->create_course();
2239 // Create the forum.
2240 $record = new stdClass();
2241 $record->course = $course->id;
2242 $forum = self::getDataGenerator()->create_module('forum', $record);
2243
2244 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
2245 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
2246
2247 self::setUser($student);
2248 $result = mod_forum_external::get_forum_access_information($forum->id);
2249 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
2250
2251 // Check default values for capabilities.
2252 $enabledcaps = array('canviewdiscussion', 'canstartdiscussion', 'canreplypost', 'canviewrating', 'cancreateattachment',
13cd05ac 2253 'canexportownpost', 'cancantogglefavourite', 'candeleteownpost', 'canallowforcesubscribe');
4daa0d08
JL
2254
2255 unset($result['warnings']);
2256 foreach ($result as $capname => $capvalue) {
2257 if (in_array($capname, $enabledcaps)) {
2258 $this->assertTrue($capvalue);
2259 } else {
2260 $this->assertFalse($capvalue);
2261 }
2262 }
2263 // Now, unassign some capabilities.
2264 unassign_capability('mod/forum:deleteownpost', $studentrole->id);
2265 unassign_capability('mod/forum:allowforcesubscribe', $studentrole->id);
2266 array_pop($enabledcaps);
2267 array_pop($enabledcaps);
2268 accesslib_clear_all_caches_for_unit_testing();
2269
2270 $result = mod_forum_external::get_forum_access_information($forum->id);
2271 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
2272 unset($result['warnings']);
2273 foreach ($result as $capname => $capvalue) {
2274 if (in_array($capname, $enabledcaps)) {
2275 $this->assertTrue($capvalue);
2276 } else {
2277 $this->assertFalse($capvalue);
2278 }
2279 }
2280 }
bc4c7337
AN
2281
2282 /**
2283 * Test add_discussion_post
2284 */
2285 public function test_add_discussion_post_private() {
2286 global $DB;
2287
2288 $this->resetAfterTest(true);
2289
2290 self::setAdminUser();
2291
2292 // Create course to add the module.
2293 $course = self::getDataGenerator()->create_course();
2294
2295 // Standard forum.
2296 $record = new stdClass();
2297 $record->course = $course->id;
2298 $forum = self::getDataGenerator()->create_module('forum', $record);
2299 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
2300 $forumcontext = context_module::instance($forum->cmid);
2301 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
2302
2303 // Create an enrol users.
2304 $student1 = self::getDataGenerator()->create_user();
2305 $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
2306 $student2 = self::getDataGenerator()->create_user();
2307 $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
2308 $teacher1 = self::getDataGenerator()->create_user();
2309 $this->getDataGenerator()->enrol_user($teacher1->id, $course->id, 'editingteacher');
2310 $teacher2 = self::getDataGenerator()->create_user();
2311 $this->getDataGenerator()->enrol_user($teacher2->id, $course->id, 'editingteacher');
2312
2313 // Add a new discussion to the forum.
2314 self::setUser($student1);
2315 $record = new stdClass();
2316 $record->course = $course->id;
2317 $record->userid = $student1->id;
2318 $record->forum = $forum->id;
2319 $discussion = $generator->create_discussion($record);
2320
2321 // Have the teacher reply privately.
2322 self::setUser($teacher1);
2323 $post = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...', [
2324 [
2325 'name' => 'private',
2326 'value' => true,
2327 ],
2328 ]);
2329 $post = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $post);
2330 $privatereply = $DB->get_record('forum_posts', array('id' => $post['postid']));
2331 $this->assertEquals($student1->id, $privatereply->privatereplyto);
2332 // Bump the time of the private reply to ensure order.
2333 $privatereply->created++;
2334 $privatereply->modified = $privatereply->created;
2335 $DB->update_record('forum_posts', $privatereply);
2336
2337 // The teacher will receive their private reply.
2338 self::setUser($teacher1);
2339 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2340 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2341 $this->assertEquals(2, count($posts['posts']));
2342 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2343
2344 // Another teacher on the course will also receive the private reply.
2345 self::setUser($teacher2);
2346 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2347 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2348 $this->assertEquals(2, count($posts['posts']));
2349 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2350
2351 // The student will receive the private reply.
2352 self::setUser($student1);
2353 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2354 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2355 $this->assertEquals(2, count($posts['posts']));
2356 $this->assertTrue($posts['posts'][0]['isprivatereply']);
2357
2358 // Another student will not receive the private reply.
2359 self::setUser($student2);
2360 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
2361 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2362 $this->assertEquals(1, count($posts['posts']));
2363 $this->assertFalse($posts['posts'][0]['isprivatereply']);
2364
2365 // A user cannot reply to a private reply.
2366 self::setUser($teacher2);
2367 $this->expectException('coding_exception');
2368 $post = mod_forum_external::add_discussion_post($privatereply->id, 'some subject', 'some text here...', [
2369 'options' => [
2370 'name' => 'private',
2371 'value' => false,
2372 ],
2373 ]);
2374 }
29cc69f3
JL
2375
2376 /**
2377 * Test trusted text enabled.
2378 */
2379 public function test_trusted_text_enabled() {
2380 global $USER, $CFG;
2381
2382 $this->resetAfterTest(true);
2383 $CFG->enabletrusttext = 1;
2384
2385 $dangeroustext = '<button>Untrusted text</button>';
2386 $cleantext = 'Untrusted text';
2387
2388 // Create courses to add the modules.
2389 $course = self::getDataGenerator()->create_course();
2390 $user1 = self::getDataGenerator()->create_user();
2391
2392 // First forum with tracking off.
2393 $record = new stdClass();
2394 $record->course = $course->id;
2395 $record->type = 'qanda';
2396 $forum = self::getDataGenerator()->create_module('forum', $record);
2397 $context = context_module::instance($forum->cmid);
2398
2399 // Add discussions to the forums.
2400 $discussionrecord = new stdClass();
2401 $discussionrecord->course = $course->id;
2402 $discussionrecord->userid = $user1->id;
2403 $discussionrecord->forum = $forum->id;
2404 $discussionrecord->message = $dangeroustext;
2405 $discussionrecord->messagetrust = trusttext_trusted($context);
2406 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
2407
2408 self::setAdminUser();
2409 $discussionrecord->userid = $USER->id;
2410 $discussionrecord->messagetrust = trusttext_trusted($context);
2411 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
2412
2413 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
2414 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
2415
2416 $this->assertCount(2, $discussions['discussions']);
2417 $this->assertCount(0, $discussions['warnings']);
2418 // Admin message is fully trusted.
2419 $this->assertEquals(1, $discussions['discussions'][0]['messagetrust']);
2420 $this->assertEquals($dangeroustext, $discussions['discussions'][0]['message']);
2421 // Student message is not trusted.
2422 $this->assertEquals(0, $discussions['discussions'][1]['messagetrust']);
2423 $this->assertEquals($cleantext, $discussions['discussions'][1]['message']);
2424
2425 // Get posts now.
2426 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id);
2427 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2428 // Admin message is fully trusted.
2429 $this->assertEquals(1, $posts['posts'][0]['messagetrust']);
2430 $this->assertEquals($dangeroustext, $posts['posts'][0]['message']);
2431
2432 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id);
2433 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2434 // Student message is not trusted.
2435 $this->assertEquals(0, $posts['posts'][0]['messagetrust']);
2436 $this->assertEquals($cleantext, $posts['posts'][0]['message']);
2437 }
2438
2439 /**
2440 * Test trusted text disabled.
2441 */
2442 public function test_trusted_text_disabled() {
2443 global $USER, $CFG;
2444
2445 $this->resetAfterTest(true);
2446 $CFG->enabletrusttext = 0;
2447
2448 $dangeroustext = '<button>Untrusted text</button>';
2449 $cleantext = 'Untrusted text';
2450
2451 // Create courses to add the modules.
2452 $course = self::getDataGenerator()->create_course();
2453 $user1 = self::getDataGenerator()->create_user();
2454
2455 // First forum with tracking off.
2456 $record = new stdClass();
2457 $record->course = $course->id;
2458 $record->type = 'qanda';
2459 $forum = self::getDataGenerator()->create_module('forum', $record);
2460 $context = context_module::instance($forum->cmid);
2461
2462 // Add discussions to the forums.
2463 $discussionrecord = new stdClass();
2464 $discussionrecord->course = $course->id;
2465 $discussionrecord->userid = $user1->id;
2466 $discussionrecord->forum = $forum->id;
2467 $discussionrecord->message = $dangeroustext;
2468 $discussionrecord->messagetrust = trusttext_trusted($context);
2469 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
2470
2471 self::setAdminUser();
2472 $discussionrecord->userid = $USER->id;
2473 $discussionrecord->messagetrust = trusttext_trusted($context);
2474 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
2475
2476 $discussions = mod_forum_external::get_forum_discussions($forum->id);
2477 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
2478
2479 $this->assertCount(2, $discussions['discussions']);
2480 $this->assertCount(0, $discussions['warnings']);
2481 // Admin message is not trusted because enabletrusttext is disabled.
2482 $this->assertEquals(0, $discussions['discussions'][0]['messagetrust']);
2483 $this->assertEquals($cleantext, $discussions['discussions'][0]['message']);
2484 // Student message is not trusted.
2485 $this->assertEquals(0, $discussions['discussions'][1]['messagetrust']);
2486 $this->assertEquals($cleantext, $discussions['discussions'][1]['message']);
2487
2488 // Get posts now.
2489 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id);
2490 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2491 // Admin message is not trusted because enabletrusttext is disabled.
2492 $this->assertEquals(0, $posts['posts'][0]['messagetrust']);
2493 $this->assertEquals($cleantext, $posts['posts'][0]['message']);
2494
2495 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id);
2496 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
2497 // Student message is not trusted.
2498 $this->assertEquals(0, $posts['posts'][0]['messagetrust']);
2499 $this->assertEquals($cleantext, $posts['posts'][0]['message']);
2500 }
56444a60
JL
2501
2502 /**
2503 * Test delete a discussion.
2504 */
2505 public function test_delete_post_discussion() {
2506 global $DB;
2507 $this->resetAfterTest(true);
2508
2509 // Setup test data.
2510 $course = $this->getDataGenerator()->create_course();
2511 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
2512 $user = $this->getDataGenerator()->create_user();
2513 $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2514 self::getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
2515
2516 // Add a discussion.
2517 $record = new stdClass();
2518 $record->course = $course->id;
2519 $record->userid = $user->id;
2520 $record->forum = $forum->id;
2521 $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2522
2523 $this->setUser($user);
2524 $result = mod_forum_external::delete_post($discussion->firstpost);
2525 $result = external_api::clean_returnvalue(mod_forum_external::delete_post_returns(), $result);
2526 $this->assertTrue($result['status']);
2527 $this->assertEquals(0, $DB->count_records('forum_posts', array('id' => $discussion->firstpost)));
2528 $this->assertEquals(0, $DB->count_records('forum_discussions', array('id' => $discussion->id)));
2529 }
2530
2531 /**
2532 * Test delete a post.
2533 */
2534 public function test_delete_post_post() {
2535 global $DB;
2536 $this->resetAfterTest(true);
2537
2538 // Setup test data.
2539 $course = $this->getDataGenerator()->create_course();
2540 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
2541 $user = $this->getDataGenerator()->create_user();
2542 $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2543 self::getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
2544
2545 // Add a discussion.
2546 $record = new stdClass();
2547 $record->course = $course->id;
2548 $record->userid = $user->id;
2549 $record->forum = $forum->id;
2550 $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2551 $parentpost = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
2552
2553 // Add a post.
2554 $record = new stdClass();
2555 $record->course = $course->id;
2556 $record->userid = $user->id;
2557 $record->forum = $forum->id;
2558 $record->discussion = $discussion->id;
2559 $record->parent = $parentpost->id;
2560 $post = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
2561
2562 $this->setUser($user);
2563 $result = mod_forum_external::delete_post($post->id);
2564 $result = external_api::clean_returnvalue(mod_forum_external::delete_post_returns(), $result);
2565 $this->assertTrue($result['status']);
2566 $this->assertEquals(1, $DB->count_records('forum_posts', array('discussion' => $discussion->id)));
2567 $this->assertEquals(1, $DB->count_records('forum_discussions', array('id' => $discussion->id)));
2568 }
2569
2570 /**
2571 * Test delete a different user post.
2572 */
2573 public function test_delete_post_other_user_post() {
2574 global $DB;
2575 $this->resetAfterTest(true);
2576
2577 // Setup test data.
2578 $course = $this->getDataGenerator()->create_course();
2579 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
2580 $user = $this->getDataGenerator()->create_user();
2581 $otheruser = $this->getDataGenerator()->create_user();
2582 $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2583 self::getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
2584 self::getDataGenerator()->enrol_user($otheruser->id, $course->id, $role->id);
2585
2586 // Add a discussion.
2587 $record = array();
2588 $record['course'] = $course->id;
2589 $record['forum'] = $forum->id;
2590 $record['userid'] = $user->id;
2591 $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
2592 $parentpost = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
2593
2594 // Add a post.
2595 $record = new stdClass();
2596 $record->course = $course->id;
2597 $record->userid = $user->id;
2598 $record->forum = $forum->id;
2599 $record->discussion = $discussion->id;
2600 $record->parent = $parentpost->id;
2601 $post = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
2602
2603 $this->setUser($otheruser);
2604 $this->expectExceptionMessage(get_string('cannotdeletepost', 'forum'));
2605 mod_forum_external::delete_post($post->id);
2606 }
2b9fe87d 2607}