weekly release 3.3dev
[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');
31
32class mod_forum_external_testcase extends externallib_advanced_testcase {
33
34 /**
35 * Tests set up
36 */
37 protected function setUp() {
38 global $CFG;
39
59075a43
AN
40 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
41 // tests using these functions.
42 \mod_forum\subscriptions::reset_forum_cache();
43
2b9fe87d
MN
44 require_once($CFG->dirroot . '/mod/forum/externallib.php');
45 }
46
59075a43
AN
47 public function tearDown() {
48 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
49 // tests using these functions.
50 \mod_forum\subscriptions::reset_forum_cache();
51 }
52
2b9fe87d
MN
53 /**
54 * Test get forums
55 */
56 public function test_mod_forum_get_forums_by_courses() {
57 global $USER, $CFG, $DB;
58
59 $this->resetAfterTest(true);
60
61 // Create a user.
62 $user = self::getDataGenerator()->create_user();
63
64 // Set to the user.
65 self::setUser($user);
66
67 // Create courses to add the modules.
68 $course1 = self::getDataGenerator()->create_course();
69 $course2 = self::getDataGenerator()->create_course();
70
71 // First forum.
72 $record = new stdClass();
73 $record->introformat = FORMAT_HTML;
74 $record->course = $course1->id;
75 $forum1 = self::getDataGenerator()->create_module('forum', $record);
76
77 // Second forum.
78 $record = new stdClass();
79 $record->introformat = FORMAT_HTML;
80 $record->course = $course2->id;
81 $forum2 = self::getDataGenerator()->create_module('forum', $record);
7ef49bd3 82 $forum2->introfiles = [];
2b9fe87d 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;
ea5b910b 92 $forum1->cancreatediscussions = true;
7ef49bd3 93 $forum1->introfiles = [];
7ea6ada3
JL
94
95 $record = new stdClass();
96 $record->course = $course2->id;
97 $record->userid = $user->id;
98 $record->forum = $forum2->id;
99 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
100 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
101 // Expect two discussions.
102 $forum2->numdiscussions = 2;
ea5b910b
JL
103 // Default limited role, no create discussion capability enabled.
104 $forum2->cancreatediscussions = false;
7ea6ada3 105
2b9fe87d
MN
106 // Check the forum was correctly created.
107 $this->assertEquals(2, $DB->count_records_select('forum', 'id = :forum1 OR id = :forum2',
108 array('forum1' => $forum1->id, 'forum2' => $forum2->id)));
109
110 // Enrol the user in two courses.
909f27ac
JM
111 // DataGenerator->enrol_user automatically sets a role for the user with the permission mod/form:viewdiscussion.
112 $this->getDataGenerator()->enrol_user($user->id, $course1->id, null, 'manual');
113 // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
2b9fe87d 114 $enrol = enrol_get_plugin('manual');
2b9fe87d
MN
115 $enrolinstances = enrol_get_instances($course2->id, true);
116 foreach ($enrolinstances as $courseenrolinstance) {
117 if ($courseenrolinstance->enrol == "manual") {
118 $instance2 = $courseenrolinstance;
119 break;
120 }
121 }
122 $enrol->enrol_user($instance2, $user->id);
123
2b9fe87d 124 // Assign capabilities to view forums for forum 2.
74b63eae 125 $cm2 = get_coursemodule_from_id('forum', $forum2->cmid, 0, false, MUST_EXIST);
2b9fe87d
MN
126 $context2 = context_module::instance($cm2->id);
127 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
128 $roleid2 = $this->assignUserCapability('mod/forum:viewdiscussion', $context2->id, $newrole);
129
130 // Create what we expect to be returned when querying the two courses.
c8f1d8a0
JL
131 unset($forum1->displaywordcount);
132 unset($forum2->displaywordcount);
133
2b9fe87d
MN
134 $expectedforums = array();
135 $expectedforums[$forum1->id] = (array) $forum1;
136 $expectedforums[$forum2->id] = (array) $forum2;
137
138 // Call the external function passing course ids.
139 $forums = mod_forum_external::get_forums_by_courses(array($course1->id, $course2->id));
c8f1d8a0
JL
140 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
141 $this->assertCount(2, $forums);
142 foreach ($forums as $forum) {
143 $this->assertEquals($expectedforums[$forum['id']], $forum);
144 }
2b9fe87d
MN
145
146 // Call the external function without passing course id.
147 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
148 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
149 $this->assertCount(2, $forums);
150 foreach ($forums as $forum) {
151 $this->assertEquals($expectedforums[$forum['id']], $forum);
152 }
2b9fe87d
MN
153
154 // Unenrol user from second course and alter expected forums.
155 $enrol->unenrol_user($instance2, $user->id);
156 unset($expectedforums[$forum2->id]);
157
158 // Call the external function without passing course id.
159 $forums = mod_forum_external::get_forums_by_courses();
c8f1d8a0
JL
160 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
161 $this->assertCount(1, $forums);
162 $this->assertEquals($expectedforums[$forum1->id], $forums[0]);
ea5b910b
JL
163 $this->assertTrue($forums[0]['cancreatediscussions']);
164
165 // Change the type of the forum, the user shouldn't be able to add discussions.
166 $DB->set_field('forum', 'type', 'news', array('id' => $forum1->id));
167 $forums = mod_forum_external::get_forums_by_courses();
168 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
169 $this->assertFalse($forums[0]['cancreatediscussions']);
c8f1d8a0
JL
170
171 // Call for the second course we unenrolled the user from.
172 $forums = mod_forum_external::get_forums_by_courses(array($course2->id));
173 $forums = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums);
174 $this->assertCount(0, $forums);
a9a0cb69
MN
175 }
176
e2ede426
JL
177 /**
178 * Test get forum posts
179 */
180 public function test_mod_forum_get_forum_discussion_posts() {
d85bedf7 181 global $CFG, $PAGE;
e2ede426
JL
182
183 $this->resetAfterTest(true);
184
185 // Set the CFG variable to allow track forums.
186 $CFG->forum_trackreadposts = true;
187
188 // Create a user who can track forums.
189 $record = new stdClass();
190 $record->trackforums = true;
191 $user1 = self::getDataGenerator()->create_user($record);
192 // Create a bunch of other users to post.
193 $user2 = self::getDataGenerator()->create_user();
194 $user3 = self::getDataGenerator()->create_user();
195
196 // Set the first created user to the test user.
197 self::setUser($user1);
198
199 // Create course to add the module.
200 $course1 = self::getDataGenerator()->create_course();
201
202 // Forum with tracking off.
203 $record = new stdClass();
204 $record->course = $course1->id;
205 $record->trackingtype = FORUM_TRACKING_OFF;
206 $forum1 = self::getDataGenerator()->create_module('forum', $record);
207 $forum1context = context_module::instance($forum1->cmid);
208
209 // Add discussions to the forums.
210 $record = new stdClass();
211 $record->course = $course1->id;
212 $record->userid = $user1->id;
213 $record->forum = $forum1->id;
214 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
215
216 $record = new stdClass();
217 $record->course = $course1->id;
218 $record->userid = $user2->id;
219 $record->forum = $forum1->id;
220 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
221
222 // Add 2 replies to the discussion 1 from different users.
223 $record = new stdClass();
224 $record->discussion = $discussion1->id;
225 $record->parent = $discussion1->firstpost;
226 $record->userid = $user2->id;
227 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
c8743f62
JL
228 $filename = 'shouldbeanimage.jpg';
229 // Add a fake inline image to the post.
230 $filerecordinline = array(
231 'contextid' => $forum1context->id,
232 'component' => 'mod_forum',
233 'filearea' => 'post',
234 'itemid' => $discussion1reply1->id,
235 'filepath' => '/',
236 'filename' => $filename,
237 );
238 $fs = get_file_storage();
239 $timepost = time();
240 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
e2ede426
JL
241
242 $record->parent = $discussion1reply1->id;
243 $record->userid = $user3->id;
244 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
245
246 // Enrol the user in the course.
247 $enrol = enrol_get_plugin('manual');
248 // Following line enrol and assign default role id to the user.
249 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
250 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
251 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
81f810dc
JL
252
253 // Delete one user, to test that we still receive posts by this user.
254 delete_user($user3);
e2ede426
JL
255
256 // Create what we expect to be returned when querying the discussion.
257 $expectedposts = array(
258 'posts' => array(),
259 'warnings' => array(),
260 );
694bf0c7 261
d85bedf7 262 // User pictures are initially empty, we should get the links once the external function is called.
e2ede426
JL
263 $expectedposts['posts'][] = array(
264 'id' => $discussion1reply2->id,
265 'discussion' => $discussion1reply2->discussion,
266 'parent' => $discussion1reply2->parent,
48fb0250 267 'userid' => (int) $discussion1reply2->userid,
e2ede426
JL
268 'created' => $discussion1reply2->created,
269 'modified' => $discussion1reply2->modified,
270 'mailed' => $discussion1reply2->mailed,
271 'subject' => $discussion1reply2->subject,
272 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
273 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
48fb0250 274 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
275 'messagetrust' => $discussion1reply2->messagetrust,
276 'attachment' => $discussion1reply2->attachment,
277 'totalscore' => $discussion1reply2->totalscore,
278 'mailnow' => $discussion1reply2->mailnow,
279 'children' => array(),
280 'canreply' => true,
281 'postread' => false,
694bf0c7 282 'userfullname' => fullname($user3),
d85bedf7 283 'userpictureurl' => ''
e2ede426 284 );
694bf0c7 285
e2ede426
JL
286 $expectedposts['posts'][] = array(
287 'id' => $discussion1reply1->id,
288 'discussion' => $discussion1reply1->discussion,
289 'parent' => $discussion1reply1->parent,
48fb0250 290 'userid' => (int) $discussion1reply1->userid,
e2ede426
JL
291 'created' => $discussion1reply1->created,
292 'modified' => $discussion1reply1->modified,
293 'mailed' => $discussion1reply1->mailed,
294 'subject' => $discussion1reply1->subject,
295 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
296 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
48fb0250 297 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
298 'messagetrust' => $discussion1reply1->messagetrust,
299 'attachment' => $discussion1reply1->attachment,
c8743f62
JL
300 'messageinlinefiles' => array(
301 array(
302 'filename' => $filename,
303 'filepath' => '/',
304 'filesize' => '27',
305 'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_forum', 'post',
306 $discussion1reply1->id, '/', $filename),
307 'timemodified' => $timepost,
308 'mimetype' => 'image/jpeg',
309 )
310 ),
e2ede426
JL
311 'totalscore' => $discussion1reply1->totalscore,
312 'mailnow' => $discussion1reply1->mailnow,
d2c58b95 313 'children' => array($discussion1reply2->id),
e2ede426
JL
314 'canreply' => true,
315 'postread' => false,
694bf0c7 316 'userfullname' => fullname($user2),
d85bedf7 317 'userpictureurl' => ''
e2ede426
JL
318 );
319
320 // Test a discussion with two additional posts (total 3 posts).
321 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
322 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
323 $this->assertEquals(3, count($posts['posts']));
324
d85bedf7
JL
325 // Generate here the pictures because we need to wait to the external function to init the theme.
326 $userpicture = new user_picture($user3);
327 $userpicture->size = 1; // Size f1.
328 $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
329
330 $userpicture = new user_picture($user2);
331 $userpicture->size = 1; // Size f1.
332 $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
333
e2ede426
JL
334 // Unset the initial discussion post.
335 array_pop($posts['posts']);
336 $this->assertEquals($expectedposts, $posts);
337
338 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
339 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
340 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
341 $this->assertEquals(1, count($posts['posts']));
342
343 }
c2586672 344
b1aa7dfa
JL
345 /**
346 * Test get forum posts (qanda forum)
347 */
348 public function test_mod_forum_get_forum_discussion_posts_qanda() {
349 global $CFG, $DB;
350
351 $this->resetAfterTest(true);
352
353 $record = new stdClass();
354 $user1 = self::getDataGenerator()->create_user($record);
355 $user2 = self::getDataGenerator()->create_user();
356
357 // Set the first created user to the test user.
358 self::setUser($user1);
359
360 // Create course to add the module.
361 $course1 = self::getDataGenerator()->create_course();
362 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
363 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
364
365 // Forum with tracking off.
366 $record = new stdClass();
367 $record->course = $course1->id;
368 $record->type = 'qanda';
369 $forum1 = self::getDataGenerator()->create_module('forum', $record);
370 $forum1context = context_module::instance($forum1->cmid);
371
372 // Add discussions to the forums.
373 $record = new stdClass();
374 $record->course = $course1->id;
375 $record->userid = $user2->id;
376 $record->forum = $forum1->id;
377 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
378
379 // Add 1 reply (not the actual user).
380 $record = new stdClass();
381 $record->discussion = $discussion1->id;
382 $record->parent = $discussion1->firstpost;
383 $record->userid = $user2->id;
384 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
385
386 // We still see only the original post.
387 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
388 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
389 $this->assertEquals(1, count($posts['posts']));
390
391 // Add a new reply, the user is going to be able to see only the original post and their new post.
392 $record = new stdClass();
393 $record->discussion = $discussion1->id;
394 $record->parent = $discussion1->firstpost;
395 $record->userid = $user1->id;
396 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
397
398 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
399 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
400 $this->assertEquals(2, count($posts['posts']));
401
402 // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
403 $discussion1reply2->created -= $CFG->maxeditingtime * 2;
404 $DB->update_record('forum_posts', $discussion1reply2);
405
406 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
407 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
408 $this->assertEquals(3, count($posts['posts']));
b1aa7dfa
JL
409 }
410
c2586672
JL
411 /**
412 * Test get forum discussions paginated
413 */
414 public function test_mod_forum_get_forum_discussions_paginated() {
d85bedf7 415 global $USER, $CFG, $DB, $PAGE;
c2586672
JL
416
417 $this->resetAfterTest(true);
418
419 // Set the CFG variable to allow track forums.
420 $CFG->forum_trackreadposts = true;
421
422 // Create a user who can track forums.
423 $record = new stdClass();
424 $record->trackforums = true;
425 $user1 = self::getDataGenerator()->create_user($record);
426 // Create a bunch of other users to post.
427 $user2 = self::getDataGenerator()->create_user();
428 $user3 = self::getDataGenerator()->create_user();
429 $user4 = self::getDataGenerator()->create_user();
430
431 // Set the first created user to the test user.
432 self::setUser($user1);
433
434 // Create courses to add the modules.
435 $course1 = self::getDataGenerator()->create_course();
436
437 // First forum with tracking off.
438 $record = new stdClass();
439 $record->course = $course1->id;
440 $record->trackingtype = FORUM_TRACKING_OFF;
441 $forum1 = self::getDataGenerator()->create_module('forum', $record);
442
443 // Add discussions to the forums.
444 $record = new stdClass();
445 $record->course = $course1->id;
446 $record->userid = $user1->id;
447 $record->forum = $forum1->id;
448 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
449
450 // Add three replies to the discussion 1 from different users.
451 $record = new stdClass();
452 $record->discussion = $discussion1->id;
453 $record->parent = $discussion1->firstpost;
454 $record->userid = $user2->id;
455 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
456
457 $record->parent = $discussion1reply1->id;
458 $record->userid = $user3->id;
459 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
460
461 $record->userid = $user4->id;
462 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
463
464 // Enrol the user in the first course.
465 $enrol = enrol_get_plugin('manual');
466
467 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
468 $enrolinstances = enrol_get_instances($course1->id, true);
469 foreach ($enrolinstances as $courseenrolinstance) {
470 if ($courseenrolinstance->enrol == "manual") {
471 $instance1 = $courseenrolinstance;
472 break;
473 }
474 }
475 $enrol->enrol_user($instance1, $user1->id);
476
81f810dc
JL
477 // Delete one user.
478 delete_user($user4);
479
c2586672
JL
480 // Assign capabilities to view discussions for forum 1.
481 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
482 $context = context_module::instance($cm->id);
483 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
484 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
485
486 // Create what we expect to be returned when querying the forums.
487
488 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
c2586672 489
d85bedf7 490 // User pictures are initially empty, we should get the links once the external function is called.
c2586672
JL
491 $expecteddiscussions = array(
492 'id' => $discussion1->firstpost,
493 'name' => $discussion1->name,
494 'groupid' => $discussion1->groupid,
495 'timemodified' => $discussion1reply3->created,
496 'usermodified' => $discussion1reply3->userid,
497 'timestart' => $discussion1->timestart,
498 'timeend' => $discussion1->timeend,
499 'discussion' => $discussion1->id,
500 'parent' => 0,
501 'userid' => $discussion1->userid,
502 'created' => $post1->created,
503 'modified' => $post1->modified,
504 'mailed' => $post1->mailed,
505 'subject' => $post1->subject,
506 'message' => $post1->message,
507 'messageformat' => $post1->messageformat,
508 'messagetrust' => $post1->messagetrust,
509 'attachment' => $post1->attachment,
510 'totalscore' => $post1->totalscore,
511 'mailnow' => $post1->mailnow,
512 'userfullname' => fullname($user1),
513 'usermodifiedfullname' => fullname($user4),
d85bedf7
JL
514 'userpictureurl' => '',
515 'usermodifiedpictureurl' => '',
c2586672 516 'numreplies' => 3,
5f219cf1 517 'numunread' => 0,
0f3bbfd4
AN
518 'pinned' => FORUM_DISCUSSION_UNPINNED,
519 'locked' => false,
520 'canreply' => false,
c2586672
JL
521 );
522
523 // Call the external function passing forum id.
524 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
525 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
526 $expectedreturn = array(
527 'discussions' => array($expecteddiscussions),
528 'warnings' => array()
529 );
d85bedf7
JL
530
531 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
532 $userpicture = new user_picture($user1);
533 $userpicture->size = 1; // Size f1.
534 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
535
536 $userpicture = new user_picture($user4);
537 $userpicture->size = 1; // Size f1.
538 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
539
c2586672
JL
540 $this->assertEquals($expectedreturn, $discussions);
541
542 // Call without required view discussion capability.
543 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
544 try {
545 mod_forum_external::get_forum_discussions_paginated($forum1->id);
546 $this->fail('Exception expected due to missing capability.');
547 } catch (moodle_exception $e) {
548 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
549 }
550
551 // Unenrol user from second course.
552 $enrol->unenrol_user($instance1, $user1->id);
553
554 // Call for the second course we unenrolled the user from, make sure exception thrown.
555 try {
556 mod_forum_external::get_forum_discussions_paginated($forum1->id);
557 $this->fail('Exception expected due to being unenrolled from the course.');
558 } catch (moodle_exception $e) {
559 $this->assertEquals('requireloginerror', $e->errorcode);
560 }
561 }
039c81f0
JL
562
563 /**
564 * Test get forum discussions paginated (qanda forums)
565 */
566 public function test_mod_forum_get_forum_discussions_paginated_qanda() {
567
568 $this->resetAfterTest(true);
569
570 // Create courses to add the modules.
571 $course = self::getDataGenerator()->create_course();
572
573 $user1 = self::getDataGenerator()->create_user();
574 $user2 = self::getDataGenerator()->create_user();
575
576 // First forum with tracking off.
577 $record = new stdClass();
578 $record->course = $course->id;
579 $record->type = 'qanda';
580 $forum = self::getDataGenerator()->create_module('forum', $record);
581
582 // Add discussions to the forums.
583 $discussionrecord = new stdClass();
584 $discussionrecord->course = $course->id;
585 $discussionrecord->userid = $user2->id;
586 $discussionrecord->forum = $forum->id;
587 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
588
589 self::setAdminUser();
590 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
591 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
592
593 $this->assertCount(1, $discussions['discussions']);
594 $this->assertCount(0, $discussions['warnings']);
595
596 self::setUser($user1);
597 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
598
599 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
600 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
601
602 $this->assertCount(1, $discussions['discussions']);
603 $this->assertCount(0, $discussions['warnings']);
604
605 }
50a20317
JL
606
607 /**
608 * Test add_discussion_post
609 */
610 public function test_add_discussion_post() {
e881c4f5 611 global $CFG;
50a20317
JL
612
613 $this->resetAfterTest(true);
614
615 $user = self::getDataGenerator()->create_user();
616 $otheruser = self::getDataGenerator()->create_user();
617
618 self::setAdminUser();
619
620 // Create course to add the module.
621 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
622
623 // Forum with tracking off.
624 $record = new stdClass();
625 $record->course = $course->id;
626 $forum = self::getDataGenerator()->create_module('forum', $record);
627 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
628 $forumcontext = context_module::instance($forum->cmid);
629
630 // Add discussions to the forums.
631 $record = new stdClass();
632 $record->course = $course->id;
633 $record->userid = $user->id;
634 $record->forum = $forum->id;
635 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
636
637 // Try to post (user not enrolled).
638 self::setUser($user);
639 try {
640 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
641 $this->fail('Exception expected due to being unenrolled from the course.');
642 } catch (moodle_exception $e) {
643 $this->assertEquals('requireloginerror', $e->errorcode);
644 }
645
646 $this->getDataGenerator()->enrol_user($user->id, $course->id);
647 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
648
41182118
BK
649 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
650 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
50a20317
JL
651
652 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
653 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
654 // We receive the discussion and the post.
655 $this->assertEquals(2, count($posts['posts']));
43ff833d
JL
656
657 $tested = false;
41182118
BK
658 foreach ($posts['posts'] as $thispost) {
659 if ($createdpost['postid'] == $thispost['id']) {
660 $this->assertEquals('some subject', $thispost['subject']);
661 $this->assertEquals('some text here...', $thispost['message']);
43ff833d
JL
662 $tested = true;
663 }
664 }
665 $this->assertTrue($tested);
50a20317 666
e881c4f5 667 // Test inline and regular attachment in post
41182118
BK
668 // Create a file in a draft area for inline attachments.
669 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5 670 $draftidattach = file_get_unused_draft_itemid();
41182118
BK
671 self::setUser($user);
672 $usercontext = context_user::instance($user->id);
673 $filepath = '/';
674 $filearea = 'draft';
675 $component = 'user';
676 $filenameimg = 'shouldbeanimage.txt';
e881c4f5 677 $filerecordinline = array(
41182118
BK
678 'contextid' => $usercontext->id,
679 'component' => $component,
680 'filearea' => $filearea,
681 'itemid' => $draftidinlineattach,
682 'filepath' => $filepath,
683 'filename' => $filenameimg,
684 );
685 $fs = get_file_storage();
41182118 686
e881c4f5
BK
687 // Create a file in a draft area for regular attachments.
688 $filerecordattach = $filerecordinline;
689 $attachfilename = 'attachment.txt';
690 $filerecordattach['filename'] = $attachfilename;
691 $filerecordattach['itemid'] = $draftidattach;
692 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
693 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
694
48143990 695 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 696 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
697 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot
698 . "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}"
699 . '" alt="inlineimage">.';
700 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'new post inline attachment',
701 $dummytext, $options);
702 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
703
704 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
705 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
706 // We receive the discussion and the post.
707 // Can't guarantee order of posts during tests.
708 $postfound = false;
709 foreach ($posts['posts'] as $thispost) {
710 if ($createdpost['postid'] == $thispost['id']) {
711 $this->assertEquals($createdpost['postid'], $thispost['id']);
e881c4f5
BK
712 $this->assertEquals($thispost['attachment'], 1, "There should be a non-inline attachment");
713 $this->assertCount(1, $thispost['attachments'], "There should be 1 attachment");
714 $this->assertEquals($thispost['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
715 $this->assertContains('pluginfile.php', $thispost['message']);
716 $postfound = true;
e881c4f5 717 break;
41182118
BK
718 }
719 }
720
721 $this->assertTrue($postfound);
722
50a20317
JL
723 // Check not posting in groups the user is not member of.
724 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
725 groups_add_member($group->id, $otheruser->id);
726
727 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
728 $record->forum = $forum->id;
729 $record->userid = $otheruser->id;
730 $record->groupid = $group->id;
731 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
732
733 try {
734 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
735 $this->fail('Exception expected due to invalid permissions for posting.');
736 } catch (moodle_exception $e) {
50a20317
JL
737 $this->assertEquals('nopostforum', $e->errorcode);
738 }
739
740 }
7ab43ac8
JL
741
742 /*
743 * Test add_discussion. A basic test since all the API functions are already covered by unit tests.
744 */
745 public function test_add_discussion() {
41182118 746 global $CFG, $USER;
7ab43ac8
JL
747 $this->resetAfterTest(true);
748
749 // Create courses to add the modules.
750 $course = self::getDataGenerator()->create_course();
751
752 $user1 = self::getDataGenerator()->create_user();
753 $user2 = self::getDataGenerator()->create_user();
754
755 // First forum with tracking off.
756 $record = new stdClass();
757 $record->course = $course->id;
758 $record->type = 'news';
759 $forum = self::getDataGenerator()->create_module('forum', $record);
760
761 self::setUser($user1);
762 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
763
764 try {
765 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
766 $this->fail('Exception expected due to invalid permissions.');
767 } catch (moodle_exception $e) {
768 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
769 }
770
771 self::setAdminUser();
41182118
BK
772 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
773 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
7ab43ac8
JL
774
775 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
776 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
777
778 $this->assertCount(1, $discussions['discussions']);
779 $this->assertCount(0, $discussions['warnings']);
780
41182118 781 $this->assertEquals($createddiscussion['discussionid'], $discussions['discussions'][0]['discussion']);
7ab43ac8
JL
782 $this->assertEquals(-1, $discussions['discussions'][0]['groupid']);
783 $this->assertEquals('the subject', $discussions['discussions'][0]['subject']);
784 $this->assertEquals('some text here...', $discussions['discussions'][0]['message']);
785
5f219cf1
BK
786 $discussion2pinned = mod_forum_external::add_discussion($forum->id, 'the pinned subject', 'some 2 text here...', -1,
787 array('options' => array('name' => 'discussionpinned',
788 'value' => true)));
789 $discussion3 = mod_forum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...');
790 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
791 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
792 $this->assertCount(3, $discussions['discussions']);
793 $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']);
41182118 794
e881c4f5 795 // Test inline and regular attachment in new discussion
41182118 796 // Create a file in a draft area for inline attachments.
e881c4f5
BK
797
798 $fs = get_file_storage();
799
41182118 800 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5
BK
801 $draftidattach = file_get_unused_draft_itemid();
802
41182118
BK
803 $usercontext = context_user::instance($USER->id);
804 $filepath = '/';
805 $filearea = 'draft';
806 $component = 'user';
807 $filenameimg = 'shouldbeanimage.txt';
808 $filerecord = array(
809 'contextid' => $usercontext->id,
810 'component' => $component,
811 'filearea' => $filearea,
812 'itemid' => $draftidinlineattach,
813 'filepath' => $filepath,
814 'filename' => $filenameimg,
815 );
e881c4f5 816
e881c4f5
BK
817 // Create a file in a draft area for regular attachments.
818 $filerecordattach = $filerecord;
819 $attachfilename = 'attachment.txt';
820 $filerecordattach['filename'] = $attachfilename;
821 $filerecordattach['itemid'] = $draftidattach;
41182118 822 $fs->create_file_from_string($filerecord, 'image contents (not really)');
e881c4f5
BK
823 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
824
41182118
BK
825 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot .
826 "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}" .
827 '" alt="inlineimage">.';
828
48143990 829 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 830 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
831 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the inline attachment subject',
832 $dummytext, -1, $options);
833 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
834
835 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
836 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
837
838 $this->assertCount(4, $discussions['discussions']);
839 $this->assertCount(0, $createddiscussion['warnings']);
840 // Can't guarantee order of posts during tests.
841 $postfound = false;
842 foreach ($discussions['discussions'] as $thisdiscussion) {
843 if ($createddiscussion['discussionid'] == $thisdiscussion['discussion']) {
e881c4f5
BK
844 $this->assertEquals($thisdiscussion['attachment'], 1, "There should be a non-inline attachment");
845 $this->assertCount(1, $thisdiscussion['attachments'], "There should be 1 attachment");
846 $this->assertEquals($thisdiscussion['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
847 $this->assertNotContains('draftfile.php', $thisdiscussion['message']);
848 $this->assertContains('pluginfile.php', $thisdiscussion['message']);
849 $postfound = true;
e881c4f5 850 break;
41182118
BK
851 }
852 }
853
854 $this->assertTrue($postfound);
7ab43ac8
JL
855 }
856
857 /**
858 * Test adding discussions in a course with gorups
859 */
860 public function test_add_discussion_in_course_with_groups() {
861 global $CFG;
862
863 $this->resetAfterTest(true);
864
865 // Create course to add the module.
866 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
867 $user = self::getDataGenerator()->create_user();
868 $this->getDataGenerator()->enrol_user($user->id, $course->id);
869
870 // Forum forcing separate gropus.
871 $record = new stdClass();
872 $record->course = $course->id;
873 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
874
875 // Try to post (user not enrolled).
876 self::setUser($user);
877
878 // The user is not enroled in any group, try to post in a forum with separate groups.
879 try {
880 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
881 $this->fail('Exception expected due to invalid group permissions.');
882 } catch (moodle_exception $e) {
883 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
884 }
885
886 try {
887 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0);
888 $this->fail('Exception expected due to invalid group permissions.');
889 } catch (moodle_exception $e) {
890 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
891 }
892
893 // Create a group.
894 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
895
896 // Try to post in a group the user is not enrolled.
897 try {
898 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
899 $this->fail('Exception expected due to invalid group permissions.');
900 } catch (moodle_exception $e) {
901 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
902 }
903
904 // Add the user to a group.
905 groups_add_member($group->id, $user->id);
906
907 // Try to post in a group the user is not enrolled.
908 try {
909 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1);
910 $this->fail('Exception expected due to invalid group.');
911 } catch (moodle_exception $e) {
912 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
913 }
914
915 // Nost add the discussion using a valid group.
916 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
917 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
918
919 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
920 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
921
922 $this->assertCount(1, $discussions['discussions']);
923 $this->assertCount(0, $discussions['warnings']);
924 $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']);
925 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
926
927 // Now add a discussions without indicating a group. The function should guess the correct group.
928 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
929 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
930
931 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
932 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
933
934 $this->assertCount(2, $discussions['discussions']);
935 $this->assertCount(0, $discussions['warnings']);
936 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
937 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
938
939 // Enrol the same user in other group.
940 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
941 groups_add_member($group2->id, $user->id);
942
943 // Now add a discussions without indicating a group. The function should guess the correct group (the first one).
944 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
945 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
946
947 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
948 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
949
950 $this->assertCount(3, $discussions['discussions']);
951 $this->assertCount(0, $discussions['warnings']);
952 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
953 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
954 $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']);
955
956 }
957
04cd8ae3
JL
958 /*
959 * Test can_add_discussion. A basic test since all the API functions are already covered by unit tests.
960 */
961 public function test_can_add_discussion() {
962
963 $this->resetAfterTest(true);
964
965 // Create courses to add the modules.
966 $course = self::getDataGenerator()->create_course();
967
968 $user = self::getDataGenerator()->create_user();
969
970 // First forum with tracking off.
971 $record = new stdClass();
972 $record->course = $course->id;
973 $record->type = 'news';
974 $forum = self::getDataGenerator()->create_module('forum', $record);
975
976 // User with no permissions to add in a news forum.
977 self::setUser($user);
978 $this->getDataGenerator()->enrol_user($user->id, $course->id);
979
980 $result = mod_forum_external::can_add_discussion($forum->id);
981 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
982 $this->assertFalse($result['status']);
983
984 self::setAdminUser();
985 $result = mod_forum_external::can_add_discussion($forum->id);
986 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
987 $this->assertTrue($result['status']);
988
989 }
990
2b9fe87d 991}