MDL-50995 mod_forum: New WS mod_forum_add_discussion_post
[moodle.git] / mod / forum / tests / externallib_test.php
CommitLineData
2b9fe87d
MN
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * The module forums external functions unit tests
20 *
21 * @package mod_forum
22 * @category external
23 * @copyright 2012 Mark Nelson <markn@moodle.com>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
30
31require_once($CFG->dirroot . '/webservice/tests/helpers.php');
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.
63 $user = self::getDataGenerator()->create_user();
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;
76 $forum1 = self::getDataGenerator()->create_module('forum', $record);
77
78 // Second forum.
79 $record = new stdClass();
80 $record->introformat = FORMAT_HTML;
81 $record->course = $course2->id;
82 $forum2 = self::getDataGenerator()->create_module('forum', $record);
83
7ea6ada3
JL
84 // Add discussions to the forums.
85 $record = new stdClass();
86 $record->course = $course1->id;
87 $record->userid = $user->id;
88 $record->forum = $forum1->id;
89 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
90 // Expect one discussion.
91 $forum1->numdiscussions = 1;
92
93 $record = new stdClass();
94 $record->course = $course2->id;
95 $record->userid = $user->id;
96 $record->forum = $forum2->id;
97 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
98 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
99 // Expect two discussions.
100 $forum2->numdiscussions = 2;
101
2b9fe87d
MN
102 // Check the forum was correctly created.
103 $this->assertEquals(2, $DB->count_records_select('forum', 'id = :forum1 OR id = :forum2',
104 array('forum1' => $forum1->id, 'forum2' => $forum2->id)));
105
106 // Enrol the user in two courses.
909f27ac
JM
107 // DataGenerator->enrol_user automatically sets a role for the user with the permission mod/form:viewdiscussion.
108 $this->getDataGenerator()->enrol_user($user->id, $course1->id, null, 'manual');
109 // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
2b9fe87d 110 $enrol = enrol_get_plugin('manual');
2b9fe87d
MN
111 $enrolinstances = enrol_get_instances($course2->id, true);
112 foreach ($enrolinstances as $courseenrolinstance) {
113 if ($courseenrolinstance->enrol == "manual") {
114 $instance2 = $courseenrolinstance;
115 break;
116 }
117 }
118 $enrol->enrol_user($instance2, $user->id);
119
2b9fe87d 120 // Assign capabilities to view forums for forum 2.
74b63eae 121 $cm2 = get_coursemodule_from_id('forum', $forum2->cmid, 0, false, MUST_EXIST);
2b9fe87d
MN
122 $context2 = context_module::instance($cm2->id);
123 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
124 $roleid2 = $this->assignUserCapability('mod/forum:viewdiscussion', $context2->id, $newrole);
125
126 // Create what we expect to be returned when querying the two courses.
c8f1d8a0
JL
127 unset($forum1->displaywordcount);
128 unset($forum2->displaywordcount);
129
2b9fe87d
MN
130 $expectedforums = array();
131 $expectedforums[$forum1->id] = (array) $forum1;
132 $expectedforums[$forum2->id] = (array) $forum2;
133
134 // Call the external function passing course ids.
135 $forums = mod_forum_external::get_forums_by_courses(array($course1->id, $course2->id));
c8f1d8a0
JL
136 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
137 $this->assertCount(2, $forums);
138 foreach ($forums as $forum) {
139 $this->assertEquals($expectedforums[$forum['id']], $forum);
140 }
2b9fe87d
MN
141
142 // Call the external function without passing course id.
143 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
144 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
145 $this->assertCount(2, $forums);
146 foreach ($forums as $forum) {
147 $this->assertEquals($expectedforums[$forum['id']], $forum);
148 }
2b9fe87d
MN
149
150 // Unenrol user from second course and alter expected forums.
151 $enrol->unenrol_user($instance2, $user->id);
152 unset($expectedforums[$forum2->id]);
153
154 // Call the external function without passing course id.
155 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
156 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
157 $this->assertCount(1, $forums);
158 $this->assertEquals($expectedforums[$forum1->id], $forums[0]);
159
160 // Call for the second course we unenrolled the user from.
161 $forums = mod_forum_external::get_forums_by_courses(array($course2->id));
162 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
163 $this->assertCount(0, $forums);
a9a0cb69
MN
164 }
165
166 /**
167 * Test get forum discussions
168 */
169 public function test_mod_forum_get_forum_discussions() {
170 global $USER, $CFG, $DB;
171
172 $this->resetAfterTest(true);
173
174 // Set the CFG variable to allow track forums.
175 $CFG->forum_trackreadposts = true;
176
177 // Create a user who can track forums.
178 $record = new stdClass();
179 $record->trackforums = true;
180 $user1 = self::getDataGenerator()->create_user($record);
181 // Create a bunch of other users to post.
182 $user2 = self::getDataGenerator()->create_user();
183 $user3 = self::getDataGenerator()->create_user();
184 $user4 = self::getDataGenerator()->create_user();
185
186 // Set the first created user to the test user.
187 self::setUser($user1);
188
189 // Create courses to add the modules.
190 $course1 = self::getDataGenerator()->create_course();
191 $course2 = self::getDataGenerator()->create_course();
192
193 // First forum with tracking off.
194 $record = new stdClass();
195 $record->course = $course1->id;
196 $record->trackingtype = FORUM_TRACKING_OFF;
197 $forum1 = self::getDataGenerator()->create_module('forum', $record);
198
199 // Second forum of type 'qanda' with tracking enabled.
200 $record = new stdClass();
201 $record->course = $course2->id;
202 $record->type = 'qanda';
bd8f5d45 203 $record->trackingtype = FORUM_TRACKING_FORCED;
a9a0cb69
MN
204 $forum2 = self::getDataGenerator()->create_module('forum', $record);
205
a9a0cb69
MN
206 // Add discussions to the forums.
207 $record = new stdClass();
208 $record->course = $course1->id;
209 $record->userid = $user1->id;
210 $record->forum = $forum1->id;
c3f31a3d 211 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
a9a0cb69
MN
212
213 $record = new stdClass();
214 $record->course = $course2->id;
215 $record->userid = $user2->id;
216 $record->forum = $forum2->id;
c3f31a3d 217 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
a9a0cb69 218
a9a0cb69
MN
219 // Add three replies to the discussion 1 from different users.
220 $record = new stdClass();
221 $record->discussion = $discussion1->id;
222 $record->parent = $discussion1->firstpost;
223 $record->userid = $user2->id;
c3f31a3d 224 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
2b9fe87d 225
a9a0cb69
MN
226 $record->parent = $discussion1reply1->id;
227 $record->userid = $user3->id;
c3f31a3d 228 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
a9a0cb69
MN
229
230 $record->userid = $user4->id;
c3f31a3d 231 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
a9a0cb69
MN
232
233 // Add two replies to discussion 2 from different users.
234 $record = new stdClass();
235 $record->discussion = $discussion2->id;
236 $record->parent = $discussion2->firstpost;
237 $record->userid = $user1->id;
c3f31a3d 238 $discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
a9a0cb69
MN
239
240 $record->parent = $discussion2reply1->id;
241 $record->userid = $user3->id;
c3f31a3d 242 $discussion2reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
a9a0cb69
MN
243
244 // Check the forums were correctly created.
40afeb40
JL
245 $this->assertEquals(2, $DB->count_records_select('forum', 'id = :forum1 OR id = :forum2',
246 array('forum1' => $forum1->id, 'forum2' => $forum2->id)));
a9a0cb69
MN
247
248 // Check the discussions were correctly created.
40afeb40
JL
249 $this->assertEquals(2, $DB->count_records_select('forum_discussions', 'forum = :forum1 OR forum = :forum2',
250 array('forum1' => $forum1->id, 'forum2' => $forum2->id)));
a9a0cb69
MN
251
252 // Check the posts were correctly created, don't forget each discussion created also creates a post.
253 $this->assertEquals(7, $DB->count_records_select('forum_posts', 'discussion = :discussion1 OR discussion = :discussion2',
254 array('discussion1' => $discussion1->id, 'discussion2' => $discussion2->id)));
255
256 // Enrol the user in the first course.
257 $enrol = enrol_get_plugin('manual');
909f27ac
JM
258 // Following line enrol and assign default role id to the user.
259 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
260 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
a9a0cb69
MN
261
262 // Now enrol into the second course.
909f27ac 263 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
a9a0cb69
MN
264 $enrolinstances = enrol_get_instances($course2->id, true);
265 foreach ($enrolinstances as $courseenrolinstance) {
266 if ($courseenrolinstance->enrol == "manual") {
267 $instance2 = $courseenrolinstance;
268 break;
269 }
270 }
271 $enrol->enrol_user($instance2, $user1->id);
272
a9a0cb69 273 // Assign capabilities to view discussions for forum 2.
74b63eae 274 $cm = get_coursemodule_from_id('forum', $forum2->cmid, 0, false, MUST_EXIST);
a9a0cb69
MN
275 $context = context_module::instance($cm->id);
276 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
277 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
278
a9a0cb69
MN
279 // Create what we expect to be returned when querying the forums.
280 $expecteddiscussions = array();
9a3f7a48 281 $expecteddiscussions[] = array(
a9a0cb69
MN
282 'id' => $discussion1->id,
283 'course' => $discussion1->course,
284 'forum' => $discussion1->forum,
285 'name' => $discussion1->name,
286 'firstpost' => $discussion1->firstpost,
287 'userid' => $discussion1->userid,
288 'groupid' => $discussion1->groupid,
289 'assessed' => $discussion1->assessed,
290 'timemodified' => $discussion1reply3->created,
291 'usermodified' => $discussion1reply3->userid,
292 'timestart' => $discussion1->timestart,
293 'timeend' => $discussion1->timeend,
294 'firstuserfullname' => fullname($user1),
295 'firstuserimagealt' => $user1->imagealt,
296 'firstuserpicture' => $user1->picture,
297 'firstuseremail' => $user1->email,
298 'subject' => $discussion1->name,
299 'numreplies' => 3,
300 'numunread' => '',
301 'lastpost' => $discussion1reply3->id,
302 'lastuserid' => $user4->id,
303 'lastuserfullname' => fullname($user4),
304 'lastuserimagealt' => $user4->imagealt,
305 'lastuserpicture' => $user4->picture,
306 'lastuseremail' => $user4->email
307 );
9a3f7a48 308 $expecteddiscussions[] = array(
a9a0cb69
MN
309 'id' => $discussion2->id,
310 'course' => $discussion2->course,
311 'forum' => $discussion2->forum,
312 'name' => $discussion2->name,
313 'firstpost' => $discussion2->firstpost,
314 'userid' => $discussion2->userid,
315 'groupid' => $discussion2->groupid,
316 'assessed' => $discussion2->assessed,
317 'timemodified' => $discussion2reply2->created,
318 'usermodified' => $discussion2reply2->userid,
319 'timestart' => $discussion2->timestart,
320 'timeend' => $discussion2->timeend,
321 'firstuserfullname' => fullname($user2),
322 'firstuserimagealt' => $user2->imagealt,
323 'firstuserpicture' => $user2->picture,
324 'firstuseremail' => $user2->email,
325 'subject' => $discussion2->name,
326 'numreplies' => 2,
327 'numunread' => 3,
328 'lastpost' => $discussion2reply2->id,
329 'lastuserid' => $user3->id,
330 'lastuserfullname' => fullname($user3),
331 'lastuserimagealt' => $user3->imagealt,
332 'lastuserpicture' => $user3->picture,
333 'lastuseremail' => $user3->email
334 );
a9a0cb69
MN
335
336 // Call the external function passing forum ids.
40afeb40 337 $discussions = mod_forum_external::get_forum_discussions(array($forum1->id, $forum2->id));
9a3f7a48 338 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
a9a0cb69 339 $this->assertEquals($expecteddiscussions, $discussions);
40afeb40
JL
340 // Some debugging is going to be produced, this is because we switch PAGE contexts in the get_forum_discussions function,
341 // the switch happens when the validate_context function is called inside a foreach loop.
342 // See MDL-41746 for more information.
343 $this->assertDebuggingCalled();
a9a0cb69 344
039c81f0 345 // Remove the users post from the qanda forum and ensure they can still see the discussion.
a9a0cb69 346 $DB->delete_records('forum_posts', array('id' => $discussion2reply1->id));
40afeb40
JL
347 $discussions = mod_forum_external::get_forum_discussions(array($forum2->id));
348 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_returns(), $discussions);
039c81f0 349 $this->assertEquals(1, count($discussions));
a9a0cb69
MN
350
351 // Call without required view discussion capability.
909f27ac 352 $this->unassignUserCapability('mod/forum:viewdiscussion', null, null, $course1->id);
a9a0cb69
MN
353 try {
354 mod_forum_external::get_forum_discussions(array($forum1->id));
355 $this->fail('Exception expected due to missing capability.');
356 } catch (moodle_exception $e) {
357 $this->assertEquals('nopermissions', $e->errorcode);
358 }
40afeb40 359 $this->assertDebuggingCalled();
a9a0cb69
MN
360
361 // Unenrol user from second course.
362 $enrol->unenrol_user($instance2, $user1->id);
363
364 // Call for the second course we unenrolled the user from, make sure exception thrown.
365 try {
366 mod_forum_external::get_forum_discussions(array($forum2->id));
367 $this->fail('Exception expected due to being unenrolled from the course.');
368 } catch (moodle_exception $e) {
369 $this->assertEquals('requireloginerror', $e->errorcode);
370 }
2b9fe87d 371 }
e2ede426
JL
372
373 /**
374 * Test get forum posts
375 */
376 public function test_mod_forum_get_forum_discussion_posts() {
377 global $CFG;
378
379 $this->resetAfterTest(true);
380
381 // Set the CFG variable to allow track forums.
382 $CFG->forum_trackreadposts = true;
383
384 // Create a user who can track forums.
385 $record = new stdClass();
386 $record->trackforums = true;
387 $user1 = self::getDataGenerator()->create_user($record);
388 // Create a bunch of other users to post.
389 $user2 = self::getDataGenerator()->create_user();
390 $user3 = self::getDataGenerator()->create_user();
391
392 // Set the first created user to the test user.
393 self::setUser($user1);
394
395 // Create course to add the module.
396 $course1 = self::getDataGenerator()->create_course();
397
398 // Forum with tracking off.
399 $record = new stdClass();
400 $record->course = $course1->id;
401 $record->trackingtype = FORUM_TRACKING_OFF;
402 $forum1 = self::getDataGenerator()->create_module('forum', $record);
403 $forum1context = context_module::instance($forum1->cmid);
404
405 // Add discussions to the forums.
406 $record = new stdClass();
407 $record->course = $course1->id;
408 $record->userid = $user1->id;
409 $record->forum = $forum1->id;
410 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
411
412 $record = new stdClass();
413 $record->course = $course1->id;
414 $record->userid = $user2->id;
415 $record->forum = $forum1->id;
416 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
417
418 // Add 2 replies to the discussion 1 from different users.
419 $record = new stdClass();
420 $record->discussion = $discussion1->id;
421 $record->parent = $discussion1->firstpost;
422 $record->userid = $user2->id;
423 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
424
425 $record->parent = $discussion1reply1->id;
426 $record->userid = $user3->id;
427 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
428
429 // Enrol the user in the course.
430 $enrol = enrol_get_plugin('manual');
431 // Following line enrol and assign default role id to the user.
432 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
433 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
434 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
81f810dc
JL
435
436 // Delete one user, to test that we still receive posts by this user.
437 delete_user($user3);
e2ede426
JL
438
439 // Create what we expect to be returned when querying the discussion.
440 $expectedposts = array(
441 'posts' => array(),
442 'warnings' => array(),
443 );
694bf0c7 444
81f810dc
JL
445 // Empty picture since it's a user deleted (user3).
446 $userpictureurl = '';
694bf0c7 447
e2ede426
JL
448 $expectedposts['posts'][] = array(
449 'id' => $discussion1reply2->id,
450 'discussion' => $discussion1reply2->discussion,
451 'parent' => $discussion1reply2->parent,
48fb0250 452 'userid' => (int) $discussion1reply2->userid,
e2ede426
JL
453 'created' => $discussion1reply2->created,
454 'modified' => $discussion1reply2->modified,
455 'mailed' => $discussion1reply2->mailed,
456 'subject' => $discussion1reply2->subject,
457 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
458 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
48fb0250 459 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
460 'messagetrust' => $discussion1reply2->messagetrust,
461 'attachment' => $discussion1reply2->attachment,
462 'totalscore' => $discussion1reply2->totalscore,
463 'mailnow' => $discussion1reply2->mailnow,
464 'children' => array(),
465 'canreply' => true,
466 'postread' => false,
694bf0c7
JL
467 'userfullname' => fullname($user3),
468 'userpictureurl' => $userpictureurl
e2ede426 469 );
694bf0c7 470
da1be050
JL
471 $userpictureurl = moodle_url::make_webservice_pluginfile_url(
472 context_user::instance($discussion1reply1->userid)->id, 'user', 'icon', null, '/', 'f1')->out(false);
694bf0c7 473
e2ede426
JL
474 $expectedposts['posts'][] = array(
475 'id' => $discussion1reply1->id,
476 'discussion' => $discussion1reply1->discussion,
477 'parent' => $discussion1reply1->parent,
48fb0250 478 'userid' => (int) $discussion1reply1->userid,
e2ede426
JL
479 'created' => $discussion1reply1->created,
480 'modified' => $discussion1reply1->modified,
481 'mailed' => $discussion1reply1->mailed,
482 'subject' => $discussion1reply1->subject,
483 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
484 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
48fb0250 485 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
486 'messagetrust' => $discussion1reply1->messagetrust,
487 'attachment' => $discussion1reply1->attachment,
488 'totalscore' => $discussion1reply1->totalscore,
489 'mailnow' => $discussion1reply1->mailnow,
d2c58b95 490 'children' => array($discussion1reply2->id),
e2ede426
JL
491 'canreply' => true,
492 'postread' => false,
694bf0c7
JL
493 'userfullname' => fullname($user2),
494 'userpictureurl' => $userpictureurl
e2ede426
JL
495 );
496
497 // Test a discussion with two additional posts (total 3 posts).
498 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
499 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
500 $this->assertEquals(3, count($posts['posts']));
501
502 // Unset the initial discussion post.
503 array_pop($posts['posts']);
504 $this->assertEquals($expectedposts, $posts);
505
506 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
507 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
508 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
509 $this->assertEquals(1, count($posts['posts']));
510
511 }
c2586672 512
b1aa7dfa
JL
513 /**
514 * Test get forum posts (qanda forum)
515 */
516 public function test_mod_forum_get_forum_discussion_posts_qanda() {
517 global $CFG, $DB;
518
519 $this->resetAfterTest(true);
520
521 $record = new stdClass();
522 $user1 = self::getDataGenerator()->create_user($record);
523 $user2 = self::getDataGenerator()->create_user();
524
525 // Set the first created user to the test user.
526 self::setUser($user1);
527
528 // Create course to add the module.
529 $course1 = self::getDataGenerator()->create_course();
530 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
531 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
532
533 // Forum with tracking off.
534 $record = new stdClass();
535 $record->course = $course1->id;
536 $record->type = 'qanda';
537 $forum1 = self::getDataGenerator()->create_module('forum', $record);
538 $forum1context = context_module::instance($forum1->cmid);
539
540 // Add discussions to the forums.
541 $record = new stdClass();
542 $record->course = $course1->id;
543 $record->userid = $user2->id;
544 $record->forum = $forum1->id;
545 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
546
547 // Add 1 reply (not the actual user).
548 $record = new stdClass();
549 $record->discussion = $discussion1->id;
550 $record->parent = $discussion1->firstpost;
551 $record->userid = $user2->id;
552 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
553
554 // We still see only the original post.
555 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
556 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
557 $this->assertEquals(1, count($posts['posts']));
558
559 // Add a new reply, the user is going to be able to see only the original post and their new post.
560 $record = new stdClass();
561 $record->discussion = $discussion1->id;
562 $record->parent = $discussion1->firstpost;
563 $record->userid = $user1->id;
564 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
565
566 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
567 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
568 $this->assertEquals(2, count($posts['posts']));
569
570 // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
571 $discussion1reply2->created -= $CFG->maxeditingtime * 2;
572 $DB->update_record('forum_posts', $discussion1reply2);
573
574 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
575 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
576 $this->assertEquals(3, count($posts['posts']));
b1aa7dfa
JL
577 }
578
c2586672
JL
579 /**
580 * Test get forum discussions paginated
581 */
582 public function test_mod_forum_get_forum_discussions_paginated() {
583 global $USER, $CFG, $DB;
584
585 $this->resetAfterTest(true);
586
587 // Set the CFG variable to allow track forums.
588 $CFG->forum_trackreadposts = true;
589
590 // Create a user who can track forums.
591 $record = new stdClass();
592 $record->trackforums = true;
593 $user1 = self::getDataGenerator()->create_user($record);
594 // Create a bunch of other users to post.
595 $user2 = self::getDataGenerator()->create_user();
596 $user3 = self::getDataGenerator()->create_user();
597 $user4 = self::getDataGenerator()->create_user();
598
599 // Set the first created user to the test user.
600 self::setUser($user1);
601
602 // Create courses to add the modules.
603 $course1 = self::getDataGenerator()->create_course();
604
605 // First forum with tracking off.
606 $record = new stdClass();
607 $record->course = $course1->id;
608 $record->trackingtype = FORUM_TRACKING_OFF;
609 $forum1 = self::getDataGenerator()->create_module('forum', $record);
610
611 // Add discussions to the forums.
612 $record = new stdClass();
613 $record->course = $course1->id;
614 $record->userid = $user1->id;
615 $record->forum = $forum1->id;
616 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
617
618 // Add three replies to the discussion 1 from different users.
619 $record = new stdClass();
620 $record->discussion = $discussion1->id;
621 $record->parent = $discussion1->firstpost;
622 $record->userid = $user2->id;
623 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
624
625 $record->parent = $discussion1reply1->id;
626 $record->userid = $user3->id;
627 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
628
629 $record->userid = $user4->id;
630 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
631
632 // Enrol the user in the first course.
633 $enrol = enrol_get_plugin('manual');
634
635 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
636 $enrolinstances = enrol_get_instances($course1->id, true);
637 foreach ($enrolinstances as $courseenrolinstance) {
638 if ($courseenrolinstance->enrol == "manual") {
639 $instance1 = $courseenrolinstance;
640 break;
641 }
642 }
643 $enrol->enrol_user($instance1, $user1->id);
644
81f810dc
JL
645 // Delete one user.
646 delete_user($user4);
647
c2586672
JL
648 // Assign capabilities to view discussions for forum 1.
649 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
650 $context = context_module::instance($cm->id);
651 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
652 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
653
654 // Create what we expect to be returned when querying the forums.
655
656 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
81f810dc 657 $userpictureurl = moodle_url::make_webservice_pluginfile_url(
c2586672 658 context_user::instance($user1->id)->id, 'user', 'icon', null, '/', 'f1');
c2586672 659
81f810dc
JL
660 // We expect an empty URL since we deleted the user4.
661 $usermodifiedpictureurl = '';
c2586672
JL
662
663 $expecteddiscussions = array(
664 'id' => $discussion1->firstpost,
665 'name' => $discussion1->name,
666 'groupid' => $discussion1->groupid,
667 'timemodified' => $discussion1reply3->created,
668 'usermodified' => $discussion1reply3->userid,
669 'timestart' => $discussion1->timestart,
670 'timeend' => $discussion1->timeend,
671 'discussion' => $discussion1->id,
672 'parent' => 0,
673 'userid' => $discussion1->userid,
674 'created' => $post1->created,
675 'modified' => $post1->modified,
676 'mailed' => $post1->mailed,
677 'subject' => $post1->subject,
678 'message' => $post1->message,
679 'messageformat' => $post1->messageformat,
680 'messagetrust' => $post1->messagetrust,
681 'attachment' => $post1->attachment,
682 'totalscore' => $post1->totalscore,
683 'mailnow' => $post1->mailnow,
684 'userfullname' => fullname($user1),
685 'usermodifiedfullname' => fullname($user4),
686 'userpictureurl' => $userpictureurl,
687 'usermodifiedpictureurl' => $usermodifiedpictureurl,
688 'numreplies' => 3,
689 'numunread' => 0
690 );
691
692 // Call the external function passing forum id.
693 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
694 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
695 $expectedreturn = array(
696 'discussions' => array($expecteddiscussions),
697 'warnings' => array()
698 );
699 $this->assertEquals($expectedreturn, $discussions);
700
701 // Call without required view discussion capability.
702 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
703 try {
704 mod_forum_external::get_forum_discussions_paginated($forum1->id);
705 $this->fail('Exception expected due to missing capability.');
706 } catch (moodle_exception $e) {
707 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
708 }
709
710 // Unenrol user from second course.
711 $enrol->unenrol_user($instance1, $user1->id);
712
713 // Call for the second course we unenrolled the user from, make sure exception thrown.
714 try {
715 mod_forum_external::get_forum_discussions_paginated($forum1->id);
716 $this->fail('Exception expected due to being unenrolled from the course.');
717 } catch (moodle_exception $e) {
718 $this->assertEquals('requireloginerror', $e->errorcode);
719 }
720 }
039c81f0
JL
721
722 /**
723 * Test get forum discussions paginated (qanda forums)
724 */
725 public function test_mod_forum_get_forum_discussions_paginated_qanda() {
726
727 $this->resetAfterTest(true);
728
729 // Create courses to add the modules.
730 $course = self::getDataGenerator()->create_course();
731
732 $user1 = self::getDataGenerator()->create_user();
733 $user2 = self::getDataGenerator()->create_user();
734
735 // First forum with tracking off.
736 $record = new stdClass();
737 $record->course = $course->id;
738 $record->type = 'qanda';
739 $forum = self::getDataGenerator()->create_module('forum', $record);
740
741 // Add discussions to the forums.
742 $discussionrecord = new stdClass();
743 $discussionrecord->course = $course->id;
744 $discussionrecord->userid = $user2->id;
745 $discussionrecord->forum = $forum->id;
746 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
747
748 self::setAdminUser();
749 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
750 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
751
752 $this->assertCount(1, $discussions['discussions']);
753 $this->assertCount(0, $discussions['warnings']);
754
755 self::setUser($user1);
756 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
757
758 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
759 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
760
761 $this->assertCount(1, $discussions['discussions']);
762 $this->assertCount(0, $discussions['warnings']);
763
764 }
50a20317
JL
765
766 /**
767 * Test add_discussion_post
768 */
769 public function test_add_discussion_post() {
770 global $CFG;
771
772 $this->resetAfterTest(true);
773
774 $user = self::getDataGenerator()->create_user();
775 $otheruser = self::getDataGenerator()->create_user();
776
777 self::setAdminUser();
778
779 // Create course to add the module.
780 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
781
782 // Forum with tracking off.
783 $record = new stdClass();
784 $record->course = $course->id;
785 $forum = self::getDataGenerator()->create_module('forum', $record);
786 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
787 $forumcontext = context_module::instance($forum->cmid);
788
789 // Add discussions to the forums.
790 $record = new stdClass();
791 $record->course = $course->id;
792 $record->userid = $user->id;
793 $record->forum = $forum->id;
794 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
795
796 // Try to post (user not enrolled).
797 self::setUser($user);
798 try {
799 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
800 $this->fail('Exception expected due to being unenrolled from the course.');
801 } catch (moodle_exception $e) {
802 $this->assertEquals('requireloginerror', $e->errorcode);
803 }
804
805 $this->getDataGenerator()->enrol_user($user->id, $course->id);
806 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
807
808 $post = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
809 $post = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $post);
810
811 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
812 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
813 // We receive the discussion and the post.
814 $this->assertEquals(2, count($posts['posts']));
815 $this->assertEquals($post['postid'], $posts['posts'][1]['id']);
816 $this->assertEquals('some subject', $posts['posts'][1]['subject']);
817 $this->assertEquals('some text here...', $posts['posts'][1]['message']);
818
819 // Check not posting in groups the user is not member of.
820 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
821 groups_add_member($group->id, $otheruser->id);
822
823 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
824 $record->forum = $forum->id;
825 $record->userid = $otheruser->id;
826 $record->groupid = $group->id;
827 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
828
829 try {
830 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
831 $this->fail('Exception expected due to invalid permissions for posting.');
832 } catch (moodle_exception $e) {
833 // Expect debugging since we are switching context, and this is something WS_SERVER mode don't like.
834 $this->assertDebuggingCalled();
835 $this->assertEquals('nopostforum', $e->errorcode);
836 }
837
838 }
2b9fe87d 839}